给快速排序,随机化快速排序,双路快速排序写了一些详细注释(java)

为了把这玩意整明白写了不少注释,先贴上来吧,回头有时间慢慢解释

快速排序在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快 

算法步骤

  1. 从数列中挑出一个元素,称为 "基准"(pivot);

  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

                                                                                               来自菜鸟教程

首先是前面的部分,主要就是生成随机数组和递归调用,分治法,以及一个swap

每次调用返回一个中间的数,这个数是在正确的位置的,接下来要把这个mid位置两边,既left-mid-1,mid+1-right分别执行一次排序方法

public class test
{
    @Test
    public void quickSort(){
        int[] list = new int[10];
        for(int i=0;i<10;i++){
            list[i]=new Random().nextInt(100);
        }
        sort(list,0,list.length-1);
    }


    public void sort(int[] list,int low,int high){
        if(left<right){
            int mid =partition(list,low,high);
            //System.out.println(Arrays.toString(list));

//因为顺序是先进行partition再递归调用,运行时会先排最大的,再排两半边小的,再排更小的
//按从大到小的顺序,直到每一段长度都为1

            sort(list,low,mid-1);
            sort(list,mid+1,high);
        }
    
    private static void swap(int[] list,int i,int j){
        int temp = list[i];
        list[i]= list[j];
        list[j]= temp;
    }
}

常规快速排序经常把第一个数作为基准,但是如果待排列数组是完全倒序之类的情况,时间复杂度会趋近于最坏情况

为了让时间复杂度更接近平均值,在开始时随机选择一个数与最前面的数交换

public static int partition(int[] list,int low,int high){

int r = low+(new Random().nextInt(high-low+1));
swap(list,low,r);                     //随机选择一个数与最前面交换,作为基准数值
int mid = low;                        //基准初始位置在第二个数
System.out.println(list[low]);
for(int i = low+1;i<=high;i++) {
if (list[i] < list[low]) {
mid++;                                  //基准位置右移一位
swap(list, mid, i);                    //将比基准小的数与当前基准位置交换
                                        
}
System.out.println(Arrays.toString(list));
}                                       //此时从下标low+1到下标mid全都是比下标low更小的了
swap(list,mid,low);                     //最后让基准值所在的low和基准位置mid交换
                                       //这样mid之前的全都小于等于mid了
                                       //同理也可以先把基准值放在high最后再换
System.out.println(Arrays.toString(list));
return mid;

}

双路快速排序:排除选定的基准数之后,从最左端和最右端向中间靠拢

这种写法会在外层while的每一轮,分别对左端和右端进行一次交换数据的尝试,但不需要单独的swap()方法,最初单独拿出来存放的基准值就是swap里的那个temp,每一轮循环结束后刚好会剩下一个位置给基准值放进去

反正我是绕晕了,但代码写起来很优雅很好记,至于面试官会不会被整晕,那就另说了

public static int partition(int[] list,int low,int high){
    int temp = list[low]; //以第一个数为比较值

    if(low>=high){
        return low;
    }
    while(low<high){
        while( low<high &&list[high]>=temp){ //从右边找第一个比temp小的值
            high--;
        }
        list[low]=list[high];                //将这个比temp小的值放到最前面low的位置,
                                             //low本来的值存在temp中,high多了一个


        while(low<high && list[low]<=temp){   //从左边找第一个比temp大的值
            low ++;
        }
        list[high] = list[low];            //将这个比temp大的值放到high那边去
                                          //high本来的值已经存进旧的low里,这里可以直接覆盖
                                          //这样low又多了一个,下一轮会被覆盖掉

                                          //如果没找到,low=high,数据没有变化
//如果只有两个数,需要让low/high最后停在tmp的位置
//即发生交换的话停在交换的二者的后面
//所以必须先high发生交换,后面low++,下一轮时high不发生交换,high--,跳出循环了
//如果反过来,low发生交换然后high--,low不交换low++,就错了
    }
    list[low] = temp;                      //最后low与high相等,而且与某个之前的值重复
                                           //这个多出的位置放最初的temp
    
    return low;
}

另一种双路快速排序,循环体里简单粗暴两个while,两边索引总会停在两个需要换到另一边的数上或者两个数相等,所以直接一个swap完事,逻辑上好理解多了

只是最后需要判断一下mid的值该放在哪

private static int partition(int[] list,int low,int high){
                //随机化懒得写了
    int midNum =low;
    low++;
    while(true){
        while (low<high && list[high]>=list[midNum]){
            high--;
        }
        while(low<high && list[low]<=list[midNum]){
            low++;
        }
                                //直到low与high相等或者
                                //low停在比mid大的数,high停在比mid小的数上

        swap(list,low,high);    //交换这两个数

                                //如果low与high相等了,跳出循环,否则再找下一对
        if(low >=high)break;

    }
                                 //跳出时low=high
                                 //但是这个位置是比基准大还是小不确定
                    
    if(list[midNum]<list[low]) {
        low--;                   //如果是比基准大,就跟low左边的数字交换
    }
    return low;

最后是连在一起的完整代码,测试的时候自己改n的值和   partition 123

快速排序

package CollectionTest;

import org.junit.Test;

import java.util.Arrays;
import java.util.Random;

public class test
{
    @Test
    public void quickSort(){

        final static int n = 100;

        int[] list = new int[n];
        for(int i=0;i<n;i++){
            list[i]=new Random().nextInt(100);
        }
        sort(list,0,list.length-1);
    }
    public void sort(int[] list,int left,int right){
        if(left<right){
            int mid =partition1(list,left,right);
            System.out.println(Arrays.toString(list));

            sort(list,left,mid-1);
            sort(list,mid+1,right);
        }
    }
    
public int partition1(int[] list,int left,int right){

        int r = low+(new Random().nextInt(high-low+1));
        swap(list,low,r);  

        int mid = left;
        int tmp = list[left];
        int i =left+1;
        for(;i<=right;i++){
            if(list[i]<list[left]) {
                mid++;
                swap(list, i, mid);
            }
        }
        swap(list,left,mid);
        return mid;
    }
public int  partition2(int[] list,int left,int right){
        int mid = left;
        int midNum = list[left];
        left++;
    //System.out.println("  "+left+"  "+right);
        while(left<right){
            while(left<right&&list[left]<=midNum){
                left++;
            }
            while(left<right&&list[right]>=midNum){
                right--;
            }
            swap(list,left,right);
        }
        if(list[left]>midNum) {
            left--;
        }
        swap(list,left,mid);
        return left;

}
public int partition3(int[] list,int left,int right){
    int tmp = list[left];
        while(left<right){

            while(left<right&& list[right]>=tmp){
                right--;
            }
            list[left]=list[right];


           while(left<right&& list[left]<=tmp){
               left++;
           }
           list[right]=list[left];



        }
    list[left]=tmp;
    return left;

}

    public void swap(int[] list,int i,int j){
        int tmp = list[i];
        list[i]=list[j];
        list[j]=tmp;

    }

}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值