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

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

这篇文章,你可以学习


  • 单路快速排序


  • 随机化


  • 双路快速排序


  • 三路快速排序


快速排序的过程


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


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


快速排序解读


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


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


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


相关文章
|
1月前
|
搜索推荐 C语言
【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)
本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。
57 4
|
26天前
|
存储 搜索推荐 Python
用 Python 实现快速排序算法。
快速排序的平均时间复杂度为$O(nlogn)$,空间复杂度为$O(logn)$。它在大多数情况下表现良好,但在某些特殊情况下可能会退化为最坏情况,时间复杂度为$O(n^2)$。你可以根据实际需求对代码进行调整和修改,或者尝试使用其他优化策略来提高快速排序的性能
117 61
|
2月前
|
算法 搜索推荐 Shell
数据结构与算法学习十二:希尔排序、快速排序(递归、好理解)、归并排序(递归、难理解)
这篇文章介绍了希尔排序、快速排序和归并排序三种排序算法的基本概念、实现思路、代码实现及其测试结果。
37 1
|
2月前
|
搜索推荐 Java Go
深入了解快速排序算法
深入了解快速排序算法
53 2
|
2月前
|
存储 搜索推荐 算法
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
|
2月前
|
算法 Python
Python算法编程:冒泡排序、选择排序、快速排序
Python算法编程:冒泡排序、选择排序、快速排序
34 0
|
2月前
|
搜索推荐 C语言 C++
【C语言】指针篇-精通库中的快速排序算法:巧妙掌握技巧(4/5)
【C语言】指针篇-精通库中的快速排序算法:巧妙掌握技巧(4/5)
|
4月前
|
搜索推荐 算法 Java
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
该博客文章通过UML类图和Java源码示例,展示了如何使用适配器模式将QuickSort类和BinarySearch类的排序和查找功能适配到DataOperation接口中,实现算法的解耦和复用。
55 1
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
|
4月前
|
算法 搜索推荐
算法设计 (分治法应用实验报告)基于分治法的合并排序、快速排序、最近对问题
这篇文章是关于分治法应用的实验报告,详细介绍了如何利用分治法实现合并排序和快速排序算法,并探讨了使用分治法解决二维平面上的最近对问题的方法,包括伪代码、源代码实现及时间效率分析,并附有运行结果和小结。
|
5月前
|
算法 搜索推荐 编译器
算法高手养成记:Python快速排序的深度优化与实战案例分析
【7月更文挑战第11天】快速排序是编程基础,以O(n log n)时间复杂度和原址排序著称。其核心是“分而治之”,通过选择基准元素分割数组并递归排序两部分。优化包括:选择中位数作基准、尾递归优化、小数组用简单排序。以下是一个考虑优化的Python实现片段,展示了随机基准选择。通过实践和优化,能提升算法技能。**
66 3