基础排序:单元,冒泡,分治排序的C++实现与讨论
2011-04-14 11:14
471 查看
一.为什么需要学习排序?
上学的时候学习排序是为了考试,是为了不挂科,在实际问题中,排序就显得不是那么重要了。特别是当使用c++ STL时,排序就是非常简单的事情了。
但为什么还需要学习排序呢?我个人感觉是为了理解细想,培养能力的一个必不可少的过程,像标题所提示的3个排序算法一样,特别是其中的分治算法对解决一切其他很多问题有深远的影响。
二.排序的用处.
排序是为了什么,在我接触的问题中,第一是为了显示,在游戏中必不可少,第二是为了多次查找。
比如说一个散列的数组需要查找一次,那么就直接遍历吧。最悲剧的情况,复杂度为O(n).
如果需要查找多次的话,那么排序后查找肯定要好的多。比如我们下面提到的分治排序,算法复杂度为O(n*ln(n)),如果再使用2分查找的话,复杂度为
O(ln(n)).如果有n个数,要使用K次查找的话,那么总的复杂度为O(n*ln(n) +
K*ln(n))。使用直接遍历的话,那么复杂度为O(K * n).
从上面可以看出,当n或者K大于一个数的时候,排序后查找肯定速度要快得多。
三.实现排序
1.单元排序
讨论:
(1).每次排序完都把最小的排在前面,越到后面需要循环比较的次数越少;
(2).返回交换的次数,为了方便跟冒泡排序法比较;
(3).算法复杂度 O(n^2)
2.冒泡排序
讨论:
(1).每次完都把最小的放在前面
(2).返回交换的次数,为了方便跟选择排序法比较
(3).算法复杂度O(n^2)
3.分治排序
讨论:
(1).把一个大的问题分成相同类型小的问题处理后,在组合起来
(2).大部分的交换都变成2个元素之间的交换,大大减少的循环的次数
(3).算法复杂度 O(n*ln(n))
附加上交换的代码:
四.总结:
1.单元排序和冒泡排序的比较:
(1).算法复杂度都一样。
(2).冒泡排序的交换次数远远大于选择排序,所以不建议使用冒泡排序,我也很少看到有人使用冒泡排序。除了教科书上,因为最直接。
2.分治排序提高了很高的效率,但是实现起来也相对困难,不是那么直接。这也许就是对我这种执着的人有很大的魅力。有时候为了提高一点效率,硬是付出了很多的时间。哈哈~~~
3.关于分治排序的合并算法,当我写出后,去网上看看其他人写的,发现就我用了一个循环实现,有点感慨,也许N人都不把代码放出来,或者搜索引擎没搜到,反正至少满足了我一点荣誉感,对于程序原来说,也许就够了。
4.我不赞成使用自己写的排序,包括我自己写的我也不会使用,只是为了研究一个问题,得到一种思维,甚至可以说得到一点满足感。如果需要在问题中使用到算法,我绝不犹豫的把现有的问题映射到已经写好的算法中
,比如说使用c++ STL。既安全,有省事,何乐而不为呢?
PS:因水平有限,不免有什么不妥之处,请各大看官不吝赐教,谢谢~
上学的时候学习排序是为了考试,是为了不挂科,在实际问题中,排序就显得不是那么重要了。特别是当使用c++ STL时,排序就是非常简单的事情了。
但为什么还需要学习排序呢?我个人感觉是为了理解细想,培养能力的一个必不可少的过程,像标题所提示的3个排序算法一样,特别是其中的分治算法对解决一切其他很多问题有深远的影响。
二.排序的用处.
排序是为了什么,在我接触的问题中,第一是为了显示,在游戏中必不可少,第二是为了多次查找。
比如说一个散列的数组需要查找一次,那么就直接遍历吧。最悲剧的情况,复杂度为O(n).
如果需要查找多次的话,那么排序后查找肯定要好的多。比如我们下面提到的分治排序,算法复杂度为O(n*ln(n)),如果再使用2分查找的话,复杂度为
O(ln(n)).如果有n个数,要使用K次查找的话,那么总的复杂度为O(n*ln(n) +
K*ln(n))。使用直接遍历的话,那么复杂度为O(K * n).
从上面可以看出,当n或者K大于一个数的时候,排序后查找肯定速度要快得多。
三.实现排序
1.单元排序
int SelectionSort(int* arr, const int length) { assert(arr != NULL); assert(length > 0); int Cnt = 0; // 计算交换次数 int len = length; int MinIndex = 0; int MinValue = 0; for (int i = 0; i < len; ++i) { MinIndex = i; MinValue = arr[i]; for (int j = i+1; j < len; ++j) // 从第一个数的后面开始循环 { if (arr[j] < MinValue) // 比最下的值小 { MinValue = arr[j]; MinIndex = j; } } if ( MinValue != arr[i]) { swap(&arr[i], &arr[MinIndex]); ++Cnt; } } return Cnt; }
讨论:
(1).每次排序完都把最小的排在前面,越到后面需要循环比较的次数越少;
(2).返回交换的次数,为了方便跟冒泡排序法比较;
(3).算法复杂度 O(n^2)
2.冒泡排序
int BubbleSort(int* arr, const int length) { int Cnt = 0; // 计算交换次数 //for (int i = 0; i < length; ++i) // 最直接版本 //{ // for (int j = i; j < length; ++j) // { // if (arr[i] > arr[j]) // { // swap(&arr[i], &arr[j]); // ++Cnt; // } // } //} for (int i = 0, j = i; j < length; j++) // 稍为改进版本,效果一样, { if (arr[i] > arr[j]) { swap(&arr[i], &arr[j]); ++Cnt; } if (j == length-1) // 到最后,重置 { ++i; j = i; } } return Cnt; }
讨论:
(1).每次完都把最小的放在前面
(2).返回交换的次数,为了方便跟选择排序法比较
(3).算法复杂度O(n^2)
3.分治排序
int Merge(int* arr, const int start, const int mid, const int end) { int len = end-start+1; // 计算长度 int* newArr = new int[len]; for (int x = start; x <= end; ++x) // 把原数组[start, end] 赋值到新数组 { newArr[x-start] = arr[x]; } int _mid = mid - start; // 新数组中间的值 int len1 = _mid+1; // 前半段的长度 int i, j; i = 0; // 前半段的索引 j = _mid+1; // 后半段的索引 int index = start; // arr数组的索引 while ((index-start+1) <= len) // 赋值没完 ,进行 len 次循环 { if (i < len1 && j < len) // 合并2个数组 { if (newArr[i] < newArr[j]) { arr[index++] = newArr[i++]; } else { arr[index++] = newArr[j++]; } } else if (i == len1 && j <= len ) // 如果有一个比较完了,把另一个数组元素依次放入后面 { arr[index++] = newArr[j++]; } else if (j == len && i <= len1) { arr[index++] = newArr[i++]; } } delete[] newArr; return index; } void DiviedMergeSort(int *arr, const int start, const int end) { if ((end-start) <= 1) // 如果只有2个或者更少的元素 { if (arr[start] > arr[end]) // 比较大小 { swap(&arr[start], &arr[end]); // 交换 } } else if (start < end) // 如果有2个以上的元素,并且数组下标合法 { int mid = (start + end) / 2; // 计算中间的元素,如果是3个元素的话,前2个为一组,后2个为一组 DiviedMergeSort(arr, start, mid); // 递归前面的一组 DiviedMergeSort(arr, mid+1, end); // 递归后面的一组 Merge(arr, start, mid, end); // 把分开的合并 } }
讨论:
(1).把一个大的问题分成相同类型小的问题处理后,在组合起来
(2).大部分的交换都变成2个元素之间的交换,大大减少的循环的次数
(3).算法复杂度 O(n*ln(n))
附加上交换的代码:
void swap(int* p1, int* p2) { int temp; temp = *p1; *p1 = *p2; *p2 = temp; }
四.总结:
1.单元排序和冒泡排序的比较:
(1).算法复杂度都一样。
(2).冒泡排序的交换次数远远大于选择排序,所以不建议使用冒泡排序,我也很少看到有人使用冒泡排序。除了教科书上,因为最直接。
2.分治排序提高了很高的效率,但是实现起来也相对困难,不是那么直接。这也许就是对我这种执着的人有很大的魅力。有时候为了提高一点效率,硬是付出了很多的时间。哈哈~~~
3.关于分治排序的合并算法,当我写出后,去网上看看其他人写的,发现就我用了一个循环实现,有点感慨,也许N人都不把代码放出来,或者搜索引擎没搜到,反正至少满足了我一点荣誉感,对于程序原来说,也许就够了。
4.我不赞成使用自己写的排序,包括我自己写的我也不会使用,只是为了研究一个问题,得到一种思维,甚至可以说得到一点满足感。如果需要在问题中使用到算法,我绝不犹豫的把现有的问题映射到已经写好的算法中
,比如说使用c++ STL。既安全,有省事,何乐而不为呢?
PS:因水平有限,不免有什么不妥之处,请各大看官不吝赐教,谢谢~
相关文章推荐
- C++实现各种基础排序(冒泡、选择、快排、插入、堆排、希尔、归并)
- 排序基础算法总结与c++实现
- 算法与数据结构基础 -- 排序 (C++实现)
- 各类排序C++实现(冒泡,选择,插入,快排,归并,堆排)
- 基础的排序C++实现
- c++实现冒泡,选择,插入,快速排序
- C++实现快速冒泡排序
- 黑马程序员——Java基础 数组排序的两种实现方法,选择和冒泡
- 四种经典排序 冒泡,快排,插入,选择 C++实现
- 冒泡排序 - C++分析实现
- 冒泡排序之C语言实现与冒泡排序之c++模板实现
- 分治排序的C/C++实现
- 算法学习 - 选择排序的稳定性讨论(C++实现)
- c++实现数据结构中的各种排序方法:直接插入、选择,归并、冒泡、快速、堆排序、shell排序
- 单链表的排序(快排和冒泡实现)以及查找中间结点
- 基础算法之冒泡排序
- C++中实现对map按照value值进行排序 - 菜鸟变身记 - 51CTO技术博客
- C++实训 单元十一 MFC GDI+编程基础
- Java基础-实现Comparator方式排序
- 回调函数实现冒泡法的多种排序