【数据结构与算法】十大经典排序算法-快速排序

这篇具有很好参考价值的文章主要介绍了【数据结构与算法】十大经典排序算法-快速排序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🌟个人博客:www.hellocode.top
🏰Java知识导航:Java-Navigate
🔥CSDN:HelloCode.
🌞知乎:HelloCode
🌴掘金:HelloCode
⚡如有问题,欢迎指正,一起学习~~


快速排序(Quick Sort)是一种高效的排序算法,是对冒泡排序的优化。它采用分治法(Divide and Conquer)的思想,将待排序序列不断分割成较小的子序列,然后对每个子序列进行排序,最后合并得到有序的序列。快速排序在大多数情况下具有优异的性能,是许多常见排序算法中最快的之一。

基本思想

【数据结构与算法】十大经典排序算法-快速排序,数据结构与算法,排序算法,java,算法

这里的动画用大佬五分钟学算法的图,很清晰

  1. 选择一个基准元素(pivot)作为参考点。
  2. 将所有比基准元素小的元素移到基准元素的左边,将所有比基准元素大的元素移到基准元素的右边。此过程称为分区(partitioning)。
  3. 对基准元素左右两边的子序列递归地进行相同的快速排序,直到子序列的大小为1或0,表示子序列已经有序。

如上图所示,快速排序的基本思想就是选取一个基准数,将基准数小的都放在左边,大的都放在右边,也就是每次循环都会确定出基准数应该在的正确位置。

代码实现

对应在代码层面,需要使用到递归法来实现,对于快速排序来说,递归相对还是很容易想到的

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月08日 21:14
 */
public class QuickSort {
    public static void quickSort(int[] arr) {
        // 特殊情况处理
        if (arr == null || arr.length == 0 || arr.length == 1) {
            return;
        }
        // 递归入口
        sort(arr, 0, arr.length - 1);
    }

    private static void sort(int[] arr, int left, int right) {
        // 递归出口
        if (left > right) {
            return;
        }
        // 初始化双指针
        int i = left, j = right;
        // 选取基准数
        int base = arr[left];
        while (i != j) {
            // (注意!!!!)从右边开始
            // 找比基准数小的
            while (i < j && arr[j] >= base) {
                j--;
            }
            // 从左边找比基准数大的
            while (i < j && arr[i] <= base) {
                i++;
            }
            // 交换i j
            if (i < j) {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        // 基准数归位(此时i == j)
        arr[left] = arr[i];
        arr[i] = base;
        // 开始左右两边分别快排
        sort(arr, left, i - 1);
        sort(arr, i + 1, right);
    }
}

这里先移动j还是先移动i主要是需要看基准数选取的位置,如果选最左边的数,就需要先移动j(确保i == j 时对应的值是小于基准数的,再把基准数和该数交换,符合排序规则)
如果选取的基准数是最右边,则先移动i指针(确保 i == j 时对应的值是大于基准数的)

测试:

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月08日 21:14
 */
public class Test {
    public static void main(String[] args) {
        int[] arr = {21, 13, 4, 10, 7, 65, 32, 15, 32, 19, 33, 65, 89, 72, 12};
        System.out.println("排序前:" + Arrays.toString(arr));
        QuickSort.quickSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }
}

【数据结构与算法】十大经典排序算法-快速排序,数据结构与算法,排序算法,java,算法

优化

快排的优化主要需要关注基准数的选取,如果待排序数组整体偏降序,此时还选最左侧的为基准数的话,效率就相对慢一些,所以选取一个好的基准数可以让快排的效率更加稳定~

主要的方法有以下几种:

  1. 随机选择基准元素:选择随机的基准元素可以降低最坏情况的概率,进而提高算法性能。
  2. 三数取中法:在选取基准元素时,不是简单地选取第一个或最后一个元素,而是选择中间元素、第一个元素和最后一个元素中的中位数作为基准元素,也可以降低最坏情况的概率。

这里基准数的选取可以很巧妙,还有很多种其他方法,都可以降低最坏情况的发生,本文以三数取中法为例:

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月08日 21:14
 */
public class QuickSort {
    public static void quickSort(int[] arr) {
        // 特殊情况处理
        if (arr == null || arr.length == 0 || arr.length == 1) {
            return;
        }
        // 递归入口
        sort(arr, 0, arr.length - 1);
    }

    private static void sort(int[] arr, int left, int right) {
        // 递归出口
        if (left > right) {
            return;
        }
        // 初始化双指针
        int i = left, j = right;
        // 选取基准数
        getBase(arr, left, right);
        int base = arr[left];
        while (i != j) {
            // (注意!!!!)从右边开始
            // 找比基准数小的
            while (i < j && arr[j] >= base) {
                j--;
            }
            // 从左边找比基准数大的
            while (i < j && arr[i] <= base) {
                i++;
            }
            // 交换i j
            if (i < j) {
                swap(arr, i, j);
            }
        }
        // 基准数归位(此时i == j)
        arr[left] = arr[i];
        arr[i] = base;
        // 开始左右两边分别快排
        sort(arr, left, i - 1);
        sort(arr, i + 1, right);
    }

    // 取三点中间值(最终会移动到left位置,这样可以不改变原有代码)
    private static void getBase(int[] arr, int left, int right) {
        // 这里可以防止溢出且使用 >> 效率相对能高一些
        // 等价于 (left + right) / 2
        int mid = left + ((right - left) >> 1);
        // 确保第一个元素最小
        if (arr[left] > arr[right]) {
            swap(arr, left, right);
        }
        // 确保最后一个元素最大
        if (arr[mid] > arr[right]) {
            swap(arr, mid, right);
        }
        // 确定mid就是中间值,交换到最左边
        if (arr[left] < arr[mid]) {
            swap(arr, left, mid);
        }
        System.out.println("基准数为:" + arr[left]);
    }

    // 交换数组两下标元素位置
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

测试:

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月07日 21:02
 */
public class Test {
    public static void main(String[] args) {
        int[] arr = {21, 13, 4, 10, 7, 65, 32, 15, 32, 19};
        System.out.println("排序前:" + Arrays.toString(arr));
        BubbleSort.bubbleSortOptimized1(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }
}
排序前:[21, 13, 4, 10, 7, 65, 32, 15, 32, 19, 33, 65, 89, 72, 12]
基准数为:15
基准数为:7
基准数为:4
基准数为:12
基准数为:10
基准数为:13
基准数为:32
基准数为:21
基准数为:19
基准数为:32
基准数为:65
基准数为:33
基准数为:65
基准数为:72
基准数为:89
排序后:[4, 7, 10, 12, 13, 15, 19, 21, 32, 32, 33, 65, 65, 72, 89]

总结

优点

  1. 高效性:快速排序是一种高效的排序算法,在大多数实际情况下,它的性能通常比其他常见排序算法(如冒泡排序、插入排序)更好。
  2. 原地排序:快速排序是原地排序算法,不需要额外的辅助空间,只需在原始数组上进行交换操作。

缺点

  1. 最坏情况下的性能:当待排序序列已经基本有序或完全逆序时,快速排序的时间复杂度退化为 O(n^2),导致性能下降。这是因为基准元素的选择可能导致分区不平衡,使得分区操作不再能有效地减少问题规模。
  2. 不稳定性:快速排序是一种不稳定的排序算法,意味着相等元素的相对顺序在排序后可能发生变化。这在某些情况下可能导致问题,特别是对于复杂对象的排序,需要额外的处理来保持稳定性。

复杂度

  • 时间复杂度:快速排序的平均时间复杂度为O(n log n),最坏情况下为O(n^2)。
  • 空间复杂度:快速排序是原地排序算法,空间复杂度为O(log n)。

适用于处理大规模数据集的排序,尤其在平均情况下,快速排序的性能较优。但在处理已经有序或近乎有序的数据集时,快速排序的性能可能会下降,这时候其他稳定的排序算法可能更合适。文章来源地址https://www.toymoban.com/news/detail-642592.html

到了这里,关于【数据结构与算法】十大经典排序算法-快速排序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《数据结构与算法》之十大基础排序算法

    冒泡排序是一种交换排序,它的思路就是在待排序的数据中,两两比较相邻元素的大小,看是否满足大小顺序的要求,如果满足则不动,如果不满足则让它们互换。 然后继续与下一个相邻元素的比较,一直到一次遍历完成。一次遍历的过程就被成为一次冒泡,一次冒泡的结束

    2024年02月05日
    浏览(82)
  • 数据结构——排序算法之快速排序

        个人主页: 日刷百题 系列专栏 : 〖C/C++小游戏〗 〖Linux〗 〖数据结构〗   〖C语言〗 🌎 欢迎各位 → 点赞 👍+ 收藏 ⭐️+ 留言 📝  ​ ​ 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。 基本思想: 任取待排序元素序列中 的某元素作为基准值,按照

    2024年01月21日
    浏览(31)
  • 数据结构与算法:快速排序

    荷兰国旗问题 想要理解快速排序,就先理解这个问题: [LeetCode75.颜色分类] 荷兰国旗是由红白蓝三色组成的: 现在将其颜色打乱 然后根据一定的算法,将其复原为红白蓝三色,这就叫做荷兰国旗问题。 在LeetCode的题目中,其将荷兰国旗的三个颜色用0,1,2来表达,也就是说

    2024年01月15日
    浏览(34)
  • 【数据结构与算法】:选择排序与快速排序

    🔥 个人主页 : Quitecoder 🔥 专栏 :数据结构与算法 我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:腾讯云 欢迎来到排序的第二个部分:选择排序与快速排序! 选择排序是一种简单直观的比较排序算法。该算法的基本思想是在每一轮中选出当前未排序部分的最

    2024年03月17日
    浏览(36)
  • 数据结构与算法之快速排序

    快速排序 (Quick Sort),又称划分交换排序(partition-exchange sort),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数

    2024年02月10日
    浏览(32)
  • 【数据结构】排序算法(二)—>冒泡排序、快速排序、归并排序、计数排序

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.冒泡排序 2.快速排序 2.1Hoare版 2.2占坑版 2.3前后指针版 2.4三数取中对快速排序的优化 2.5非递归版 3.归

    2024年02月08日
    浏览(31)
  • 【数据结构与算法】:非递归实现快速排序、归并排序

    🔥 个人主页 : Quitecoder 🔥 专栏 :数据结构与算法 上篇文章我们详细讲解了递归版本的快速排序,本篇我们来探究非递归实现快速排序和归并排序 快速排序的非递归实现主要依赖于栈(stack)来模拟递归过程中的函数调用栈。递归版本的快速排序通过递归调用自身来处理子

    2024年03月24日
    浏览(40)
  • 【数据结构与算法】如何对快速排序进行细节优化以及实现非递归版本的快速排序?

    君兮_的个人主页 即使走的再远,也勿忘启程时的初心 C/C++ 游戏开发 Hello,米娜桑们,这里是君兮_,国庆长假结束了,无论是工作还是学习都该回到正轨上来了,从今天开始恢复正常的更新频率,今天为大家带来的内容是快速排序的两大优化和非递归实现 好了废话不多说,开

    2024年02月08日
    浏览(34)
  • 【数据结构与算法】快速排序的非递归实现方法

      目录 一.前言 二.非递归实现 如果数据量过大的话,不断递归就会出现 栈溢出 的现象,这个时候你的代码是没问题的,但就是跑不起来,这个时候就要 把递归改成非递归 。 一般有两种改法: 1.直接改,利用循环等; 2.借助栈的辅助。 而快速排序的非递归实现方法就需要

    2023年04月17日
    浏览(39)
  • 【数据结构与算法】快速排序的三种实现方法

      目录 一.基本思想 二.Hoare法 动态演示 三.挖坑法 动态演示 四.前后指针法 动态演示 五.快速排序优化 随机下标交换法 三路取中法 六.快速排序的特性 任取待排序元素序列中的某元素作为 基准值 ,按照该排序码将待排序集合 分割成两子序列 , 左子序列中所有元素均小于基

    2023年04月09日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包