赞
踩
先看表格,下面慢慢解释。
- void bubbleSort(int * a, int n)
- {
- bool isSorted = false;
- for (int i = n - 1; !isSorted && i > 0; --i) // 待排序范围
- {
- isSorted = true;
- for (int j = 0; j < i; ++j) // 从开头到无序的前一个位置
- {
- if (a[j] > a[j + 1])
- {
- std::swap(a[j], a[j + 1]);
- isSorted = false;
- }
- }
- }
- return;
- }
冒泡排序,相邻两两交换,每次交换消去一个逆序对。一个序列最多有n²/2个逆序对,所以复杂度是O(n²)。两个相邻的data若一样,则不会被交换。所以冒泡排序是稳定的。
对于一个算法,补充如下思考。(来自参考资料1)
robustenss:应对退化(degeneracy)情况,比如n<=0,上述代码仍可正常返回。
重用性:对于float/char等类型,算法应能推广。见下代码。这是一个成员函数模板。
实际上,stl中的排序算法在algorithm中,独立于容器。是函数模板。
- template <typename T>
- void Vector<T>::bubbleSort(int lo, int hi)
- {
- bool isSorted = false;
- for (int i = size - 1; !isSorted && i > 0; --i)
- {
- isSorted = true;
- for (int j = 0; j < i; ++j)
- {
- if (elem[j] > elem[j + 1])
- {
- std::swap(elem[j], elem[j + 1]);
- isSorted = false;
- }
- }
- }
- return;
- }
此后我们的代码都写为模板的形式,Vector的定义见:
总结:
1. 出于对空间复杂度的考量,我们不是在每次merge时开辟临时数组,而是在归并排序的最外层函数(调整接口的那一层)开辟一个最大的临时数组,然后每次merge或递归时带着这个临时数组的地址作为参数。
2. 左闭右开区间大大的简化了表达式与思维量。
3. 分治法的复杂度分析(递推式)
4. 临时数组来回倒腾可以把already设置成lo
5. 所有排序都是,参数是xxxSort(int lo, int hi), 但我们将其映射从0开始排序,共hi-lo个元素
6.error:不允许使用默认参数 可能原因 ①函数重复②从右向左③声明时指定 非静态成员变量当默认参数虽然不会报这个错,但也不行
7. 提高效率:tmp数组和elem每趟归并从一个倒到另一个,下次再倒回来。
8. 非递归归并映射之后,不能再用merge了,应该再写一个,以newElem为待归并数据,或压根不用映射。
- template <typename T>
- void Vector<T>::mergeSort_rec(int lo, int hi) //统一接口,递归版归并排序
- {
- T * tmp = new T[hi];
- _mergeSort(lo, hi, tmp);
- delete[]tmp;
- return;
- }
-
- template <typename T>
- void Vector<T>::_mergeSort(int lo, int hi, T * tmp)
- {
- if (hi - lo <= 1) return;
- else
- {
- _mergeSort(lo, (lo + hi) / 2, tmp);
- _mergeSort((lo + hi) / 2, hi, tmp);
- merge(lo, (lo + hi) / 2, hi, tmp);
- }
- return;
- }
-
- template <typename T>
- void Vector<T>::merge(int lo, int mi, int hi, T * tmp)
- {
- int left = lo, right = mi;
- int already = 0;
- while (left != mi && right != hi)
- {
- if (elem[left] <= elem[right])
- {
- tmp[already] = elem[left];
- ++left;
- ++already;
- }
- else
- {
- tmp[already] = elem[right];
- ++right;
- ++already;
- }
- }
- while (left != mi) tmp[already++] = elem[left++];
- while (right != hi) tmp[already++] = elem[right++];
-
- for (int i = lo; i < hi; ++i)
- {
- elem[i] = tmp[i - lo];
- }
- return;
- }
-
- template <typename T>
- void Vector<T>::mergeSort_nonrec(int lo, int hi) //非递归版归并排序
- {
- for (int i = 0; i < size; ++i) std::cout << elem[i] << " ";
- std::cout << std::endl;
-
- // 共 n 个元素
- int n = hi - lo;
-
- // 归并用的临时数组
- T * tmp = new T[hi];
-
- //最长有序区间长度
- int length = 1;
-
- //归并后的有序区间长度(不算尾巴)
- int doublelength = 2;
-
- for (; length < n; length *= 2, doublelength *= 2) //每一躺归并
- {
- for (int i = 0; (i+1)*doublelength <= n; ++i)
- {
- merge(i*doublelength+lo, i*doublelength + length+lo, (i + 1)*doublelength+lo, tmp); //加lo是取消映射
- }
- // 尾巴
- if (n % doublelength > length) merge(n/doublelength*doublelength + lo, n / doublelength * doublelength+length + lo, n + lo, tmp);
- for (int i = 0; i < size; ++i) std::cout << elem[i] << " ";
- std::cout << std::endl;
- }
-
- delete[]tmp;
- return;
- }
稳定与否,取决于怎么移动元素。
每次选出最值,只一次交换,那就不稳定。
eg.
A 80 B 80 C 70 升序排列
第一趟 选出A,swap(A, C) 变为 C 70 B 80 A 80
第二趟 选出B,序列不变,还是 C 70 B 80 A 80 排序完成
所以不稳定
有人说把大于号变成大于等于号,这样虽然在前缀中选出的最大值是最靠右的那一个,但是swap将原本最末位置的元素提到了前面,破坏了原有顺序,请看下例
A 90 B 80 C 70 D 20 E 20
第一趟选出B(因为用大于等于号,所以选出的是B不是A)E20 B 80 C 70 D 20 A 90
注意这样E就跑到D左边去了
第二趟:E20 D 20 C 70 B 80 A 90
第三趟、第四趟都是E20 D 20 C 70 B 80 A 90
还是不稳定
(来自我的知乎回答)
基于链表或将所有元素左移,则稳定。
复杂度是O(n²)还是O(nlogn)受限于每趟选出最大值的方法,若用堆,则成为堆排序,不稳定,O(nlogn)。
所以背表格是没有用滴。但是“简单选择排序”的算法就是基于数组、扫描选最值、swap 所以是O(n²)且不稳定。
以下代码以基于List的选择排序为例。 数据结构List的定义请看此处
- template <typename T>
- void List<T>::selectionSort(listNode<T>*p, int n)
- {
- // 找到有序后缀(注意,初始长度为0)
- listNode<T>* pos = p; //一会儿要在pos之前插入
- for (int i = 1; i <= n; ++i) pos = pos->succ;
-
- for (int rank = 0; rank < n; ++rank, pos = pos->pred) //有序后缀的长度
- {
- // 找前缀中最大值
- listNode<T> * tmp_max = Max(pos->pred, n - rank);
-
- // 插入后缀最前面
- insertBefore(tmp_max->data, pos);
- remove(tmp_max);
- }
- }
-
- // 从p开始往前找n个,这些data之中最大的,且最右的
- template <typename T>
- listNode<T>* List<T>::Max(listNode<T>* p, int n)
- {
- listNode<T> * tmp_max = p;
- while (n-- && p != head)
- {
- if (p->data > tmp_max->data) tmp_max = p;
- p = p->pred;
- }
- return tmp_max;
- }
- template <typename T>
- void List<T>::insertionSort(listNode<T>*p, int n)
- {
- for (int rank = 1; rank <= n; ++rank) //有序前缀的长度
- {
- listNode<T>* pos = search(p->data, p->pred, rank);
- insertAfter(p->data, pos);
- //p = p->succ;
- p = remove(p->pred);
- }
- }
邓俊辉 数据结构
陈越 数据结构
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。