算法导论(3) 快速排序、计数排序、基数排序
来源:互联网 发布:linux timekeeper 编辑:程序博客网 时间:2024/06/06 09:06
1.快速排序
快速排序也采用了分治思想:
- 分解:数组A[p..r]划分为两个可能为空的子数组A[p..q-1]和A[q+1..r],前者均小于等于A[q],后者均大于等于A[q]。
- 解决:递归调用,对两个子数组继续采用快速排序的方法。
- 合并:数组是原址操作,不需要合并。
因为看过好多次快速排序的实现方式,因此学习了多种快速排序的实现方式,不过算法导论上的方法QuickSort2更容易理解以及记忆。其实只要会一种可以熟练应用就可以了。
template<class T>void QuickSort(T a[], int left, int right){ T key = a[(left + right) / 2]; T temp=0; int l, r; l = left; r = right; while (l< r) { while (a[r] > key){ r--; } while(a[l]<key){ l++; } if (l<=r){ temp = a[r]; a[r] = a[l]; a[l] = temp; r--; l++; } if (l == r){ l++; } } if (left < r) { QuickSort(a, left, l-1); } if (right >l) { QuickSort(a, r+1, right); }}template<class T>void QuickSort2(T a[], int left, int right){ T temp, x = a[right]; int i,j,q; if (left < right){ i = left - 1; for (j = left; j < right; j++) { if (a[j] <= x) { i += 1; temp = a[j]; a[j] = a[i]; a[i] = temp; } } temp = a[right]; a[right] = a[i + 1]; a[i + 1] = temp; q = i + 1; QuickSort2(a, left, q - 1); QuickSort2(a, q + 1, right); }}template<class T>void QuickSort3(T a[], int left, int right){ T temp, x = a[left]; int i, j, q; if (left < right){ i = right+ 1; for (j = right; j > left; j--) { if (a[j] >= x) { i -= 1; temp = a[j]; a[j] = a[i]; a[i] = temp; } } temp = a[left]; a[left] = a[i -1]; a[i - 1] = temp; q = i - 1; QuickSort2(a, left, q - 1); QuickSort2(a, q + 1, right); }}template<class T>void QuickSort4(T a[], int left, int right){ if (left >= right) { return; } int first = left; int last = right; int key = a[first]; while (first < last) { while (first < last && a[last] >= key) { --last; } a[first] = a[last]; while (first < last && a[first] <= key) { ++first; } a[last] = a[first]; } a[first] = key; QuickSort4(a, left, first - 1); QuickSort4(a, first + 1, right);}
快速排序的运行时间依赖于划分是否平衡,如果平衡,其性能与归并排序一样,如果不平衡,其性能接近于插入排序。最坏情况下,子问题分别包含n-1和0个元素,其运行时间时O(n^2),性能还不如插入排序,因为假如数组已有序,插入排序O(n),而快速排序还是O(n^2)。最好情况下为Θ(nlogn)。
快速排序的平均运行时间更接近与最好情况,而非最坏情况,即使每次划分的比例都是9:1,看似很不平衡,但其时间复杂度仍是O(nlogn)。
目前为止这几种排序方法都是通过元素之间的比较进行排序的,因此被称为比较排序,在最坏情况下,任何比较排序算法都需要做Ω(nlogn)次比较。因此归并排序和堆排序是渐进最优的。但快速排序通常是实际应用排序中最好的选择,因此其平均性能非常好。期望时间复杂度为Θ(nlogn),并且Θ(nlogn)其中隐含的常数因子非常小,能够进行原址排序。
2.计数排序
假设n个输入元素中每一个都是在0到k区间的一个整数,其中k为某个整数。计数排序的基本思想是,对一个输入元素x,确定小于x的元素个数,利用这一信息将其放在对应的输出位置上。
void CountSort(int a[], int b[], int n,int k){ vector<int> c; c.resize(k); for (int i = 0; i < k; i++) { c[i] = 0; } //记录某个元素出现了几次 for (int j = 0; j < n; j++) { c[a[j]] += 1; } //累加,记录某个元素应该出现在哪个位置上 for (int i = 1; i < k; i++) { c[i] += c[i - 1]; } //将原数组中元素放到对应的位置上,c数组减一是为处理有相同值的情况 for (int j = n-1; j>=0; j--) { b[c[a[j]]-1] = a[j]; c[a[j]] -= 1; }}
总得时间代价是Θ(k+n),实际工作中,如果k=O(n)时,一般会采用计数排序,运行时间为Θ(n)。计数排序是稳定的,具有相同值的元素在输入输出数组中的相对位置时一样的。个人感觉上只能用来处理整数排序,而且运行时间要取决于k值的大小。
3.基数排序
通过对数值一位一位(不是二进制是十进制)比较而达到排序的目的,通常采用按最低有效位来排序。一位数的排序算法必须是稳定的。感觉上也是针对整数的排序,但应该可以改写成针对其他类型数据的排序,时间复杂度上也可以达到线性的时间代价。
//求数据的最大位数int maxbit(int data[], int n) { //保存最大的位数 int d = 1; int p = 10; for (int i = 0; i < n; i++) { while (data[i] >= p) { p *= 10; d++; } } return d;}//基数排序void radixsort(int data[], int n) { int d = maxbit(data, n); int *tmp = new int[n]; //计数器 int *count = new int[10]; int i, j, k; int radix = 1; //进行d次排序 for (i = 1; i <= d; i++) { //计数排序 for (j = 0; j < 10; j++) count[j] = 0; for (j = 0; j < n; j++) { k = (data[j] / radix) % 10; count[k]++; } for (j = 1; j < 10; j++) count[j] = count[j - 1] + count[j]; for (j = n - 1; j >= 0; j--) { k = (data[j] / radix) % 10; tmp[count[k] - 1] = data[j]; count[k]--; } for (j = 0; j < n; j++) data[j] = tmp[j]; radix = radix * 10; } delete[]tmp; delete[]count;}
4.桶排序
算法导论中讲述的是假设要排序的数组均匀、独立分布在[0,1)之间。然后将[0,1)区间划分为n个大小相同的区间,称为桶,然后先对每个桶中的元素进行排序,最后按照次序将桶中元素列出即可。时间复杂度也是线性时间。
感觉类似于基数排序,但是是按照最高位进行排序,然后把相同最高位的分别进行排序,最后按照次序列出。这也说明基数排序也是可以修改为对浮点数等类型进行排序。这两种算法的基本思想就是一位一位进行排序,而书中针对一位数排序时,基数排序是让采用稳定的排序方法,而桶排序采用的是插入排序。这几种排序方法均可以在线性时间内完成。
- 算法导论(3) 快速排序、计数排序、基数排序
- 算法导论之排序:快速排序、归并排序、计数排序、基数排序、桶排序
- 【算法导论】第五课 线性排序(基数排序 计数排序)
- 算法导论------------计数排序and基数排序
- 《算法导论》读书笔记--计数排序&基数排序
- 算法导论学习笔记(六):计数排序与基数排序
- 算法导论-- 线性时间排序(计数排序、基数排序、桶排序)
- 算法导论第八章计数排序-基数排序-桶排序-c++
- 算法导论 第8章 线性时间排序(计数排序、基数排序、桶排序)
- 冒泡排序、快速排序、基数排序、计数排序算法性能分析
- 算法导论 计数排序
- 【算法导论】计数排序
- 【算法导论】计数排序
- 算法导论 计数排序
- 计数排序----算法导论
- 算法导论 计数排序
- [算法导论]计数排序
- 基础排序算法总结(插入、选择、冒泡、合并、二分查找、堆排序、快速排序、基数排序、桶排序、计数排序)
- 巩固基础篇:八皇后问题:经典回溯法
- 程序员的升级之路
- 存储过程使用DDL语句
- [APIO 2016] 赛艇 Boat:动态规划、离散化、组合数学和常数优化
- Android开源项目 工具库
- 算法导论(3) 快速排序、计数排序、基数排序
- hdu 2586 How far away
- 放球问题
- 方便回顾篇进程
- 泛型的典型应用:人的信息类和接口
- Java 之多线程同步
- html+js+cgi
- 方便查看篇---内存
- 【Python学习笔记】函数式编程:装饰器