• 手写快排


    前言
    快速排序是一种很重要的排序算法,我花了不少时间去理解并总结它,希望可以通过图文的方式让你快速理解快速排序,并能手撸一个快排。

    快速排序简介
    快速排序是一种很不错的排序算法,算法复杂度为n*logn。快排使用了分而治之的思想,每次排序是都找到一个基准(我们学习时经常使用第一个作为基准),然后把小于基准的元素放到基准元素的左边,大于基准的元素放到基准元素的右边,这样一次排序下来,基准元素左边都是小于(等于)基准的数,基准右边的元素都是大于(等于)基准的元素了。快速排序关键点就是找到这样一个基准并将其放到恰到的位置。

    算法思路
    定义一个快速排序函数,arr是要排序的数组,l指向要排序的数组最左边的元素,r指向要排序的数组最右边的元素。

    public static void quick_sort(int arr[],int l,int r){
      if(l >= r) return;
      int p = partition2(arr,l,r);
      quick_sort(arr,l,p-1);
      quick_sort(arr,p+1,r);
    }


    那么现在关键就是这么实现这个partition2函数

    假设现在有一个数组arr[]:

    我们每次都取第一个元素为基准元素,定义i指向基准的下一个元素位置,j指向最后一个元素的位置: 


    i 从左向右扫描,如果arr[i] <= v , i++ 
    j从右向左扫描,如果 arr[j] > v ,j– 
    直到i指向一个大于v的元素,j指向一个小于j的元素,且 这个过程中 i <= j

    此时i左边(除基准元素)都是小于v的,j右边都是大于v的,现在只需要交换arr[i]与arr[j]的位置,就可以把小于v的放左边,大于v的放右边,然后i++,j–

    继续比较,此时arr[i] = 4 < 5,i++ ,arr[j] > 5 j–

    到这里 i > j 了,退出循环,最后把基准v与arr[j]交换位置,即把v放到了小于v的元素的最后一个,此时一次快速排序就完成了。

    代码实现

    public static int partiton2(int arr[],int l,int r){
      int v = arr[l];
      int i=l+1,j=r;
      while(true){
        while(i<=r&&arr[i]<v) i++;
        while(j>l&&arr[j]>v) j--;
        if(i>j)
          break;
        int t=arr[i];
        arr[i]=arr[j];
        arr[j]=t;
        i++;
        j--;
      }
      int t=arr[l];
      arr[l]=arr[j];
      arr[j]=t;
      return j;
    }



    快速排序的几个问题

    1、对于几乎有序的数组,快速排序返回的基准的位置都在第一个或很靠前的位置,使得对数组的切分不够平均,可能使得快速排序的时间复杂度退到o(n^2) , 对于这种情况,可以在排序时选取数组中的一个随机位置的数作为基准数。

    2、对于数组中的元素有很多相等元素时,也会导致快排对数组的切分不平衡,此时快排的时间复杂度也可能退到o(n^2) , 对于这种情况,可以使用三路快速排序,把小于基准v的放左边,等于v的放中间,大于v的放右边,就避免了左右不平衡的问题。

    3、在进行快排时候,当数比较少的时候,使用插入排序代替快排可提高算法效率。

    三路快速排序
    三路快速排序针对数组中有很多相等元素时照样能有很不错的效率,其主要思想就是将数组中的数分成三个部分:小于基准v的放左边,等于v的放中间,大于v的放右边。 
    代码

    /**
         * 三路快速排序,确定基准v后将数分成"小于v的","等于v的","大于v的"
         * @param arr
         * @param l
         * @param r
         */
        public static void quickSort3Ways(int arr[],int l,int r){
            if(l >= r) return;
            int v = arr[l];
            //初始时,i指向基准位置,j指向最后一个元素的下一个位置
            int i = l , j = r + 1;
            //cur指向当前要进行比较的元素
            int cur = i + 1; 
            while(cur < j){
                //如果当前元素大于基准元素,将次元素放到后面去,j--
                if(arr[cur] > v){
                    int t = arr[cur];
                    arr[cur] = arr[j-1];
                    arr[j-1] = t;
                    j--;
                //如果当前元素小于v,将它放到i前面去
                }else if(arr[cur] < v){
                    int t = arr[cur];
                    arr[cur] = arr[i+1];
                    arr[i+1] = t;
                    i++;
                    cur++;
                //若当前元素等于v,当前指针cur后移即可
                }else{
                    cur++;
                }   
            }
            //最后交换arr[l](基准元素)与arr[i]的位置
            int t = arr[i];
            arr[i] = arr[l];
            arr[l] = t;
            //对基准元素左边快排
            quickSort3Ways(arr,l,i-1);
            //对基准元素右边快排
            quickSort3Ways(arr,j,r);
        }

  • 相关阅读:
    Algs4-2.2.24-改进的有序测试
    Algs4-2.2.23-2比较正文中实现的归并和反向复制辅助数组归并之间的性能
    ssh登录卡住问题
    DELL R730安装ESXI虚拟化
    Linux umount的device is busy问题
    shell脚本调试技巧
    git编译安装
    卸载gitlab
    磁盘性能测试方法
    N! HDU 1042
  • 原文地址:https://www.cnblogs.com/strawqqhat/p/10602199.html
Copyright © 2020-2023  润新知