数据结构——快速排序的介绍

快速排序

快速排序是霍尔(Hoare)于1962年提出的一种二叉树结构的交换排序方法。快速排序是一种常用的排序算法,其基本思想是通过选择一个元素作为"基准值",将待排序序列分割成两个子序列,其中一个子序列的元素都小于等于基准值,另一个子序列的所有素都大于基准值。然后对这两个子序列分别进行递归排序,最后将排好序的子序列合并起来,即可得到完整的有序序列。

思想

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。在这里插入图片描述

霍尔版本

霍尔版本单趟排序代码实现

定义两个下标left和right。假设keyi在最左边,那么右边先走。当右边找到比keyi小的就停下。然后左边开始走,当左边找到比keyi大的就停下。交换左右的值。然后直到左右指针碰面,那么将left和keyi的值交换。
在这里插入图片描述
在这里插入图片描述

void QuickSort(int* arr,int begin, int end)
{
   	int left = begin;
    int right = end;
    int keyi = left;
    while (left < right)
    {
        //右边先走
        while (left < right && arr[right] >= arr[keyi])
        {
            right--;
        }
        while (left < right && arr[left] <= arr[keyi])
        {
           left++;
        }
        Swap(&arr[left], &arr[right]);
    }
    Swap(&arr[keyi], &arr[left]);
}

霍尔版本排序代码实现

当单趟排序把第一个keyi的数据放到它应该蹲的位置时,此时数组就可以分成三个部分,分别是[begin,keyi-1],keyi,[keyi+1,end]。此时依据二叉树的递归分治的思路将子区间的数据再次选左边为keyi来进行单趟排序,直到子区间不存在。最后就能够将数据排序。
在这里插入图片描述

针对已有序数组的优化

针对已经有序数组的排序,上面的代码有一个致命的问题。即未优化的快排最坏的情况下时间复杂度为O(N^2)。当数据量大的时候,还会有因为递归而栈溢出的问题。下面介绍第一种优化的方式,即随机数选keyi。

随机数选keyi

通过取数组长度的随机数做keyi,可以在一定程度上避免了完全有序或者接近有序的情况下快速排序的时间复杂度问题。下面简单看一下处理的方式
在这里插入图片描述

三数取中

通过最左边、最右边和中间位置的值作比较,取中间值来做下标keyi。这样在一定程度上可以优化有序或局部有序情况下,快速排序效率下降的问题。

在这里插入图片描述
在这里插入图片描述

挖坑法

挖坑法单趟排序代码实现

由于霍尔大佬的方法比较晦涩难懂,所以就有了我们现在要介绍的挖坑法。挖坑法顾名思义就是根据快排思想下的优化出来的一个版本。先假设key在最左边的数,所以坑位在左边。那么从右边开始走,当碰到比key小的就把它放到坑中,并且将坑位改变到停下的位置。左边同理。
在这里插入图片描述

void QuickSort1(int* arr, int begin, int end)
{
    int left = begin;
    int right = end;
    随机选key
    通过随机数来选择key,优化了顺序和逆序的情况下的时间复杂度
    //int randi = left + (rand() % (right - left));
    //
    //Swap(&arr[randi], &arr[left]);

    //三数取中优化
    int ret = GetMidNum(arr, left, right);
    if (left != ret)
        Swap(&arr[ret], &arr[left]);

    //左边做key
    int key = arr[left];
    int hole = left;
    while (left < right)
    {
        //右边先走
        while (left < right && arr[right] >= key)
        {
            right--;
        }
        //右边比key小就把它填到坑里,并改变坑位
        arr[hole] = arr[right];
        hole = right;
        while (left < right && arr[left] <= key)
        {
            left++;
        }
        //左边比key大就把它填到坑里,并改变坑位
        arr[hole] = arr[left];
        hole = left;
    }
    //最后坑位就是key应该蹲的位置
    arr[hole] = key;
}

挖坑法代码实现

在这里插入图片描述

前后指针法

前后指针法单趟排序代码实现

实现思路如下:首先定义两个指针prev和cur。以最左边作key,cur找小,当cur指向的内容比key小,++prev,prev的值和cur值交换位置(位置重合可以不交换)。当cur找到的值比key大时,++cur。这种方法的大体情况分为两种,一、prev紧跟cur。二、prev和cur直接间隔这一段比key的值。那么此时比key大的值会逐步往后翻,比key小的值会逐步往前甩。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

前后指针法代码实现

在这里插入图片描述

小区间优化

我们知道快速排序其实就是运用了二叉树结构分治的思想来进行排序的。在二叉树这种结构中,当高度为h时,我们考虑最优情况下(满二叉树),最后一层的结点就是2^(h-1)个。占据整棵树的一半。倒数第二层的结点个数为2 ^ (h-2)个,占整棵树结点的25%。其实只要消灭了倒数三层的递归其实就可以在大体上减少递归带来的损耗,提高效率,并且可以让栈区空间的损耗降低。在一个局部有序的小区间内进行优化,我们可以选择的排序有很多种。但是,根据我们前面所学知识我们可以发现,其实使用堆排序和希尔排序相对于小区间优化的优势并没有很明显。前者需要建堆,后者需要根据gap来进行预排序。选择插入排序来小区间优化其实是最适合的。因为在局部有序的区间内,插入排序的效率是最高的。
在这里插入图片描述

在这里插入图片描述

快速排序非递归实现

利用栈来存需要递归的区间,然后根据栈后进先出的性质来模拟递归的顺序。把区间化成子区间进行继续排序。直到区间不存在或区间只有一个数就停止将区间的下标入栈。
在这里插入图片描述

在这里插入图片描述

快速排序的特性总结

一、快速排序是一个综合性能较好和使用场景比较广泛的排序。
二、快速排序的时间复杂度为O(NLogN)。
在这里插入图片描述
在最好情况下,快速排序的时间复杂度为O(nlogn)。当待排序的序列能够均匀地分成两个子序列时,每次分割操作都能将序列大致平分,此时递归调用层数为logn,每层需要的比较次数为n,因此总比较次数为O(N
LogN)。在最坏情况下,快速排序的时间复杂度为O(n ^ 2)。当待排序的序列已经有序或基本有序(例如,序列已经是升序排列,但选择的基准值总是最小或最大元素),每次分割操作只能切割出一个子序列,递归调用的层数为n,每层需要的比较次数为n,因此总比较次数为n ^ 2。然而,通过随机选择基准值或使用三数取中法等优化方法,可以减少最坏情况发生的可能性,从而提高了快速排序的平均性能。在实际应用中,快速排序通泛使用。
三、快速排序是一个不稳定的排序。

代码获取

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot集成WebSocket实现消息实时推送(提供Gitee源码)

前言&#xff1a;在最近的工作当中&#xff0c;客户反应需要实时接收消息提醒&#xff0c;这个功能虽然不大&#xff0c;但不过也用到了一些新的技术&#xff0c;于是我这边写一个关于我如何实现这个功能、编写、测试到部署服务器&#xff0c;归纳到这篇博客中进行总结。 目录 …

【计算机网络自顶向下】计算机网络期末自测题(一)

前言 “(学不懂一点) &#xff08;阴暗的爬行&#xff09;&#xff08;尖叫&#xff09;&#xff08;扭曲&#xff09;&#xff08;阴暗的爬行&#xff09;&#xff08;尖叫&#xff09;&#xff08;扭曲&#xff09;&#xff08;阴暗的爬行&#xff09;&#xff08;尖叫&#…

LeetCode·1262. 可被三整除的最大和·贪心

作者&#xff1a;小迅 链接&#xff1a;https://leetcode.cn/problems/greatest-sum-divisible-by-three/solutions/2314049/tan-xin-zhu-shi-chao-ji-xiang-xi-by-xun-r0n76/ 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 著作权归作者所有。商业转载请联系作者获得…

vscode 调试

目录 准备 GDB 调试方法 问题 准备 然后点击 文件-打开文件夹&#xff0c;找到创建的代码路径&#xff0c;确定后&#xff0c;在左侧的资源管理器可以看到代码文件。 第一次运行需要安装 c 的扩展&#xff0c;在扩展页面中&#xff0c;安装 C/C 编译注意一定要加上 -g 指令…

Linux tar.xz 格式的文件正确的解压命令

Linux tar.xz 最近下载 Linux kernel&#xff0c;好像最近流行 tar.xz 格式的后缀 对于 xz 后缀的压缩文件&#xff0c;我之前的解压方式是分为两步&#xff1a; xz -d xxx.tar.xz 解压成 xxx.tar 格式文件&#xff0c;然后再 tar xf xxx.tar 解压文件。 这样的操作不仅比较的…

跳槽过去,刚工作三天就被裁是一种怎样的体验

前言 还有谁&#xff1f;刚上三天班就被公司公司的工作不适合我&#xff0c;叫我先提升一下。 后面我也向公司那边讨要了一个说法&#xff0c;我只能说他们那边的说辞让我有些不服气。 现在之所以把这件事在csdn上记录一下&#xff0c;一是记录一下自己的成长轨迹&#xff0…

使用STM32F103的串口实现IAP程序升级功能

使用STM32F103的串口实现IAP程序升级功能 &#x1f3ac;IAP程序烧录全过程演示&#xff1a; ✨这几天折腾IAP升级功能&#xff0c;狂补了很多相关BootLoader相关的知识。本来最想实现IAP升级程序的方式是&#xff0c;基于SPI通讯的SD卡&#xff0c;借助挂载的FatFS文件系统&am…

【计网】第一章 计算机网络概述

文章目录 计算机网络概述一、计算机网络在信息时代中的作用二、互联网概述2.1 互连网概念2.2 网络的网络2.3 互连网基础结构发展的三个阶段2.4 互连网的标准化工作 三、互联网的组成3.1 互联网的边缘部分3.2 互联网的核心部分3.2.1 基础概念3.2.2 电路交换3.2.3 报文交换3.2.4 …

Baumer工业相机堡盟工业相机如何使用新版本NEOAPI SDK控制相机数据流的开启和关闭(C++)

Baumer工业相机堡盟工业相机如何使用新版本NEOAPI SDK控制相机数据流的开启和关闭&#xff08;C&#xff09; Baumer工业相机Baumer工业相机NEOAPI SDK的技术背景Baumer工业相机使用NEOAPISDK控制相机数据流的方式1.引用合适的类文件2.使用NEOAPISDK控制相机数据流的方式2.使用…

macOS Monterey 12.6.7 (21G651) 正式版发布,ISO、IPSW、PKG 下载

macOS Monterey 12.6.7 (21G651) 正式版发布&#xff0c;ISO、IPSW、PKG 下载 本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持…

【发布】ChatGLM2-6B:性能大幅提升,8-32k上下文,推理提速42%

自3月14日发布以来&#xff0c; ChatGLM-6B 深受广大开发者喜爱&#xff0c;截至 6 月24日&#xff0c;来自 Huggingface 上的下载量已经超过 300w。 为了更进一步促进大模型开源社区的发展&#xff0c;我们再次升级 ChatGLM-6B&#xff0c;发布 ChatGLM2-6B 。 在主要评估LLM模…

css绘制网格背景

文章目录 前言效果图说明 前言 本篇文章主要简单扼要的去实现css网格背景&#xff0c;并进一步探求其应用原理 效果图 css代码 body::before, body::after {position: fixed;top: 0;left: 0;right: 0;bottom: 0;content: ;background-repeat: repeat;pointer-events: none;o…

解密EEMD分析:Rlibeemd包带你玩转信号分解和时间序列预测

一、简介 1.1 什么是EEMD? EEMD&#xff08;Ensemble Empirical Mode Decomposition&#xff09;是一种信号分解方法&#xff0c;它旨在分解非线性、非平稳或非白噪声的信号&#xff0c;以揭示复杂信号的局部特征和周期性成分。EEMD不同于传统的余弦变换、小波变换等线性变换…

android存储3--初始化.unlock事件的处理

android版本&#xff1a;android-11.0.0_r21http://aospxref.com/android-11.0.0_r21 概述&#xff1a;SystemServiceManager收到unlock事件后&#xff0c;遍历service链表&#xff0c;执行各个service的onUserUnlocking。对于存储service&#xff0c;执行的是StorageManagerS…

【javascript】闭包

通过定时器从第一个元素开始往后&#xff0c;每隔一秒输出arr数组中的一个元素。 <script>var arr [one, two, three];for(var i 0; i < arr.length; i) {setTimeout(function () {console.log(arr[i]);}, i * 1000);} </script> 但是运行过后&#xff0c;我…

【LLMs 入门实战 】第二式:MiniGPT4 模型学习与实战

2023年4月17日&#xff0c;多模态问答模型MiniGPT-4发布&#xff0c;实现了GPT-4里的宣传效果《MiniGPT-4: Enhancing Vision-language Understanding with Advanced Large Language Models》《MiniGPT-4&#xff1a;使用高级大语言模型增强视觉语言理解》 模型介绍模型架构微调…

ECCV2022 多目标跟踪(MOT)汇总

一、《Towards Grand Unification of Object Tracking》 作者: Bin Yan1⋆, Yi Jiang2,†, Peize Sun3, Dong Wang1,†,Zehuan Yuan2, Ping Luo3, and Huchuan Lu School of Information and Communication Engineering, Dalian University of Technology, China 2 ByteDance …

5.6.2 传输层编址--端口

5.6.2 传输层编址 传输层为应用进程提供了端到端的逻辑通信&#xff0c;两个主机之间的通信实际上是两个主机中的应用进程之间的相互通信&#xff0c;因此一个主机中可能有多个应用进程同时和另一个主机中多个应用进程进行通信&#xff0c;而网络层我们学习的网际协议能够保证…

动态规划:积木画

积木画 问题描述 小明最近迷上了积木画, 有这么两种类型的积木, 分别为 I I I 型&#xff08;大小为 2 个单位面积) 和 L L L 型 (大小为 3 个单位面积): 同时, 小明有一块面积大小为 2 N 2 \times N 2N 的画布, 画布由 2 N 2 \times N 2N 个 1 1 1 \times 1 11 区域…

【强化学习】——Q-learning算法为例入门Pytorch强化学习

&#x1f935;‍♂️ 个人主页&#xff1a;Lingxw_w的个人主页 ✍&#x1f3fb;作者简介&#xff1a;计算机研究生在读&#xff0c;研究方向复杂网络和数据挖掘&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;CSDN专家博主、人工智能领域优质创作者&#xf…