快速排序

思想

快速排序采用的思想是分治思想。

基准(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

posted on 2016-04-19 19:06  skipping  阅读(158)  评论(0)    收藏  举报

导航