快速排序
思想
快速排序采用的思想是分治思想。
基准(pivot):快速排序是找出一个元素(理论上可以随便找一个)作为基准(pivot), 然后对数组进行分区操作, 使基准左边元素的值都不大于基准值, 基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。
递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。
举例说明一下吧,这个可能不是太好理解。假设要排序的序列为
2 2 4 9 3 6 7 1 5 首先用2当作基准,使用i j两个指针分别从两边进行扫描,把比2小的元素和比2大的元素分开。首先比较2和5,5比2大,j左移
2 2 4 9 3 6 7 1 5 比较2和1,1小于2,所以把1放在2的位置
2 1 4 9 3 6 7 1 5 比较2和4,4大于2,因此将4移动到后面
2 1 4 9 3 6 7 4 5 比较2和7,2和6,2和3,2和9,全部大于2,满足条件,因此不变
经过第一轮的快速排序,元素变为下面的样子
[1] 2 [4 9 3 6 7 5]
之后,在把2左边的元素进行快排,由于只有一个元素,因此快排结束。右边进行快排,递归进行,最终生成最后的结果。
代码
1 #include <iostream> 2 #include <stdlib.h> 3 4 using namespace std; 5 6 /* 7 * (1) key = arr[left] ; // left位置空了,放比较小的数 8 9 * (2) 首先要找right side小于key的值,然后移动到左边; // right位置空了,放较大的数 10 * (3) 从left side 开始,找打大于key的值,放在 // 又将left位置空了 11 * (4) 循环(2)(3)直至,left == right,将arr[left] = key, 12 * (5) 将“已经有序的两个数组” 分治,重复以上步骤 13 14 */ 15 16 void quicksort(int arr[], int low, int high) 17 { 18 if(low<high) 19 { 20 int key = arr[low]; 21 int left,right; 22 left = low; right = high; 23 while(left<right){ 24 // 数组右边第一个小于key值 25 while(left<right && arr[right]>key) right--; 26 arr[left] = arr[right]; // 将小于key值移位到左边 27 28 // 数组左半部分,第一个大于等于key的值 29 while(left<right && arr[left]<=key) left++; 30 arr[right] = arr[left];// 将大于key的值放在right边 31 } 32 33 arr[left] = key; // 重新将原来的空位填充 34 35 // 递归排序 36 quicksort(arr,low,left-1); 37 quicksort(arr,left+1,high); 38 } 39 } 40 41 int main() 42 { 43 int arr[] = {2,4,2,1,5,4,8,7,1,8}; 44 quicksort(arr,0,9); 45 for(int i=0;i<10;i++){ 46 cout << arr[i] << " "; 47 }cout << endl; 48 49 return 0; 50 }
分步解析
快速排序可以像归并排序那样分析:
划分问题: 把数组的各个元素重排后分成左右两个部分,使得左边的任意元素都小于或等于右边的任意元素;
递归求解: 把左右两部分分别排序;
合并问题: 不用合并,因为此时数组已经完全有序;
1 #include <iostream> 2 3 using namespace std; 4 5 6 int partion(int arr[], int start, int end) 7 { 8 int temp = arr[start]; 9 int left = start, right = end; 10 while(left<right){ 11 // 找到右边第一个小于pivot的值 12 while(left<right && arr[right]>temp) right--; 13 arr[left] = arr[right]; 14 15 // 找到左边第一个大于pivot的值 16 while(left<right && arr[left]<=temp) left++; 17 arr[right] = arr[left]; 18 } 19 arr[left] = temp; 20 return left; 21 } 22 23 void quickSort(int arr[], int start, int end) 24 { 25 if(end>start){ 26 int pivot = partion(arr,start,end); // 新的pivot 27 quickSort(arr, start, pivot-1); // 根据pivot进行递归排序 28 quickSort(arr, pivot+1, end); 29 } 30 } 31 32 int main() 33 { 34 int n; 35 int arr[1000]; 36 int T[1000]; 37 while(cin>>n){ 38 for(int i=0;i<n;i++){ 39 cin >> arr[i]; 40 } 41 quickSort(arr, 0, n-1); 42 for(int i=0;i<n;i++){ 43 cout << arr[i] << " " ; 44 }cout<<endl; 45 46 } 47 }
效率分析
快速排序的时间主要耗费在划分操作上,对长度为k的区间进行划分,共需k-1次关键字的比较。
最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。时间复杂度为O(n*n)
在最好情况下,每次划分所取的基准都是当前无序区的"中值"记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:O(nlgn)
尽管快速排序的最坏时间为O(n2),但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。它的平均时间复杂度为O(nlgn)。
参考博文: http://www.cnblogs.com/luchen927/archive/2012/02/29/2368070.html