快排及其应用

一、快排的引入:

  1.给定一个数组arr,和一个数num,请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。

     这就是快排的一个partition的过程,简单,代码就不给了。

  2.稍微进阶:荷兰国旗问题:给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边

    解决这个问题,可以对经典快排进行优化,因为划分为等于num的数的区域不用在比较了,减少了比较次数

         

 1 public static int[] partition(int[] arr, int l, int r, int p) {
 2         int less = l - 1;
 3         int more = r + 1;
 4         while (l < more) {
 5             if (arr[l] < p) {
 6                 swap(arr, ++less, l++);
 7             } else if (arr[l] > p) {
 8                 swap(arr, --more, l);
 9             } else {
10                 l++;
11             }
12         }
13         return new int[] { less + 1, more - 1 };
14     }

  二、快排:基于上述荷兰国旗问题实现

    

 1 public static void quickSort(int[] arr) {
 2         if (arr == null || arr.length < 2) {
 3             return;
 4         }
 5         quickSort(arr, 0, arr.length - 1);
 6     }
 7 
 8     public static void quickSort(int[] arr, int l, int r) {
 9         if (l < r) {
10             swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
11             int[] p = partition(arr, l, r);
12             quickSort(arr, l, p[0] - 1);
13             quickSort(arr, p[1] + 1, r);
14         }
15     }
16 
17     public static int[] partition(int[] arr, int l, int r) {
18         int less = l - 1;
19         int more = r;
20         while (l < more) {
21             if (arr[l] < arr[r]) {
22                 swap(arr, ++less, l++);
23             } else if (arr[l] > arr[r]) {
24                 swap(arr, --more, l);
25             } else {
26                 l++;
27             }
28         }
29         swap(arr, more, r);
30         return new int[] { less + 1, more };
31     }
32 
33     public static void swap(int[] arr, int i, int j) {
34         int tmp = arr[i];
35         arr[i] = arr[j];
36         arr[j] = tmp;
37     }

三.快排的应用 

   1.top k问题

    用经典快排每次partition返回的值index,当index == k - 1,这左边(或右边)就是top k最小(或最大)值,可以达到O(n)的时间复杂度

 1 public int findKth(int[] p,int L,int R,int k){
 2             if(L > R || k < 1)//检查输入参数是否合法
 3                 return -1;
 4             if(L == R)//如果L等于R说明已找到,直接返回
 5                 return p[R];
 6             int temp = quickSort(p,L,R);//进行一次快排,返回下标
 7             if(k+L == temp+1)//如果k+L等于返回的下标加1(L不一定从0开始)
 8                 return p[temp];//则直接返回
 9             if(k+L < temp+1)//如果k+L小于返回的下标加1
10                 return findKth(p,L,temp-1,k);//在temp的左边查找第k大数
11             else//否则,在temp的右边部分查找第k-(temp-L+1)大数。这里,右边的第
12                 //k-(temp-L+1)大数就是整个数组的第k大数
13                 return findKth(p,temp+1,R,k-(temp-L+1));                   
14         }   
15        /*
16         * 一次快速排序
17         *以p[L]为比较对象,比p[L]大或等于的在其左边,否则在其右边
18         */
19         public int quickSort(int[] p ,int L,int R){
20             if(L >= R)
21                 return -1;
22             int i = L;
23             int j = R;
24             int temp = p[L];           
25             while(i < j){
26                 while(i < j && p[j] < temp) j--;
27                 if(i < j){
28                     p[i] = p[j];
29                     i++;
30                 }
31                 while(i < j && p[i] > temp) i++;
32                 if(i < j){
33                     p[j] = p[i];
34                     j--;
35                 }           
36             }     
37             p[i] = temp;
38             //去掉以下两句注释,再将return注释掉,并且将返回值改为void,
39             //就是一个完整的快速排序
40             //quickSort(p ,L,i-1);
41             //quickSort(p ,i+1,R);
42             return i;
43         }

 当然还有一些题:1.对字符数组排序,所有的小写字母在前,大写字母在后

                                 2.所有的负数在前,正数在后,0在中间等等这类通过某种属性进行划分为两路或三路的过程,都可以基于partition的过程

猜你喜欢

转载自www.cnblogs.com/ycf5812/p/10826584.html