完整教程:【数据结构】快速排序与归并排序的实现

news/2025/10/3 18:46:36/文章来源:https://www.cnblogs.com/tlnshuju/p/19124862

完整教程:【数据结构】快速排序与归并排序的实现

2025-10-03 18:42  tlnshuju  阅读(0)  评论(0)    收藏  举报

1. 快速排序

1.1 算法思想

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

1.2 算法实现

快速排序的实现方式有很多,我们主要介绍三种方法。

1.2.1 Hoare 法

1. 利用两个变量left,right分别指向数组的起始位置与末尾位置。并且以数组第一个元素作为key值。
2. right先从右往左依次遍历找到比key小的数,left从左往右依次遍历找到比key大的数。然后交换left与right下标对应的值。重复步骤2直至right>=left。
3. 之后交换key与left或者right对应的值,并且把该位置记为mid。
4. 最后划分区间[left,mid-1]与[mid+1,right]继续重复1,2步骤。直至不能划分。

思考:为什么最后相遇位置一定小于或等于 key 值?

我们知道rightleft相遇无非两种情况:

情况一:right停住,left移动与right相遇·。因为right一直再找比key小的值,所以right停下位置一定比key小,相遇位置也一定比key小。
情况二:left停住,right移动与left相遇·。此时又分为两种情况:
left从未移动,右侧数据都比key大,相遇位置就是key,交换不变。
left移动过至少一次,也就是至少交换过一次,此时left停留位置的值是上一轮right所对应的值,又因为right一直在找比key小的值,所以相遇位置也一定比key小。

代码实现:

void swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
int PartSort1(int* arr, int begin, int end)
{int left = begin, right = end;int keyi = begin;while (left < right){//left=而不是>防止数据出现死循环while (left=arr[keyi])//寻找比key小的值{right--;}while (left < right && arr[left] <= arr[keyi])//寻找比key大的值{left++;}swap(&arr[left], &arr[right]);}int mid = left;swap(&arr[keyi], &arr[mid]);return mid;
}
void QuickSort(int* arr, int left, int right)
{if (left >= right)//不能划分{return;}int mid = PartSort1(arr, left, right);QuickSort(arr, left, mid - 1);//左区间QuickSort(arr, mid+1, right);//右区间
}

1.2.2 挖坑法

1. 先将起始位置key值设为坑,之后right从右往左找比key值小的值,找到之后放入坑位,此时right就形成新的坑。然后left从左往右找比key大的值, 找到之后放入坑位,此时left就又形成新的坑。
2. 最后left与right相遇,将key放入最后一个坑,并将该位置记为mid,。·
3. 最后划分区间[left,mid-1]与[mid+1,right]继续重复1,2步骤。直至不能划分。

代码实现:

int PartSort2(int* arr, int begin, int end)
{int left = begin, right = end;int hole = begin;//记录坑位int key = arr[left];while (left < right){//left=而不是>防止数据出现死循环while (left < right && arr[right] >= key)//寻找比key小的值{right--;}arr[hole] = arr[right];hole = right;while (left < right && arr[left] <= key)//寻找比key大的值{left++;}arr[hole] = arr[left];hole = left;}arr[hole] = key;return hole;
}
void QuickSort(int* arr, int left, int right)
{if (left >= right)//不能划分{return;}int mid = PartSort2(arr, left, right);QuickSort(arr, left, mid - 1);//左区间QuickSort(arr, mid+1, right);//右区间
}

1.2.3 双指针法

1. 先定义一个prev指向数组首元素,然后定义一个cur指向第二个位置。
2. cur从左往右依次遍历找key小的值,找到之后++prev,然后交换prev与cur指向的值。之后cur++继续遍历。(key为起始位置的值)
3. 当cur遍历完之后,此时交换prev指向的值与key。将此时位置记为mid。
4. 最后划分区间[left,mid-1]与[mid+1,right]继续重复1,2,3步骤。直至不能划分。

代码实现:

int PartSort3(int* arr, int begin, int end)
{int prev = begin;int cur = begin + 1;int keyi = begin;while (cur <= end){if (arr[cur] < arr[keyi])//小于则交换{swap(&arr[++prev], &arr[cur]);}cur++;}swap(&arr[prev], &arr[keyi]);return prev;
}
void QuickSort(int* arr, int left, int right)
{if (left >= right)//不能划分{return;}int mid = PartSort3(arr, left, right);QuickSort(arr, left, mid - 1);//左区间QuickSort(arr, mid+1, right);//右区间
}

1.3 算法优化

1.3.1 改变基准元素

当数组有序时,我们再对其进行快速排序,其时间复杂度讲话劣化为O(N2)。

这时候我们为了防止这种现象,可以选择提前改变基准元素 key。

三数取中:即取出数组首尾以及中间元素,选取数值位于中间的元素作为准元素 key。

int GetMidNum(int*arr, int left, int right)
{int mid = (left + right) >> 1;if (arr[mid] > arr[left]){if (arr[mid] < arr[right]){	//left  mid  rightreturn mid;}else if (arr[left] > arr[right]){	//right  left  midreturn left;}else{	//left  right  midreturn right;}}
}
int PartSort3(int* arr, int begin, int end)
{int prev = begin;int cur = begin + 1;int keyi = begin;int mid=GetMidNum(arr, begin, end);swap(&arr[begin], &arr[mid]);while (cur <= end){if (arr[cur] < arr[keyi])//小于则交换{swap(&arr[++prev], &arr[cur]);}cur++;}swap(&arr[prev], &arr[keyi]);return prev;
}

② 随机数取中:三数取中有时候也并不能保证基准元素的准确性,这时候我们最好使用随机数获取基准值。

int GetRanNum(int*arr, int left, int right)
{srand(time(0));//生成随机种子int mid = rand() % (right - left) + left;//随机数return mid;
}

1.3.2 区间优化

我们进行递归调用时,递归越深递归调用的次数就会越多,为了优化这个问题,我们可以当区间较小时采用其他排序,如插入排序。

void QuickSort(int* arr, int left, int right)
{if (left >= right)//不能划分return;if ((right - left + 1) < 10)//小区间优化{InsertSort(arr+left, right - left + 1);return ;}int mid = PartSort3(arr, left, right);QuickSort(arr, left, mid - 1);//左区间QuickSort(arr, mid+1, right);//右区间
}

2. 归并排序

2.1 算法思想

归并排序(Merge Sort)是建立在归并操作上的一种有效的排序算法, 该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

2.2 算法步骤

1. 创建一个与待排序数组同等大小的tmp数组。
2. 然后将待排序数组分为两个子数组,让两个子数组有序。为了让这两个子数组有序,我们又要将每个子数组分为两个子数组,让其有序。
3. 当子数组没有元素或者只有一个元素时,我们可以认为其有序,然后将两个子数组开始归并。
4. 归并时因为两个子数组有序,我们可以定义两个指针begin1,begin2分别指向两个数组起始位置。然后遍历比较arr[begin1]与arr[begin2],取较小的元素尾插进tmp数组。
5. 最后tmp数组数据拷贝回原数组。

2.3 动画演示

2.4 代码实现

void _MergeSort(int* arr, int begin, int end, int* tmp)
{if (begin >= end)return;int mid = (begin + end) >> 1;_MergeSort(arr, begin, mid, tmp);//归并左区间_MergeSort(arr, mid+1, end, tmp);//归并右区间int i = begin;int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[i++] = arr[begin1++];}else{tmp[i++] = arr[begin2++];}}//若是还有区间存在数据while (begin1 <= end1){tmp[i++] = arr[begin1++];}while (begin2 <= end2){tmp[i++] = arr[begin2++];}//最后将归并完后后的数据拷贝回原数组memcpy(arr + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* arr, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail:");return;}_MergeSort(arr, 0, n - 1, tmp);free(tmp);tmp = NULL;
}

2.5 算法优化

2.5.1 区间优化

当递归调用层数越多时,最后三层的递归调用会浪费大量时间。为了避免这种情况,这时我们就可以采用小区间使用插入排序的方法。

void _MergeSort(int* arr, int begin, int end, int* tmp)
{if (begin >= end)return;if (end - begin + 1 < 10)//小区间优化{InsertSort(arr + begin, end - begin + 1);return;}int mid = (begin + end) >> 1;_MergeSort(arr, begin, mid, tmp);//归并左区间_MergeSort(arr, mid+1, end, tmp);//归并右区间int i = begin;int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[i++] = arr[begin1++];}else{tmp[i++] = arr[begin2++];}}//若是还有区间存在数据while (begin1 <= end1){tmp[i++] = arr[begin1++];}while (begin2 <= end2){tmp[i++] = arr[begin2++];}//最后将归并完后后的数据拷贝回原数组memcpy(arr + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}

2.5.2 判断区间有序

在归并排序合并时,如果两个区间是有序,即 arr[end1] <= arr[begin2] 时就不需要对其进行归并。

void _MergeSort(int* arr, int begin, int end, int* tmp)
{if (begin >= end)return;int mid = (begin + end) >> 1;_MergeSort(arr, begin, mid, tmp);//归并左区间_MergeSort(arr, mid+1, end, tmp);//归并右区间int i = begin;int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;if (arr[begin2] < arr[end1])//区间有序则不合并{while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[i++] = arr[begin1++];}else{tmp[i++] = arr[begin2++];}}//若是还有区间存在数据while (begin1 <= end1){tmp[i++] = arr[begin1++];}while (begin2 <= end2){tmp[i++] = arr[begin2++];}//最后将归并完后后的数据拷贝回原数组memcpy(arr + begin, tmp + begin, sizeof(int) * (end - begin + 1));}
}

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

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

相关文章

05. 事件处理

一、信号与槽在 QML 中,信号与槽机制是对象间通信的一种重要方式。它允许对象在其状态改变或发生特定事件时通知其他对象,并触发相应的处理函数。信号 是对象发出的通知,表明某个事件已经发生。槽 (信号处理器)是…

网站流量分析的指标有哪些wordpress京东主题

文章目录 第6章 逻辑斯谛回归与最大熵模型6.1 逻辑斯谛回归模型6.1.1 逻辑斯谛分布6.1.2 二项逻辑斯谛回归模型6.1.3 模型参数估计6.1.4 多项逻辑斯谛回归 《统计学习方法&#xff1a;李航》笔记 从原理到实现&#xff08;基于python&#xff09;-- 第3章 k邻近邻法 《统计学习…

总结问题2 软工10.3

使用纯水机发生器公式进行不断的遍历, XN是上一个种子,它遍历出XN加1就是下一个种子。Jdk SIMULATOR outer point ln的方法中,它是利用了很多的重载,来实现不同的功能。关于4则运算,答题框的生成,可以以答题框为…

如何选择网站建设流程网站展示效果图

我想用来自Java的参数调用python程序。但是我的输出是空白。代码在这里。 Python代码在这里&#xff1a; import sys print(sys.argv[1]) Java代码在这里&#xff1a; public class PrintNumber{ public static void main(String[] args){ Process proc; try { proc Runtime.g…

BPL包无法调试的问题

转 由于系统结构是Host主程序动态加载BPL包的模式。所以用到了Package的调试,但无论如何有一个包就是无法调试(加断点不起作用)。经过N久的查找,发现: 1.包Package在编译,生成的时候会自动产生DCP和BPL文件,缺省…

学院网站群建设的目标网站开发背景和意义

国产大模型开源一哥再登场&#xff0c;最强双语LLM「全家桶」级开源&#xff01;340亿参数超越Llama2-70B 为什么说大模型训练很难&#xff1f; - 知乎 GitHub - jeinlee1991/chinese-llm-benchmark: 中文大模型能力评测榜单&#xff1a;覆盖百度文心一言、chatgpt、阿里通义千…

如何在国内做网站手机网站建设选 朗创营销

图为RUST吉祥物 大家好,我是get_local_info作者带剑书生,这里用一篇文章讲解get_local_info是怎样获得杀毒软件的病毒库时间的。 首先,先要了解get_local_info是什么? get_local_info是一个获取linux系统信息的rust三方库,并提供一些常用功能,目前版本0.2.4。详细介绍地址…

江苏省建设工程竣工备案网站wordpress发号系统

目录 1、环境 1.1 操作系统初始化配置 1.2 部署 docker引擎 1.3 部署 etcd 集群 1.4 准备签发证书环境 1.5 部署 Master 组件 1.6 部署 Worker Node 组件 1.7 部署 CNI 网络组件 1.7.1 部署 flannel 1.7.2 部署 Calico 1.7.3 node02 节点部署 1.7.4 部署 CoreDNS 1…

信息科学与数据分析:真正的区别是什么?

信息科学与数据分析:真正的区别是什么?pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "M…

awk命令一文速通

awk命令一文速通1. awk简介和基本语法格式 Awk自动地搜索输入文件,并把每一个输入行切分成字段。许多工作都是自动完成的,例如读取每个输入行、字段分割、存储管理、初始化等。在AWK中不需声明变量数据类型,它内置字…

小程序开发:开启定制化custom-tab-bar但不生效疑问,以及使用NutUI-React Taro的安装和使用

小程序开发:开启定制化custom-tab-bar但不生效疑问,以及使用NutUI-React Taro的安装和使用2025-10-03 18:28 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !import…

做影视外包的网站优化方案怎么写

长期佩戴耳机可能会出现听力下降、耳道感染等危害。 听力下降&#xff1a;长时间戴耳机可能会导致耳道内的声音过大&#xff0c;容易对耳膜造成一定的刺激&#xff0c;容易出现听力下降的情况。 耳道感染&#xff1a;长时间戴耳机&#xff0c;耳道长期处于封闭潮湿的情况下&a…

免费com域名网站湖北省勘察设计协会网站

我们新项目硬件设计上使用gpio口做按键&#xff0c;所以我就需要搞定这个驱动&#xff0c;本来想自己写一个gpio口的按键驱动&#xff0c;然后看了下内核下面的代码&#xff0c;已经有现成的了。Linux内核下游很多很多的现成驱动&#xff0c;只要你想得到的&#xff0c;基本都是…

获胜者网站建设旅游网页素材

目录 1、享元模式&#xff08;Flyweight Pattern&#xff09;含义 2、享元模式的UML图学习 3、享元模式的应用场景 4、享元模式的优缺点 5、C实现享元模式的简单实例 1、享元模式&#xff08;Flyweight Pattern&#xff09;含义 享元模式&#xff08;Flyweight&#xff09…

深圳网站建设 设计首选中国建设银行南京分行网站首页

儿童安全门和围栏 儿童安全门和围栏用于在门口&#xff08;如门道&#xff09;内设置围栏&#xff0c;或用作自支撑围栏&#xff0c;将幼儿可能在其中活动的区域围起来。这些商品可能由塑料、金属、乙烯树脂或木制组件等材料制成。此政策包括但不限于可扩展围栏、伸缩安全门和…

最短路练习

最短路为背景的题 + 做法是最短路的题A - Minimum Path https://www.luogu.com.cn/problem/CF1473E经典套路,发现可以把 max 和 min 换成任意路径中的边,然后 max 和 min 就会最小化这个式子 故写一个 Dijkstra + DP…

东莞网站建设选择菲凡网络wordpress布局切换功能

题目&#xff1a; P2024 [NOI2001] 食物链 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 本文学习自&#xff1a; 题解 P2024 【食物链】 - RE: 从零开始的异世界信竞生活 - 洛谷博客 (luogu.com.cn) ———— 关系并查集其实就是在普通并查集的基础上额外开个数组r…

依据XShell采用Git三板斧

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

沈阳微信网站制作重庆市建设工程管理网

文章来源&#xff1a;芯片开放社区&#xff0c;作者&#xff1a;OCC编辑万里征途迈出第一步&#xff0c;基于RISC-V的安卓10系统来了。点击链接查案演示视频&#xff1a; 平头哥芯片开放社区(OCC)​occ.t-head.cn今天&#xff0c;平头哥完成了安卓10对RISC-V的移植并开源了全部…

什么网站做ppt赚钱app下载注册量推广平台

spring4单切面 spring4多切面 spring4 spring5