快速排序——三种划分方式

思路:

1.分治思想:先划分成两个问题,然后对两个子问题递归排序,最后再合并。
2.核心算法:快排的核心在于划分问题(找到分界点)。
在这里插入图片描述

代码:

//快速排序
public static void quickSort(int[] arr, int left, int right) {

    if(left < right) {
    //此处用单指针扫描来划分(可以替换划分方法)
        int fenjiedian = singleScan_partition(arr,left,right);//从分界点开始分成两段子问题
        quickSort(arr,left,fenjiedian-1);//递归解决子问题1
        quickSort(arr,fenjiedian+1,right);//递归解决子问题2
    }
}

快排的三种划分方法:

1.单指针扫描划分:
在这里插入图片描述
思路:p1作为扫描指针,找到比pivot大的元素则停下与p2元素交换,p2后移。

代码:

//单指针扫描
public static int singleScan_partition(int[] arr, int left, int right) {
	//确定参考值
    int pivot = arr[left];
    //建立p1指针
    int p1 = left+1;
    //建立p2指针
    int p2 = right;
    
    while(p1 <= p2) {
    //p1指的元素小则前移,否则就与p2交换,p2后移
        if(arr[p1] <= pivot){
            p1++;
        } else {
            Util.swap(arr,p1,p2);
            p2--;
        }
    }//当p1指针和p2指针交叉的时候退出循环
	
	//画草图举例分析可以知道最终p2所在位置就是两个子问题的分界点
    Util.swap(arr,p2,left);
    return p2;
}

2.双指针扫描划分:
在这里插入图片描述
思路:p1找到比pivot大的元素停下,p2找到比pivot小的元素停下,然后交换p1,p2元素

代码:

//双指针扫描
public static int doubleScan_partition2(int[] arr, int left, int right) {
	//指定参考值
    int pivot = arr[left];
    //创建p1指针
    int p1 = left+1;
    //创建p2指针
    int p2 = right;
    //p1与p2交叉时退出循环
    while (p1 <= p2) {
        //注意:!!!!!!!!条件要并上 p1 <= p2
        while(p1 <= p2 && arr[p1] <= pivot) p1++;
        while(p1 <= p2 && arr[p2] > pivot) p2--;
        if(p1 <= p2) Util.swap(arr,p1,p2);
    }
    //p2所在下标即为分界点
    Util.swap(arr,p2,left);
    return p2;
}

3.三指扫描划分
在这里插入图片描述
思路:p1指针遇到大于pivot的元素停下,p2遇到小于pivot元素停下,此时交换p1,p2,p1遇到等于pivot的元素则equal跑过来,p1继续前移,遇到小于pivot的数则与equal交换。

代码:

//三指针扫描(待定复盘)
public static int threeScan_partition(int[] arr, int left, int right) {
    int pivot = arr[left];
    int p1 = left+1;
    int equal = p1;
    int p2 = right;
    while(p1 <= p2) {
        //遇到大的元素和等于的元素p1都可能停下
        while(p1 <= p2 && arr[p1] < pivot) p1++;
        //遇到小的元素p2停下
        while(p1 <= p2 && arr[p2] >= pivot) p2--;
        //遇到相等的元素,equal移动到p1位置,p1前移
        while(p1 <= p2 && arr[p1] == pivot) {
            equal = p1;
            p1++;
        }
        
        //如果p1因为遇到比equal更小的元素而停下
        if(p1 <= p2 && arr[p1] < arr[equal]) Util.swap(arr,p1,equal);
        //否则p1和p2同时停下
        else if(p1 <= p2) {
            Util.swap(arr,p1,p2);
        }
    }//循环结束后,p1与p2交叉
    Util.swap(arr,left,p2);
    return p2;
}

收获:

1.快排和核心算法在于分区,在同一个数组上通过指针位移比较,进行元素的交换
2.与归并排序不同的是,归并排序重在合并,其中要建立辅助空间拷贝原数组,再进行指针位移比较,然后把元素覆盖到原数组的位置
3.复盘的时候一边写注释梳理思路一边写代码。

发布了7 篇原创文章 · 获赞 0 · 访问量 1702

猜你喜欢

转载自blog.csdn.net/k909397116/article/details/105628826