【排序算法】—— 快速排序

        快速排序的原理是交换排序,其中qsort函数用的排序原理就是快速排序,它是一种效率较高的不稳定函数,时间复杂度为O(N*longN),接下来就来学习一下快速排序。

一、快速排序思路

1.整体思路

以升序排序为例:

        (1)、首先随便找一个数(一般取首元素)作为排序对象(记为key),先将这个数排到准确的位置,即把小于等于key的全部放在key左边,大于key的全部放在key右边。这是排一趟需要做的事。

        (2)、排完一趟后被排的那个数key此时它在的位置是准确的,接下来只需要用同样的方法分别对它的左数组和右数组排序,这就有点类似于二叉树的前序遍历

如下:

void QuickSort(int* arr, int left, int right)
{if (left >= right)//如果左区间>=右区间就不用再排,直接返回。return;int key=_Sort3(arr, left, right);//该函数用来排单趟QuickSort(arr, left, key - 1);QuickSort(arr, key + 1, right);
}

        至于排单趟的算法一般有三种分别是:霍尔法,挖坑法,前后指针法。在下面会细讲。 

2.单趟排序

2.1.霍尔法

        把第一个元素当排序对象key(首元素的下标),用L储存左下标,R储存右下标。先让R从右往左走找到比key小的元素时停下,然后让L从左往右走找到比key大的元素停下。然后把位于L位置和位于R位置的的值进行交换,最后重复上面的操作直到L>=R为止。再把key的位置与L位置的值交换。至此一趟排序就结束了。

代码示例:

int _Sort1(int* arr, int begin, int end)//霍尔法
{int key = begin;while (begin < end){while (begin < end && arr[end] > arr[key])end--;while (begin < end && arr[begin] <= arr[key])begin++;Swap(&arr[begin], &arr[end]);}Swap(&arr[key], &arr[begin]);key = begin;return key;
}

2.1.挖坑法

          把首元素当做排序对象赋值给key,然后把第一个下标L当做坑位,让R从右边出发找到比key小的数放在坑位,然后R的位置变成坑位,让L从左往右找到比key大的数放在坑位,L位置变成坑位,再次让R走,循环往复直到L与R相遇。最后把key放在坑位,至此一趟排序结束。

 代码示例:

int _Sort2(int* arr, int begin, int end)
{int key = arr[begin];int kw = begin;//坑位下标while (begin < end){while (begin < end && arr[begin] < key)begin++;arr[kw] = arr[begin];kw = begin;while (begin < end && arr[end] >= key)end--;arr[kw] = arr[end];kw = end;}arr[kw] = key;return kw;
}

2.3.前后指针法

         把第一个元素当排序对象key(首元素的下标),prev指向首元素下标,cur指向首元素下一个元素下标。

        如果cur指向的元素小于key的话prev向右走,并且如果prev不等于cur,那么把prev与cur互换。最后cur都需要无条件向右走一步。

代码示例:

int _Sort3(int* arr, int begin, int end)//前后指针法
{int perv = begin, cur = begin + 1;while (cur <= end){if (arr[cur] < arr[begin] && ++perv != cur)Swap(&arr[perv], &arr[cur]);cur++;}Swap(&arr[perv], &arr[begin]);return perv;
}

二、提供效率的方法

1.三数取中(key值的选取)

        快速排序的"三数取中"(Median of Three)是一种优化技术,它相当于选排序对象key。其具体做法如下:

        (1).选择三个元素:从待排序数组中选择三个关键元素,通常是第一个元素、中间元素和最后一个元素。
        (2).比较并选择中位数:将这三个元素进行比较,选择值不是最大也不是最小的数。比如,如果三个元素分别是arr[left]、arr[mid]、arr[right],则中位数是介于这三个值之间的那个数。
        (3).交换为第一个元素:将选定的中位数元素与数组的第一个元素交换位置,以便在后续的快速排序中使用。

        通过使用"三数取中"的方法,可以有效地减少快速排序中最坏情况的发生概率,因为选取的主元更有可能接近数组的中值,而不是极端值。这样可以更均匀地划分数组,减少排序的平均时间复杂度,提高算法的整体性能。

2.小区间优化

        小区间优化指的是在快速排序算法中针对较小规模的子数组(或子问题)采用其他排序方法,而不是继续使用快速排序本身。这种优化技术的目的是避免在小规模问题上快速排序的递归调用带来的额外开销,提高算法的整体效率。
        具体来说,当快速排序的递归过程划分的子数组规模减小到一定程度时,可以选择使用插入排序或其他适合小规模数组的排序方法来处理。这样可以避免递归深度过深,减少递归调用和分区操作的开销,从而提高整体排序算法的性能。
具体的优化策略包括:

        (1).设定阈值:在实现快速排序时设定一个阈值,当子数组的大小小于这个阈值时,转而使用插入排序或者直接选择排序等方法进行排序。常见的阈值一般设定在5到10之间,具体取决于具体实现和排序数据的特性。
        (2).切换到其他排序算法:在快速排序的递归过程中,当子数组大小小于设定的阈值时,停止快速排序的递归,转而使用其他排序算法完成剩余的排序工作。

        小区间优化技术能有效降低快速排序在小规模数据上的不必要开销,提高整体排序算法的效率和性能。

三、非递归实现

        把递归该为非递归毋庸置疑首先考虑使用栈结构,这里我们需要抓住重点,考虑需要用栈结构储存什么数据,先从函数参数上看左右区间的参数是一直在变化的,那么只需要把区间储存到栈中,每次从栈中就可以取到需要排序的区间,对这个区间进行一趟排序后再分为两个小区间存入栈中,重复以上操作直到栈为空。

四、源码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include"Stack.h"
#define size 100
void Swap(int* a, int* b)
{int c = *a;*a = *b;*b = c;
}
void InsertSort(int* arr, int sz)//插入排序(小区间优化)
{for (int i = 0; i < sz - 1; i++){int j = i;int tmp = arr[j + 1];while (j >= 0 && tmp < arr[j]){arr[j + 1] = arr[j--];}arr[j + 1] = tmp;}
}
int Sk(int* arr, int left, int right)//三数取中
{int v = (left + right) / 2;if (arr[left] > arr[right]){if (arr[right] > arr[v])return right;else{if (arr[left] < arr[v])return left;elsereturn v;}}else{if (arr[left] > arr[v])return left;else{if (arr[right] > arr[v])return right;elsereturn v;}}
}
int _Sort1(int* arr, int begin, int end)//霍尔法
{int key = begin;while (begin < end){while (begin < end && arr[end] > arr[key])end--;while (begin < end && arr[begin] <= arr[key])begin++;Swap(&arr[begin], &arr[end]);}Swap(&arr[key], &arr[begin]);key = begin;return key;
}
int _Sort2(int* arr, int begin, int end)//挖坑法
{int val = arr[begin];int kw = begin;//坑位while (begin < end){while (begin < end && arr[begin] < val)begin++;arr[kw] = arr[begin];kw = begin;while (begin < end && arr[end] >= val)end--;arr[kw] = arr[end];kw = end;}arr[kw] = val;return kw;
}
int _Sort3(int* arr, int begin, int end)//前后指针法
{int perv = begin, cur = begin + 1;while (cur <= end){if (arr[cur] < arr[begin] && ++perv != cur)Swap(&arr[perv], &arr[cur]);cur++;}Swap(&arr[perv], &arr[begin]);return perv;
}
void QuickSort(int* arr, int left, int right)//快速排序(递归实现)
{if (left >= right)return;if (right - left <= 10){InsertSort(arr + left, right - left + 1);return;}int key=_Sort1(arr, left, right);QuickSort(arr, left, key - 1);QuickSort(arr, key + 1, right);
}
void QuickSortNoR(int* arr, int left, int right)//快速排序(非递归)
{Stack st;StackInit(&st);//关于栈结构的函数实现在这里并未展示,已在Stack.h中声明StackPush(&st, right);StackPush(&st, left);while (!StackEmpty(&st)){int begin = StackTop(&st);StackPop(&st);int perv = begin, cur = begin + 1;int end = StackTop(&st);StackPop(&st);if (begin >= end)continue;while (cur <= end){if (arr[cur] < arr[begin] && ++perv != cur)Swap(&arr[cur], &arr[perv]);cur++;}Swap(&arr[begin], &arr[perv]);StackPush(&st, end), StackPush(&st, perv + 1);StackPush(&st, perv - 1), StackPush(&st, begin);}StackDestroy(&st);
}
int main()
{int arr[size] = { 0 };srand((unsigned int)time(NULL));//随机数种子for (int i = 0; i < size; i++){arr[i] = rand() % 1000 + 1;//生成随机数赋值给arr[i]}QuickSort(arr, 0, size - 1);for (int i = 0; i < size; i++){printf("%-3d ", arr[i]);if ((i + 1) % 20 == 0)printf("\n");}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CC工具箱使用指南:【相交占比分析】

一、简介 需求场景如下&#xff0c;有【待分析地块】和【面积占比参考】2个图层。2个图层之间存在空间上的重叠。工具的目的是为了分析出【待分析地块】的每1个图斑中&#xff0c;和【面积占比参考】相交的面积&#xff0c;以及和总面积的占比。 举一个应用场景为例&#xff0…

Idea新增Module报错:sdk ‘1.8‘ type ‘JavaSDK‘ is not registered in ProjectJdkTable

文章目录 一&#xff0c;创建Module报错二&#xff0c;原因分析三&#xff0c;解决方案1&#xff0c;点击上图的加号&#xff0c;把JDK8添加进来即可2&#xff0c;点击左侧[Project]&#xff0c;直接设置SDK为JDK8 四&#xff0c;配置检查与验证 一&#xff0c;创建Module报错 …

【Linux】:程序地址空间

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关Linux程序地址空间的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从…

PingCAP 成为全球数据库管理系统市场增速最快的厂商

近日&#xff0c;Gartner 发布的《Market Share Analysis: Database Management Systems, Worldwide, 2023》&#xff08;2024 年 6 月&#xff09;报告显示&#xff1a;“2023 年全球数据库管理系统&#xff08;DBMS&#xff09;市场的增长率为 13.4%&#xff0c;略低于去年的…

LLM4Decompile——专门用于反编译的大规模语言模型

概述 论文地址&#xff1a;https://arxiv.org/abs/2403.05286 反编译是一种将已编译的机器语言或字节码转换回原始高级编程语言的技术。该技术用于分析软件的内部工作原理&#xff0c;尤其是在没有源代码的情况下&#xff1b;Ghidra 和 IDA Pro 等专用工具已经开发出来&#…

学习笔记——动态路由——OSPF聚合(汇总)

十一、OSPF聚合(汇总) 1、路由聚合(汇总) 路由汇总是一种重要的思想&#xff0c;在大型的项目中是必须考虑的一个重点事项。随着网络的规模越来越大&#xff0c;网络中的设备所需维护的路由表项也就会越来越多&#xff0c;路由表的规模也就会逐渐变大&#xff0c;而路由表是需…

【TB作品】51单片机 Proteus仿真 超声波LCD1602ADC0832 身高体重测量仪

00024 超声波LCD1602ADC0832 实验报告&#xff1a;基于51单片机的身高体重测量仪设计 背景介绍 本实验设计并实现了一个基于51单片机的身高体重测量仪。该系统利用超声波传感器测量高度&#xff0c;通过ADC0832模数转换芯片获取重量数据&#xff0c;并使用LCD1602显示屏显示…

系统测试-测试方法学习

目录 &#xff08;1&#xff09;等价类 &#xff08;2&#xff09;边界值 &#xff08;3&#xff09;正交&#xff1a;&#xff08;只用于确定排列组合&#xff0c;不确定具体内容&#xff09; (4)判定表法 &#xff08;5&#xff09;流程分析法 &#xff08;6&#xff0…

Day05-04-持续集成总结

Day05-04-持续集成总结 1. 持续集成2. 代码上线目标项目 1. 持续集成 git 基本使用, 拉取代码,上传代码,分支操作,tag标签 gitlab 用户 用户组 项目 , 备份,https,优化. jenkins 工具平台,运维核心, 自由风格工程,maven风格项目,流水线项目, 流水线(pipeline) mavenpom.xmlta…

uni-app使用ucharts地图,自定义Tooltip鼠标悬浮显示内容并且根据@getIndex点击事件获取点击的地区下标和地区名

项目场景&#xff1a; uni-app使用ucharts地图,自定义Tooltip鼠标悬浮显示内容并且根据getIndex点击事件获取点击的地区下标和地区名 例如&#xff1a; 问题描述 官方给的文档有限&#xff0c;需要自己下载地图json数据然后自己渲染和编写鼠标悬浮显示内容以及获取点击地址…

在DevEco运行typeScript代码,全网详细解决执行Set-ExecutionPolicy RemoteSigned报出的错

目录 基本思路 网络推荐 本人实践 如下操作,报错: 基本思路 //在DevEco运行typeScript代码 /** * 1.保证node -v出现版本,若没有,配置环境变量(此电脑-属性-高级系统变量配置-path-粘贴路径);DevEco在local.properties中可看到当前nodejs的路径 * 2.npm install …

【Transformer】transformer模型结构学习笔记

文章目录 1. transformer架构2. transformer子层解析3. transformer注意力机制4. transformer部分释疑 图1 transformer模型架构 图2 transformer主要模块简介 图3 encoder-decoder示意图N6 图4 encoder-decoder子层示意图 1. transformer架构 encoder-decoder框架是一种处理NL…

strcpy,srtcmp,strlen函数漏洞利用

strcpy,srtcmp,strlen函数漏洞利用 strcpy strcpy函数用于将字符串复制到另一个指针指向的空间中&#xff0c;遇到空字符 **b’x\00’**时停止&#xff0c;&#xff1a; 所以可以利用 strcpy不检查缓冲区 的漏洞&#xff08;构造的字符串要以\0结尾&#xff09;&#xff0c;…

Android平台崩溃和 ANR 问题进行符号化解析、解析崩溃日志的内存地址

使用Android Logcat Stacktrace Utility | Android Logcat | 1.2.3 1.设置so库路径 2.打开Stacktrace Utility工具 3.在Original粘贴报错内存地址 4.点击Resolve Stacktraces,就会解析出内存地址 如果是红色,解析失败了,缺少原生so库,可以在第一步添加so库文件再次尝试…

freemarker生成pdf,同时pdf插入页脚,以及数据量大时批量处理

最近公司有个需求&#xff0c;就是想根据一个模板生成一个pdf文档&#xff0c;当即我就想到了freemarker这个远古老东西&#xff0c;毕竟freemarker在模板渲染方面还是非常有优势的。 准备依赖&#xff1a; <dependency><groupId>org.springframework.boot</gr…

【植物大战僵尸杂交版】获取+存档插件

文章目录 一、还记得《植物大战僵尸》吗&#xff1f;二、在哪下载&#xff0c;怎么安装&#xff1f;三、杂交版如何进行存档功能概述 一、还记得《植物大战僵尸》吗&#xff1f; 最近&#xff0c;一款曾经在15年前风靡一时的经典游戏《植物大战僵尸》似乎迎来了它的"文艺复…

EN-SLAM:Implicit Event-RGBD Neural SLAM解读

论文路径&#xff1a;https://arxiv.org/pdf/2311.11013.pdf 目录 1 论文背景 2 论文概述 2.1 神经辐射场&#xff08;NeRF&#xff09; 2.2 事件相机&#xff08;Event Camera&#xff09; 2.3 事件时间聚合优化策略&#xff08;ETA&#xff09; 2.4 可微分的CRF渲染技术…

CosyVoice多语言、音色和情感控制模型,one-shot零样本语音克隆模型本地部署(Win/Mac),通义实验室开源

近日&#xff0c;阿里通义实验室开源了CosyVoice语音模型&#xff0c;它支持自然语音生成&#xff0c;支持多语言、音色和情感控制&#xff0c;在多语言语音生成、零样本语音生成、跨语言声音合成和指令执行能力方面表现卓越。 CosyVoice采用了总共超15万小时的数据训练&#…

学习笔记——动态路由——OSPF(邻接/邻居)

十、OSPF的邻接/邻居 1、OSPF路由器之间的关系 (1)基本介绍 在OSPF网络中&#xff0c;为了交换链路状态信息和路由信息&#xff0c;邻居设备之间首先要建立邻接关系&#xff0c;邻居(Neighbors)关系和邻接(Adjacencies)关系是两个不同的概念。 OSPF路由器的两种关系&#x…

创建线程的五种方式

一.继承Thread ,重写run class MyThread extends Thread{Overridepublic void run() {//这里的内容就是该线程要完成的工作while(true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeExceptio…