【高效且应用广泛的排序 —— 快速排序算法】

news2024/11/15 20:36:09

高效且应用广泛的排序 —— 快速排序算法

快速排序是一种常用的排序算法,主要采用分治的思想。以下是对快速排序算法的详细介绍及代码示例:

快速排序的基本思路是,每次将一个位置上的数据归位,使得该数左边的所有数据都比该数小,右边所有的数据都比该数大,然后递归将已归位的数据左右两边再次进行快排,从而实现所有数据的归位。

算法步骤如下:

  1. 从数列中挑出一个元素,称为“基准”。
  2. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区操作。
  3. 递归地把小于基准值元素的子数列和大于基准值元素的子数列排序。

在这里插入图片描述

例如,对于数组,选择最左边的元素 29 作为中间点元素,然后将数组分成三部分:(0, 14, 15, 20, 25),(29),(44, 37)。中间节点 29 已经排好序了,不需要处理。接下来再对左右分别进行快速排序。

下面是 Java 代码示例:

public class QuickSort {
    // 测试
    public static void main(String[] args) {
        int[] arr = {5,2,9,6,3,1,7,8,4};
        int left = 0;
        int right = arr.length - 1;
        quickSort(arr, left, right);
        System.out.println("排序结果:");
        for(int num : arr){
            System.out.print(num + " ");
        }
    }
    // 快速排序算法
    public static void quickSort(int[] arr,int left,int right) {
        if(left < right) {
            int partitionIndex = partition(arr, left, right);
            // 将数组划分为两部分,并返回划分点的索引
            quickSort(arr, left, partitionIndex - 1);
            // 递归排序左子数组
            quickSort(arr, partitionIndex + 1, right);
            // 递归排序右子数组
        }
    }
    // 划分函数
    public static int partition(int[] arr,int left,int right) {
        int pivot = arr[right];
        // 选择最后一个元素作为基准值
        int i = left - 1;
        // i 为较小元素的索引
        for(int j = left; j < right; j++){
            if(arr[j] <= pivot){
                i++;
                swap(arr, i, j);
                // 交换较小元素与当前元素
            }
            // 如果数组元素大于等于 middleValue,则继续向后遍历,middleIndex 值不变
        }
        swap(arr, i + 1, right);
        // 将基准值放置到正确的位置上
        return i + 1;
        // 返回划分点的索引
    }
    // 交换数组中两个元素的位置
    public static void swap(int[] arr,int i,int j) {
        int temp = arr[i];
        arr[i]= arr[j];
        arr[j]= temp;
    }
}

快速排序在平均情况下时间复杂度为 O(n log n),但在最坏情况下时间复杂度会变为 O(n²)。可以通过随机选择基准元素或使用三数取中法等方法来避免最坏情况。空间复杂度为 O(log n),因为在递归调用中需要使用栈来存储中间结果。快速排序虽然在最坏情况下时间复杂度可能较高,但在实际应用中通常表现良好,尤其对于大规模数据集的排序。如果实现得当,它是一种高效的排序算法。

快速排序算法基本思路

在这里插入图片描述

快速排序是一种高效的排序算法,其基本思路是分治法。首先从数列中取出一个数作为基准数,然后进行分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。接着再对左右区间重复这个步骤,直到各区间只有一个数。概括来说就是“挖坑填数+分治法”。

快速排序就像整理一堆杂乱的卡片。假设我们有一叠无序的数字卡片,我们随机选取一张卡片作为基准数,比如选择第一张卡片。然后我们从这叠卡片的右边开始,找到比基准数小的卡片,从左边开始找到比基准数大的卡片,找到后交换这两张卡片的位置。这样不断进行,直到左右指针相遇。此时,我们把基准数放到正确的位置上,这个位置左边的数字都小于等于基准数,右边的数字都大于基准数。然后我们再对左右两边的子序列重复这个过程,直到所有的子序列都只有一个数字,此时整个序列就有序了。

例如,有一组数字。我们选择4作为基准数,首先从右边开始,9大于4,继续向左,3小于4,此时停下。然后从左边开始,1小于4,继续向右,6大于4,停下。交换3和6的位置,得到。接着继续这个过程,指针不断移动,直到左右指针相遇。最后把4放到正确的位置上,此时序列变为。然后再对左边的子序列和右边的子序列分别进行快速排序,直到所有子序列都有序。

快速排序算法步骤

  1. 从序列中任选一个数作为基准数,一般就使用第一个数。
  2. 进行分区操作,将大于基准数的数放在右边,小于基准数的数放在左边,注意一定要先右后左。具体操作是设置两个指针i和j,分别指向数组的第一个元素和最后一个元素。从j开始向左挪动,直到找到一个小于基准数的数停下来;然后切换到i再向右挪动,直到找到一个数大于基准数的数停下来。最后将i和j指向的元素进行交换位置。重复这个过程直到i和j重合,此时把重合点的元素与基准元素交换位置。
  3. 对左右区间分别执行上述步骤,直到每个区间只有一个数为止。

例如对于数组,选择5作为基准数。首先j从6开始向左移动,找到3小于5停下。i从0开始向右移动,找到8大于5停下。交换3和8的位置,得到。接着j继续向左移动,找到1小于5停下。i继续向右移动,找到2小于5不停下,继续移动找到8大于5停下。交换1和8的位置,得到。此时i和j重合在1的位置,把1和5交换位置,得到。然后对左边的子序列和右边的子序列分别进行快速排序,直到所有子序列都有序。

快速排序代码示例解析

以下是一段快速排序的 Python 代码示例:

def quickSort(lists, left, right):
    # 快速排序
    if left >= right:
        return lists
    key = lists[left]
    low = left
    high = right
    while left < right:
        while left < right and lists[right] >= key:
            right -= 1
        lists[left] = lists[right]
        while left < right and lists[left] <= key:
            left += 1
        lists[right] = lists[left]
    lists[right] = key
    print("lists:", lists)
    quickSort(lists, low, left - 1)
    quickSort(lists, left + 1, high)
    return lists

这段代码首先判断左右指针的位置,如果左指针大于等于右指针,说明该子序列已经有序,直接返回。然后选择左指针指向的元素作为基准数key。接着使用两个循环,从右向左找到小于基准数的元素,从左向右找到大于基准数的元素,然后交换这两个元素的位置。当左右指针相遇时,将基准数放到正确的位置上。最后递归地对左右子序列进行快速排序。

以列表为例,调用quickSort函数进行排序。首先选择3作为基准数,从右向左找到2小于3停下,从左向右找到6大于3停下,交换2和6的位置,得到。接着继续这个过程,直到左右指针相遇,把3放到正确的位置上。然后对左边的子序列和右边的子序列分别进行快速排序,直到整个列表有序。

快速排序时间复杂度分析

快速排序的时间复杂度与划分是否平衡密切相关。最优情况下,时间复杂度为 O(nlogn)。这是因为每次划分都能将序列均匀地分成两部分,此时快速排序的性能与归并排序一样。

例如,对于一个包含 n 个数的序列,递归的第一层,将 n 个数划分为 2 个子区间,每个子区间的数字个数约为 n/2;递归的第二层,将 n 个数划分为 4 个子区间,每个子区间的数字个数约为 n/4;以此类推,递归的第 logn 层,将 n 个数划分为 n 个子区间,每个子区间的数字个数为 1。在每一层的划分过程中,时间复杂度约为 O(n),而总共需要划分 logn 层,所以最优情况下的时间复杂度为 O(nlogn)。

然而,在最坏情况下,时间复杂度为 O(n^2)。当每次选择的基准元素是最大或最小元素时,快速排序的时间复杂度是 O(n^2)。例如,序列已经是有序的,每次选择第一个元素作为基准数,那么每次划分只能分出一个元素,导致递归的深度达到 n,而每一层的时间复杂度为 O(n),所以最坏情况下的时间复杂度为 O(n^2)。

平均情况下,快速排序的时间复杂度也是 O(nlogn)。在实际应用中,快速排序通常表现良好,尤其对于大规模数据集的排序。

快速排序避免最坏情况方法

为了避免快速排序的最坏情况,可以采用以下几种方法:

  1. 随机选取划分点:每次随机从待排序序列中选取一个元素作为划分点,从而降低最坏情况的概率。这样可以避免每次都选择到最大或最小元素作为基准数,使得划分更加平衡。
  2. 三数取中法:选取待排序序列的第一个、中间一个、最后一个元素中的中间值作为划分点,从而降低最坏情况的概率。例如对于序列,可以选择1、6、3中的中间值3作为基准数,这样可以在一定程度上避免选择到最大或最小元素。
  3. 在递归深度较浅时采用其他排序算法,比如插入排序或归并排序等,从而避免快速排序退化成 O(n^2) 的情况。当问题规模小于某一 k 值时,采用插入排序,提高算法效率。

总结

快速排序算法是一种高效的排序算法,其基本思路是分治法,通过不断地划分和递归,将序列分成越来越小的子序列进行排序,直到所有子序列都有序。在实际应用中,可以根据不同的情况选择合适的方法来避免最坏情况的发生,以提高算法的性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2159295.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小程序开发设计-小程序的宿主环境:组件⑦

上一篇文章导航&#xff1a; 小程序开发设计-小程序的宿主环境&#xff1a;宿主环境简介⑥-CSDN博客https://blog.csdn.net/qq_60872637/article/details/142425131?spm1001.2014.3001.5501 注&#xff1a;不同版本选项有所不同&#xff0c;并无大碍。 目录 上一篇文章导航…

阿里巴巴首页pc端1688店铺招牌店铺装修教程

1688运营1688批发首页1688装修模板1688店铺怎么装修模板自定义装修代码1688店铺装修模板旺铺装修阿里店铺首页怎么装修1688店铺装修教程视频全屏通栏代码1688店铺装修模板阿里巴巴店铺装修设计 阿里巴巴首页pc端1688店铺招牌店铺装修教程 工具&#xff1a;一秒美工

英特尔:昔日芯片霸主的命运抉择

英特尔&#xff1a;昔日芯片霸主的命运抉择 简介 英特尔的辉煌与困境 高通的崛起与收购意向 英特尔困境的原因 英特尔的未来走向 简介 据美国《华尔街日报》9月20日报道&#xff0c;芯片巨头高通公司近日就收购事宜与英特尔公司进行了接洽。但目前这只是一个意向&#xff…

centos7 docker部署nacos

1. 一行代码安装git yum -y install git 2. 下载最新版nacos源码&#xff1a; git clone https://github.com/nacos-group/nacos-docker.git 进入nacos-docker文件 cd nacos-docker 3.docker安装数据库Mysql8 按这个来就行&#xff0c;非常好 Docker安装mysql8-超详细、每…

828华为云征文|华为云Flexus云服务器X实例 基于CentOS系统镜像快速部署Laravel开源论坛

最近公司可热闹了&#xff01;大家都在为搭建博客论坛系统忙得不可开交&#xff0c;尤其是在选服务器这件事儿上&#xff0c;那叫一个纠结。 同事 A 说&#xff1a;“咱得选个厉害的服务器&#xff0c;不然这论坛以后卡得跟蜗牛爬似的可咋办&#xff1f;” 同事 B 回应道&#…

思维商业篇(5)—发展趋势分析

思维商业篇(5)—发展趋势分析 核心理论 巴菲特曾在《滚雪球》一书中提到他的投资之道其实非常简单&#xff0c;可以总结为两句话&#xff1a;找到足够长的雪道&#xff0c;找到足够湿的雪球。 而发展趋势的分析&#xff0c;正好可以借助巴菲特的这个滚雪球理论。 足够长的雪…

OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【用户态内存调测】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 基本概念 Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存…

Unity 设计模式 之 行为型模式 -【状态模式】【观察者模式】【备忘录模式】

Unity 设计模式 之 行为型模式 -【状态模式】【观察者模式】【备忘录模式】 目录 Unity 设计模式 之 行为型模式 -【状态模式】【观察者模式】【备忘录模式】 一、简单介绍 二、状态模式&#xff08;State Pattern&#xff09; 1、什么时候使用状态模式 2、使用状态模式的…

Android RecyclerView 实现 GridView ,并实现点击效果及方向位置的显示

效果图 一、引入 implementation com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30 二、使用步骤 1.Adapter public class UnAdapter extends BaseQuickAdapter<UnBean.ResultBean, BaseViewHolder> {private int selectedPosition RecyclerView.NO_POSITIO…

苹果电脑系统重磅更新——macOS Sequoia 15 系统 新功能一 览

有了 macoS Sequoia&#xff0c;你的工作效率将再次提升&#xff1a;快速调整桌面布局&#xff0c;一目了然地浏览网页重点&#xff0c;还可以通过无线镜像功能操控你的iPhone。 下面就来看看几项出色新功能&#xff0c;还有能够全面发挥这些功能的 App 和游戏。 macOS Sequo…

探秘 Web Bluetooth API:连接蓝牙设备的新利器

引言 随着物联网技术的快速发展&#xff0c;蓝牙设备在日常生活中扮演着越来越重要的角色。而在 Web 开发领域&#xff0c;Web Bluetooth API 的出现为我们提供了一种全新的方式来连接和控制蓝牙设备。本文将深入探讨 Web Bluetooth API 的使用方法和原理&#xff0c;帮助开发…

ElasticSearch的使用、Kibana和ES-Head可视化工具

ElasticSearch的使用、Kibana和ES-Head可视化工具 一、ElasticSearch概述1. ES2.IK分词器3. Kibana4.Head 二、安装1.ES安装2. 配置跨域和IK分词器3.Kibana安装4. Head安装 三、常用操作1. ES结构2. ES操作1. 索引的基本操作-创建索引-查看索引-修改索引-删除索引-特殊查看 2.文…

git笔记之在多个分支中复用某个分支提交的更改

git笔记之在多个分支中复用某个分支提交的更改 code review! 文章目录 git笔记之在多个分支中复用某个分支提交的更改1.实现该功能的 Bash 脚本示例2.这个脚本是否可以处理新添加的文件&#xff1f;3.该脚本使用前&#xff0c;应先使用下述脚本重置本地仓库所有分支与远程保持一…

Jenkins Pipeline 中通过勾选参数来控制是否构建 Docker 镜像

1.定义参数&#xff1a; 使用 booleanParam 定义一个布尔参数&#xff0c;示例如下 booleanParam(name: BUILD_DOCKER, description: 是否构建Docker镜像, defaultValue: false)2.使用参数&#xff1a; 在 stage 中&#xff0c;根据参数的值决定构建方式&#xff1a; stage(编…

HTTP 与 HTTPS 的三次握手与四次挥手详解

文章目录 HTTP 与 HTTPS 的三次握手与四次挥手详解一、HTTP 的三次握手与四次挥手1. HTTP 三次握手2. HTTP 四次挥手 二、HTTPS 的三次握手与四次挥手1. HTTPS&#xff08;无证书&#xff09;的三次握手与 SSL/TLS 握手2. HTTPS&#xff08;有证书&#xff09;的三次握手与 SSL…

vue-baidu-map的基本使用

前言 公司项目需求引入百度地图&#xff0c;由于给的时间比较短&#xff0c;所以就用了已经封装好了的vue-baidu-map 一、vue-baidu-map是什么&#xff1f; vue-baidu-map是基于vue.js封装的百度地图组件(官方文档) 二、使用步骤 1.下载插件 //我下载的版本 npm install …

注册讲堂 | 医疗器械组合包类产品常见问题(2)

问题一&#xff1a;组合包类产品的有效期应该如何确定&#xff1f; 组合包类产品的有效期以组件中最短有效期为最终产品有效期。 对于外购件&#xff0c;需要考虑组件购买时的剩余效期及影响效期主要因素&#xff0c;如材料老化、灭菌有效期等。 问题二&#xff1a;组合包类产…

【计算机网络强化】计网强化笔记

第一章 计算机网络体系结构 1.1 计算机网络概述 1.计算机网络由若干个节点和连接这些节点的链路组成 2. 3.计算机网络的组成 ①硬件、软件、协议 ②边缘部分和核心部分 ③通信子网和资源子网 4.电路交换、报文交换和分组交换 ①电路交换 分为三步&#xff1a;建立连接、…

基于SpringBoot+Vue的在线问诊管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

SQL面试常见题目

SQL面试常见题目涉及多个方面&#xff0c;包括数据查询、数据操作、表的设计与优化等。以下列举一些经典的SQL面试题目&#xff0c;并附上解析答案&#xff1a; 1. 查询一张表中重复的数据 题目&#xff1a; 给定一个表 employees&#xff0c;包含 id, name, salary 列。如何…