排序之快速排序

大学期间学习了快速排序,下面是快速排序的代码:

public void quickSort(int[] a,int begin,int end){
   if(a == null || a.length <= 1 || begin >= end)return;//递归结束条件
   int L = begin;
   int R = end;
   int temp = a[begin];//哨兵设置为数组的第一个元素
   while(L < R){
      while(L < R && a[R] >= temp)R--;//从右往左,找到第一个小于temp的数
      if(L < R){//L<R,说明找到了
        a[L] = a[R];//替换
        L++; 
      }
      while(L < R && a[L] < temp)L++;//从左往右,找到第一个大于等于temp的数
      if(L < R){//L<R,说明找到了
        a[R] = a[L];//替换
        R--; 
      }
   }
   //出了while循环,意味着一次排序将要完成,别忘了最后对a[i]赋值为temp
   a[L] = temp; 
   quickSort(a,begin,L - 1);//分治
   quickSort(a,L + 1, end);//分治
}

在这里插入图片描述


下面来总结关于《算法 第4版》中的快速排序。

思路

1)先选择一个切片v(即上文中的哨兵),一般来说选择数组的第一个元素;

2)然后,从切片位置的下一个位置开始,从前往后查找到大于等于切片v的元素,停下来,i记录索引位置;从数组末尾向前查找,找到小于等于切片v的元素,停下来,j记录索引位置;

3)交换。将索引i位置和索引j位置的元素互换位置;

4)继续从i位置到右查找到大于等于v的元素,停下来;继续从j到左查找到小于等于v的元素,停下来,交换,直到i和j相遇;

5)i,j相遇后,将v的值和a[j] 的值互换,这时将数组分成了3个部分:< v;v;>v。

6)分别对v的左半部分和v的右半部分再次执行上述操作(递归)。
在这里插入图片描述

下图是《算法 第4版》的例子,其中a[lo]为切分元素。
在这里插入图片描述

参考代码

public void quickSort(int[] arr, int low, int heigh){
   if(low >= heigh)return;
   int k = partition(arr,low,heigh);//将数组分为2部分,左部分小于等于k,右部分大于等于k
   quickSort(arr,low,k - 1);//分治
   quickSort(arr,k + 1, heigh);//分治
}

public int partition(int[] arr, int low, int heigh){
   int i = low;
   int j = heigh + 1;//注意这里是 heigh+1
   int v = arr[low];//v每次选择第一个元素,注意这里不能写成arr[0]
   while(true){//死循环
      /**
        这里i先 +1,j先 -1,然后再进行比较,这样就可以理解 i ,j 的初值为什么那么设置了。(i从切片v的右边开始查找,j从heigh开始查找)
      **/
      while(arr[++i] < v){if(i == heigh)break;}//从左到右找 >=v的数,if防越界
      while(arr[--j] > v){if(j == low)break;}//从右到左找 <=v的数,if防越界
      if(i >= j)break;//循环结束条件
      swap(arr, i, j);//交换,然后进行下一轮循环查找(i++,j--)
   }
   swap(arr, low, j);//i,j相遇,交换arr[low]和arr[j]
   return j;//别忘返回
}

//注意参数
public void swap(int[] arr,int i,int j){
   int t = arr[i];
   arr[i] = arr[j];
   arr[j] = t;
}

在这里插入图片描述
注意:

partition方法中,while死循环中的第2个while中的越界判断可以省略掉:j一直往左走,当遇到小于等于v的值就停下来,而边界就是数组的首元素v,所以肯定会停下来,不用进行边界判断。


进阶

常考面试题:最小的k个数

题目描述:

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

参考代码:

 public int[] getLeastNumbers(int[] arr, int k) {
   if(arr == null || arr.length <= 0 || k < 0)return new int[]{};
   if(k >= arr.length)return arr;
   int[] res = new int[k];
   quickSort(arr,0,arr.length - 1,k);
   for(int i = 0; i < k; i++){
     res[i] = arr[i];
   }
   return res;
 }
 
 public void quickSort(int[] arr,int lower,int high,int k){
   int index = partition(arr,lower,high);
   if(index == k)return;
   else if(index > k)quickSort(arr,lower,index - 1,k);
   else quickSort(arr,index + 1,high,k);
 }
 
 public int partition(int[] arr, int lower,int high){
   int i = lower;
   int j = high + 1;
   int v = arr[lower];
   while(true){
     while(arr[++i] < v){if(i == high)break;}
     while(arr[--j] > v){if(j == lower)break;}
     if(i >= j)break;
     swap(arr,i,j);
   }
   swap(arr,j,lower);
   return j;
 }
  
 public void swap(int[] arr, int i, int j){
   int t = arr[i];
   arr[i] = arr[j];
   arr[j] = t;
 }

补充

LeetCode 215题:数组中的第K个最大元素

题目描述:

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

思路分析:

求第k大的数,就是将数组排序后,索引为length-k的数,然而,用上面的思路,可以不用全部进行排序。

参考代码:

public int findKthLargest(int[] nums, int k) {
   if(nums == null || nums.length == 0 || k < 0)
          new RuntimeException();
   int lower = 0;
   int high = nums.length - 1;
   k = nums.length - k;//第length-k个元素
   while(lower < high){
     int t = partition(nums,lower,high);
     if(k == t)break;
     else if(k < t)high = t - 1;
     else lower = t + 1;
   }
   return nums[k];
}

public int partition(int[] nums,int lower,int high){
   int i = lower;
   int j = high + 1;
   int t = nums[lower];
   while(true){
      while(nums[++i] < t)if(i == high)break;
      while(nums[--j] > t)if(j == lower)break;
      if(i >= j)break;
      swap(nums,i,j);
   } 
   swap(nums,lower,j);
   return j;
}

public void swap(int[] nums,int i,int j){
   int t = nums[i];
   nums[i] = nums[j];
   nums[j] = t;
}

三向切分的快速排序

public static void sort(int[] nums, int lower, int high){
   if(nums == null || nums.length == 0)return;
   int l = lower;
   int h = high;
   int i = l + 1;
   if(lower < high){
      int v = nums[lower];
      while(l <= h){
         if(nums[i] < v)swap(nums, i++, l++);
         else if(nums[i] > v)swap(nums, i, h--);
         else i++;
      }
      sort(nums, lower, l - 1);//注意:这里的递归要写在if内
      sort(nums, h + 1, high);//只有长度有效的时候才会正常运行,否则出现异常
   }
}

一次while遍历后的结果如下图:

在这里插入图片描述


双轴快速排序

public void doubleQuickSort(int[] nums, int lower, int high){
  if(lower > high)return;
  if(nums == null || nums.length == 0)return;
  if(nums[lower] > nums[high])swap(nums, lower, high);
  int p1 = nums[lower];
  int p2 = nums[high];
   
   
  int i = lower + 1;
  int j = lower + 1;
  int k = high - 1;
  while(j <= k){
    if(nums[j] < p1)swap(nums, i++, j++);
    else if(nums[j] > p2)swap(nums, j, k--);
    else j++;
  }
  swap(nums, lower, i - 1);//i左边都小于p1
  swap(nums, k + 1, high);//k右边都大于p2
  doubleQuickSort(nums, lower, i - 2);
  doubleQuickSort(nums, i, k);
  doubleQuickSort(nums, k + 2, high);
  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值