高级排序算法(快速排序)

简介: 高级排序算法(快速排序)

这篇文章,你可以学习


  • 单路快速排序


  • 随机化


  • 双路快速排序


  • 三路快速排序


快速排序的过程


网络异常,图片无法展示
|


网络异常,图片无法展示
|


快速排序解读


网络异常,图片无法展示
|


网络异常,图片无法展示
|


partition的实现


方式一:创建两个额外的空间


网络异常,图片无法展示
|


网络异常,图片无法展示
|


网络异常,图片无法展示
|


方式二:原地partition


网络异常,图片无法展示
|


网络异常,图片无法展示
|


网络异常,图片无法展示
|


网络异常,图片无法展示
|


网络异常,图片无法展示
|


  • 假设[l, r]区间的第一个元素为判断元素。


  • 让其前面的元素都小于判断元素,后面的元素都大于判断元素。这就找到了判断元素应该放置的位置。


  • j = l


  • i = l + 1


  • 当当前i位置的元素小于判断元素,那么将让j++, 然后后交换i 和 j位置的元素。


  • 最后,当i越界后,即交换 j 和 l 位置的元素。


// 这个函数就是查找到arr[l]位置应该处于什么位置
    private static <E extends Comparable<E>> int partition(E[] arr, int l, int r) {
        int j = l;
        for(int i = l + 1; i <= r; i++) {
            if(arr[l].compareTo(arr[i]) > 0) { // i位置的元素小于当前元素
                j ++;
                // 交换j和i的位置
                swap(arr, j, i);
            }
        }
        // 当i遍历完后,交换j和l的位置。
        swap(arr, j, l);
        // 最后返回j的下标
        return j;
    }
    // 交换数组中两个元素
    private static <E> void swap(E[] arr, int j, int i) {
        E temp = arr[j];
        arr[j] = arr[i];
        arr[i] = temp;
    }


上面partition算法问题一,对于有序数组来说


他就是在partition的时候,元素分配不平衡。


网络异常,图片无法展示
|



网络异常,图片无法展示
|


如何解决上面的问题呢?


不能固定我们选取的标注点为最左边的元素,要随机选取。


网络异常,图片无法展示
|


为快速排序添加随机化


网络异常,图片无法展示
|


// 这个函数就是查找到arr[l]位置应该处于什么位置
    // 循环不变量:arr[l + 1...j] < v; arr[j + 1...i] >= v;
    private static <E extends Comparable<E>> int partition(E[] arr, int l, int r) {
        // j = l;
        // 随机化一个下标,防止每次都使用第一个元素作为基准。对于有序数组,那么他的时间复杂度就变为o(n2)
//        int randomIndex = (int) Math.floor(Math.random() * (r - l + 1)) + l;
      +  int randomIndex = (new Random()).nextInt(r - l + 1) +l;
      +  swap(arr, randomIndex, l);
        int j = l;
        for(int i = l + 1; i <= r; i++) {
            if(arr[l].compareTo(arr[i]) > 0) { // i位置的元素小于当前元素
                j ++;
                // 交换j和i的位置
                swap(arr, j, i);
            }
        }
        // 当i遍历完后,交换j和l的位置。
        swap(arr, j, l);
        // 最后返回j的下标
        return j;
    }


上面partition算法存在问题二,对于元素都相同的数组


我们对于和标注点相同的元素,无能为力。所以,我们需要重新设计partition算法,平均分配与标注点相同元素到标注点的两侧。


所以就有了双路快速排序算法


网络异常,图片无法展示
|


网络异常,图片无法展示
|


网络异常,图片无法展示
|


网络异常,图片无法展示
|


网络异常,图片无法展示
|


  • 通过随机下标,交换与区间第一个元素的位置,选取标注点。


  • i = l + 1;


  • j = r;


  • arr[i] >= arr[l],暂停。arr[j] < arr[l],暂停。然后交换i和j的位置。i++, j--。


  • 如果二者比较不成功,那么就进行i++ / j--;


  • i > j时,循环结束。交换j和l的位置元素。


private static <E extends Comparable<E>> int partition2(E[] arr, int l , int r) {
//        int randomIndex = (new Random()).nextInt(r - l + 1) + l;
        int randomIndex = (int) Math.floor(Math.random() * (r - l + 1)) + l;
        swap(arr, randomIndex, l);
        int i = l + 1;
        int j = r;
        while(true) {
            // i未超界,并且元素小于标注数
            while(i <= j && arr[i].compareTo(arr[l]) < 0) {
                i++;
            }
            // j未超界,并且元素大于标注数
            while(j >= i && arr[j].compareTo(arr[l]) > 0) {
                j--;
            }
            // 越界,跳出循环。
            if(i >= j) break;
            // 上面i, j下标都暂停,那么交换两个位置的元素
            swap(arr, i, j);
            i++;
            j--;
        }
        // 最后交换l和j的位置
        swap(arr, l, j);
        return j;
    }
    // 交换数组中两个元素
    private static <E> void swap(E[] arr, int j, int i) {
        E temp = arr[j];
        arr[j] = arr[i];
        arr[i] = temp;
    }


快速排序算法的复杂度分析


网络异常,图片无法展示
|


三路快速排序算法


  • 当i位置元素等于l位置元素


网络异常,图片无法展示
|


  • 当i位置元素小于l位置元素


网络异常,图片无法展示
|


  • 当i位置元素大于l位置元素


网络异常,图片无法展示
|


  • 循环结束的条件,i >= gt


  • 最后交换l 和 lt位置的元素。然后返回lt。


网络异常,图片无法展示
|


网络异常,图片无法展示
|


public static <E extends Comparable<E>> void sort3(E[] arr, int l, int r) {
        if(l >= r) {
            return;
        }
        int randomIndex = (new Random()).nextInt((r - l + 1)) + l;
        swap(arr, l, randomIndex);
        // 循环不变量 arr[l ... lt - 1] < v ; arr[lt ... gt - 1] == v ; arr[gt ... r] > v;
        // 开始的时候区间元素都为空
        int lt = l, i = l + 1, gt = r + 1;
        while(i < gt) { // 当i 和 gt重合时
            if(arr[l].compareTo(arr[i]) < 0) { // 交换gt和i的位置
                gt--;
                swap(arr, i, gt);
            }else if(arr[l].compareTo(arr[i]) > 0) { // 交换lt和i的位置
                lt++;
                swap(arr, lt, i);
                i++;
            }else { // 二者相等,i直接++
                i++;
            }
        }
        // 交换二者位置。 l, lt
        swap(arr, l, lt);
        sort3(arr, l, lt - 1);
        sort3(arr, gt, r);
    }
    // 交换数组中两个元素
    private static <E> void swap(E[] arr, int j, int i) {
        E temp = arr[j];
        arr[j] = arr[i];
        arr[i] = temp;
    }


三路快速排序算法解决LeetCode 75 颜色分类


class Solution {
        public void sortColors(int[] nums) {
            // nums[0 ... zero] == 0; nums[zero +1, i] == 1; nums[two, n - 1] == 2;
            int zero = -1, i = 0, two = nums.length;
            while(i < two) {
                if(nums[i] == 0) {
                    zero ++;
                    swap(nums, zero, i);
                    i++;
                }else if(nums[i] == 2) {
                    two --;
                    swap(nums, i, two);
                }else { // nums[i] == 1
                    i++;
                }
            }
        }
        private void swap(int [] nums, int i, int j ) {
            int t = nums[i];
            nums[i] = nums[j];
            nums[j] = t;
        }
    }


找到数组中第k小的元素


网络异常,图片无法展示
|


LeetCode相关习题。


  • 215


  • 剑指offer 40


相关文章
|
25天前
|
搜索推荐 算法 Java
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
该博客文章通过UML类图和Java源码示例,展示了如何使用适配器模式将QuickSort类和BinarySearch类的排序和查找功能适配到DataOperation接口中,实现算法的解耦和复用。
16 1
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
|
4月前
|
算法 前端开发
前端算法之快速排序
前端算法之快速排序
33 0
|
23天前
|
算法 搜索推荐
算法设计 (分治法应用实验报告)基于分治法的合并排序、快速排序、最近对问题
这篇文章是关于分治法应用的实验报告,详细介绍了如何利用分治法实现合并排序和快速排序算法,并探讨了使用分治法解决二维平面上的最近对问题的方法,包括伪代码、源代码实现及时间效率分析,并附有运行结果和小结。
|
2月前
|
算法 搜索推荐 编译器
算法高手养成记:Python快速排序的深度优化与实战案例分析
【7月更文挑战第11天】快速排序是编程基础,以O(n log n)时间复杂度和原址排序著称。其核心是“分而治之”,通过选择基准元素分割数组并递归排序两部分。优化包括:选择中位数作基准、尾递归优化、小数组用简单排序。以下是一个考虑优化的Python实现片段,展示了随机基准选择。通过实践和优化,能提升算法技能。**
37 3
|
3月前
|
搜索推荐 算法 Java
Java中的快速排序、归并排序和堆排序是常见的排序算法。
【6月更文挑战第21天】Java中的快速排序、归并排序和堆排序是常见的排序算法。快速排序采用分治,以基准元素划分数组并递归排序;归并排序同样分治,先分割再合并有序子数组;堆排序通过构建堆来排序,保持堆性质并交换堆顶元素。每种算法各有优劣:快排平均高效,最坏O(n²);归并稳定O(n log n)但需额外空间;堆排序O(n log n)且原地排序,但不稳定。
34 3
|
3月前
|
算法 搜索推荐 JavaScript
算法学习:快速排序
算法学习:快速排序
29 1
|
3月前
|
算法
数据结构与算法-快速排序
数据结构与算法-快速排序
18 1
|
2月前
|
算法 搜索推荐 C#
|
4月前
|
存储 搜索推荐 算法
快速排序算法详解
快速排序算法详解
|
3月前
|
搜索推荐 C语言
【C/排序算法】:快速排序和归并排序的非递归实现
【C/排序算法】:快速排序和归并排序的非递归实现
20 0
下一篇
DDNS