数据结构与算法(Java篇)笔记--快速排序

简介: 数据结构与算法(Java篇)笔记--快速排序



前言

在我们的程序中,排序是非常常见的一种需求,提供一些数据元素,把这些数据元素按照一定的规则进行排序。比如查询一些订单,按照订单的日期进行排序;再比如查询一些商品,按照商品的价格进行排序等等。所以,接下来我们要学习一些常见的排序算法。


一、选择排序

  快速排序是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一 部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序 过程可以递归进行,以此达到整个数据变成有序序列。

需求:

排序前:{6, 1, 2, 7, 9, 3, 4, 5, 8}

排序后:{1, 2, 3, 4, 5, 6, 7, 8, 9}

二、排序原理

  Step1.首先设定一个分界值,通过该分界值将数组分成左右两部分;

  Step2.将大于或等于分界值的数据放到到数组右边,小于分界值的数据放到数组的左边。此时左边部分 中各元素都小于 或等于分界值,而右边部分中各元素都大于或等于分界值;

  Step3.然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两 部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。

  Step4.重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当 左侧和右侧两个部分的数据排完序后,整个数组的排序也就完成了。

切分原理

把一个数组切分成两个子数组的基本思想:

  Step1.找一个基准值,用两个指针分别指向数组的头部和尾部;

  Step2.先从尾部向头部开始搜索一个比基准值小的元素,搜索到即停止,并记录指针的位置;

  Step3.再从头部向尾部开始搜索一个比基准值大的元素,搜索到即停止,并记录指针的位置;

  Step4.交换当前左边指针位置和右边指针位置的元素;

  Step5.重复2,3,4步骤,直到左边指针的值大于右边指针的值停止。

API设计

类名 Quick
构造方法 Quick():创建Quick对象
成员方法 1.public static void sort(Comparable[] a):对数组内的元素进行排序
2.private static void sort(Comparable[] a, int lo, int hi):对数组a中从索引lo到索引hi之间的元素 进行排序
3.public static int partition(Comparable[] a,int lo,int hi):对数组a中,从索引 lo到索引 hi之间的元 素进行分组,并返回分组界限对应的索引
4.private static boolean less(Comparable v,Comparable w):判断v是否小于w
5.private static void exch(Comparable[] a,int i,int j):交换a数组中,索引i和索引j处的值

1.代码实现

//排序代码
public class Quick {
    /*
        对数组a中的元素进行排序
    */
    public static void sort(Integer[] a){
        int lo = 0;
        int hi = a.length - 1;
        sort(a, lo, hi);
    }
    /*
        对数组a中从lo到hi的元素进行排序
    */
    public static void sort(Integer[] a, int lo, int hi){
        // 做安全性校验
        if(hi <= lo){
            return;
        }
        //对a数组中,从lo到hi的元素进行切分
        int partition = partition(a, lo, hi);
        //对左边分组中的元素进行排序
        sort(a,lo,partition-1);
        //对右边分组中的元素进行排序
        sort(a,partition+1, hi);
    }
    //对数组a中,从索引lo到索引hi之间的元素进行分组,并返回分组界限对应的索引
    public static int partition(Integer[] a, int lo, int hi){
        Integer key = a[lo];//把最左边的元素当做基准值
        int left = lo;//定义一个左侧指针,初始指向最左边的元素
        int right = hi + 1;//定义一个右侧指针,初始指向最右侧的元素下一个位置
        //进行切分
        while(true){
            //先从右往左扫描,找到一个比基准值小的元素
            while(less(key, a[--right])){//循环停止,证明找到了一个比基准值小的元素
                if(right == lo){
                    break;//已经扫描到最左边了,无需继续扫描
                }
            }
            //再从左往右扫描,找一个比基准值大的元素
            while(less(a[++left], key)){//循环停止,证明找到了一个比基准值大的元素
                if(left == hi){
                    break;//已经扫描到了最右边了,无需继续扫描
                }
            }
            if(left >= right){
                //扫描完了所有元素,结束循环
                break;
            }else{
                //交换left和right索引处的元素
                exch(a,left,right);//right就是切分的界
            }
        }
        //交换最后rigth索引处和基准值所在的索引处的值
        exch(a, lo, right);
        return right; //right就是切分的界限
    }
    /*
    数组元素i和j交换位置
    */
    private static void exch(Integer[] a, int i, int j){
        Integer t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
    /*
    比较v元素是否小于w元素
    */
    private static boolean less(Integer v, Integer w){
        return v.compareTo(w) < 0;
    }
}

测试类

//测试代码
public class Test {
    public static void main(String[] args) throws Exception {
        Integer[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 8};
        Quick.sort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

2.运行结果

编写完成之后,点击运行就能知道选择排序的结果,如下图所示:


总结

快速排序和归并排序的区别

  快速排序是另外一种分治的排序算法,它将一个数组分成两个子数组,将两部分独立的排序。快速排序和归并排序 是互补的:归并排序将数组分成两个子数组分别排序,并将有序的子数组归并从而将整个数组排序,而快速排序的 方式则是当两个数组都有序时,整个数组自然就有序了。在归并排序中,一个数组被等分为两半,归并调用发生在 处理整个数组之前,在快速排序中,切分数组的位置取决于数组的内容,递归调用发生在处理整个数组之后。

时间复杂度

  如果我们把数组的切分看做是一个树,那么上图就是它的最优情况的图示,共切分了logn次,所以,最优情况下快 速排序的时间复杂度为O(nlogn);

  最坏情况:每一次切分选择的基准数字是当前序列中最大数或者最小数,这使得每次切分都会有一个子组,那么总 共就得切分n次,所以,最坏情况下,快速排序的时间复杂度为O(n^2);

  平均情况:每一次切分选择的基准数字不是最大值和最小值,也不是中值,这种情况我们也可以用数学归纳法证 明,快速排序的时间复杂度为O(nlogn),由于数学归纳法有很多数学相关的知识,容易使我们混乱,所以这里就不对 平均情况的时间复杂度做证明了。

感谢观看,如果有帮助到你,请给题解点个赞和收藏,让更多的人看到。🌹🌹🌹

也欢迎你,关注我。👍👍👍

你们的点赞和留言对我真的很重要!!! ✿✿✿

相关文章
|
5月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
8天前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
5月前
|
存储 缓存 监控
上网行为监控系统剖析:基于 Java LinkedHashMap 算法的时间序列追踪机制探究
数字化办公蓬勃发展的背景下,上网行为监控系统已成为企业维护信息安全、提升工作效能的关键手段。该系统需实时记录并深入分析员工的网络访问行为,如何高效存储和管理这些处于动态变化中的数据,便成为亟待解决的核心问题。Java 语言中的LinkedHashMap数据结构,凭借其独有的有序性特征以及可灵活配置的淘汰策略,为上网行为监控系统提供了一种兼顾性能与功能需求的数据管理方案。本文将对LinkedHashMap在上网行为监控系统中的应用原理、实现路径及其应用价值展开深入探究。
122 3
|
5月前
|
人工智能 算法 NoSQL
LRU算法的Java实现
LRU(Least Recently Used)算法用于淘汰最近最少使用的数据,常应用于内存管理策略中。在Redis中,通过`maxmemory-policy`配置实现不同淘汰策略,如`allkeys-lru`和`volatile-lru`等,采用采样方式近似LRU以优化性能。Java中可通过`LinkedHashMap`轻松实现LRUCache,利用其`accessOrder`特性和`removeEldestEntry`方法完成缓存淘汰逻辑,代码简洁高效。
236 0
|
4月前
|
存储 算法 安全
Java中的对称加密算法的原理与实现
本文详细解析了Java中三种常用对称加密算法(AES、DES、3DES)的实现原理及应用。对称加密使用相同密钥进行加解密,适合数据安全传输与存储。AES作为现代标准,支持128/192/256位密钥,安全性高;DES采用56位密钥,现已不够安全;3DES通过三重加密增强安全性,但性能较低。文章提供了各算法的具体Java代码示例,便于快速上手实现加密解密操作,帮助用户根据需求选择合适的加密方案保护数据安全。
375 58
|
3月前
|
存储 负载均衡 算法
我们来说一说 Java 的一致性 Hash 算法
我是小假 期待与你的下一次相遇 ~
130 1
|
3月前
|
存储 监控 算法
企业上网监控场景下布隆过滤器的 Java 算法构建及其性能优化研究
布隆过滤器是一种高效的数据结构,广泛应用于企业上网监控系统中,用于快速判断员工访问的网址是否为违规站点。相比传统哈希表,它具有更低的内存占用和更快的查询速度,支持实时拦截、动态更新和资源压缩,有效提升系统性能并降低成本。
115 0
|
6月前
|
算法 搜索推荐
快速排序-数据结构与算法
快速排序(Quick Sort)是一种基于分治法的高效排序算法。其核心思想是通过选择基准(pivot),将数组划分为左右两部分,使得左侧元素均小于基准,右侧元素均大于基准,然后递归地对左右两部分进行排序。时间复杂度平均为 O(n log n),最坏情况下为 O(n²)(如数组已有序)。空间复杂度为 O(1),属于原地排序,但稳定性不佳。 实现步骤包括编写 `partition` 核心逻辑、递归调用的 `quickSort` 和辅助函数 `swap`。优化方法有随机化基准和三数取中法,以减少最坏情况的发生。
421 13
|
6月前
|
存储 机器学习/深度学习 监控
如何监控员工的电脑——基于滑动时间窗口的Java事件聚合算法实现探析​
在企业管理场景中,如何监控员工的电脑操作行为是一个涉及效率与合规性的重要课题。传统方法依赖日志采集或屏幕截图,但数据量庞大且实时性不足。本文提出一种基于滑动时间窗口的事件聚合算法,通过Java语言实现高效、低资源占用的监控逻辑,为如何监控员工的电脑提供一种轻量化解决方案。
151 3
|
存储 算法 Java
【数据结构与算法】1、学习动态数组数据结构(基本模拟实现 Java 的 ArrayList 实现增删改查)
【数据结构与算法】1、学习动态数组数据结构(基本模拟实现 Java 的 ArrayList 实现增删改查)
251 0