快速排序
2012-07-26 11:45
176 查看
快速排序(quicksort)的期望运行时间是Θ(n lgn),而最坏情况为Θ(n2)
快速排序与归并排序一样,也是基于分治的想法的,分治过程有三个步骤:
1、分解 将一个数组一分为二
2、解决 递归调用分治过程,对分出的两个子数组排序
3、合并 这里要理解的是,因为两个子数组都是就地排序,所以合并的过程并不需要操作
在《算法导论》以及后面的思考题中,介绍了好几种快排版本,下面我主要实现了算法导论正文的版本、随机化版本及思考题7-1的最初的快排版本
(1)下面是算法导论版本的快排代码及注释解析:
以下是算法导论中关于partition的原地排序过程的一个样例数组图例,看代码不能理解的话,可以就着下图顺着程序运行步骤理解下.
(2)接着是快排的随机化版本,以下是代码及其主要解析(我也偷懒下,如果是上面已出现的函数基本没什么大变化的话,我不会再进行注释)
(3)以下是最初的快排版本,主要的不同在于Partition函数的不同,但其目的基本是一样(这一个就没注释得那么详细了
):
快排终于写完了,这一两周一直想早点写完,但是因为一些事总是不能去写,好纠结....
还是那句话:“加油!”
快速排序与归并排序一样,也是基于分治的想法的,分治过程有三个步骤:
1、分解 将一个数组一分为二
2、解决 递归调用分治过程,对分出的两个子数组排序
3、合并 这里要理解的是,因为两个子数组都是就地排序,所以合并的过程并不需要操作
在《算法导论》以及后面的思考题中,介绍了好几种快排版本,下面我主要实现了算法导论正文的版本、随机化版本及思考题7-1的最初的快排版本
(1)下面是算法导论版本的快排代码及注释解析:
/* ***Author: asd ***Data: 2012/7/20 ***blog:http://blog.csdn.net/zhengjj_asd ***quick-sort (算法导论版) */ #include <iostream> using namespace std; int size; //size代表待排序元素个数 //对数组输出 void arr_display(int *a) { for(int i = 0; i < size - 1; ++ i) cout << a[i] << ' '; cout << a[size - 1] << endl; } //交换 void swap(int &a, int &b) { int t = a; a = b; b = t; } //快排的关键函数,对子数组a[p..r]进行就地重排 //以 key 即 a[r] 作为主元,围绕它划分子数组 //使函数结束后,保证子数组 a[p..i] <= a[i + 1] <= a[i + 2 .. r], 其中a[r + 1] = key; //具体操作我会在这段代码结束时,贴上算法导论上的样例图解,不懂的可以顺着程序模拟下过程就明白了 int Partition(int *a, int p, int r) { int key = a[r]; int i = p - 1; for(int j = p; j < r; ++ j) { if(a[j] <= key) { ++ i; swap(a[i], a[j]); } } swap(a[i + 1], a[r]); return i + 1; } //采用分治的思想,将一个子数组就地排序后,按partition中的key的位置,分为左右两个元素个数更小的两个子数组 //最后左右两个子数组合并时,因为对两个子数组是进行原地排序,所以并不需要任何合并操作 void Quick_Sort(int *a, int p, int r) { if(p < r) { int q = Partition(a, p, r); //*******以下这段主要是为了在学习快排过程中,了解partition对原数组就地排序的作用******** cout << "对第" << p + 1 << " 个元素到第 " << r + 1 << "个元素调用 Partition 结果为:"; arr_display(a); //************************************************************************** Quick_Sort(a, p, q - 1); Quick_Sort(a, q + 1, r); } } int main() { int a[100]; cout << "输入元素个数:"; cin >> size; cout << "输入各个元素:"; for(int i = 0; i < size; ++ i) cin >> a[i]; cout << "排序开始..." << endl; Quick_Sort(a, 0, size - 1); cout << "排序结束,结果为:"; arr_display(); return 0; }
以下是算法导论中关于partition的原地排序过程的一个样例数组图例,看代码不能理解的话,可以就着下图顺着程序运行步骤理解下.
(2)接着是快排的随机化版本,以下是代码及其主要解析(我也偷懒下,如果是上面已出现的函数基本没什么大变化的话,我不会再进行注释)
/* ***Author: asd ***Data: 2012/7/26 ***blog:http://blog.csdn.net/zhengjj_asd ***Quick_Sort (随机化版本) */ #include <iostream> #include <cstdlib> #include <ctime> using namespace std; int size; //******************以下这段在上面代码一样****** void arr_display(int *a) { for(int i = 0; i < size - 1; ++ i) cout << a[i] << ' '; cout << a[size - 1] << endl; } void swap(int &a, int &b) { int t = a; a = b; b = t; } int Partition(int *a, int p, int r) { int key = a[r]; int i = p - 1; for(int j = p; j < r; ++ j) { if(a[j] <= key) { ++ i; swap(a[i], a[j]); } } swap(a[i + 1], a[r]); return i + 1; } //*****************以上一样************ //求a ~ b中的一个随机数 int Rand(int a, int b) { return a + rand() % (b - a + 1); } //通过这个函数,将a[i]做为key,其中i是随机出来的值 //思考题 7-5 所说的“三数取中”划分则是,随机三个元素出来,然后取其中的中值做为partition的key; int Rand_Partition(int *a, int p, int r) { int i = Rand(p, r); swap(a[i], a[r]); return Partition(a, p, r); } //与上一个代码的Quick_Sort函数基本相同 void Rand_Quick_Sort(int *a, int p, int r) { if(p < r) { int q = Rand_Partition(a, p, r); cout << "对第" << p + 1 << " 个元素到第 " << r + 1 << "个元素调用 Partition 结果为:"; arr_display(a); Rand_Quick_Sort(a, p, q - 1); Rand_Quick_Sort(a, q + 1, r); } } int main() { //生成随机种子 srand( (unsigned)time( NULL ) ); int a[100]; cout << "输入元素个数:"; cin >> size; cout << "输入各个元素:"; for(int i = 0; i < size; ++ i) cin >> a[i]; cout << "排序开始..." << endl; Rand_Quick_Sort(a, 0, size - 1); return 0; }
(3)以下是最初的快排版本,主要的不同在于Partition函数的不同,但其目的基本是一样(这一个就没注释得那么详细了
):
/* ***Author: asd ***Data: 2012/7/26 ***blog:http://blog.csdn.net/zhengjj_asd ***Quick_Sort (Partition为最初的快排版本,Hoare) */ #include <iostream> using namespace std; int size; void arr_display(int *a) { for(int i = 0; i < size - 1; ++ i) cout << a[i] << ' '; cout << a[size - 1] << endl; } void swap(int &a, int &b) { int t = a; a = b; b = t; } //与算法导论版本唯一的不同就是partition的操作 //两者目的全都是一样,不同的是Hoare_Partition过程是将主元key放入划分的两个子数组中的某一个中 int Hoare_Partition(int *a, int p, int r) { int key = a[p]; int i = p - 1; int j = r + 1; while(1) { while(a[-- j] > key); while(a[++ i] < key); if(i < j) swap(a[i], a[j]); else return j; } } void Quick_Sort(int *a, int p, int r) { if(p < r) { int q = Hoare_Partition(a, p, r); cout << "对第" << p + 1 << " 个元素到第 " << r + 1 << "个元素调用 Partition 结果为:"; arr_display(a); Quick_Sort(a, p, q - 1); Quick_Sort(a, q + 1, r); } } int main() { int a[100]; cout << "输入元素个数:"; cin >> size; cout << "输入各个元素:"; for(int i = 0; i < size; ++ i) cin >> a[i]; cout << "排序开始..." << endl; Quick_Sort(a, 0, size - 1); return 0; }
快排终于写完了,这一两周一直想早点写完,但是因为一些事总是不能去写,好纠结....
还是那句话:“加油!”