快速排序算法详解:Hoare法、挖坑法、前后指针法及非递归实现
创作时间:
作者:
@小白创作中心
快速排序算法详解:Hoare法、挖坑法、前后指针法及非递归实现
引用
CSDN
1.
https://m.blog.csdn.net/weixin_64099089/article/details/137792497
**快速排序是一种非常高效的排序算法,由C. A. R. Hoare在1960年提出。它采用分治法策略,通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,然后分别对这两部分记录继续进行排序,以达到整个序列有序的目的。本文将详细介绍快速排序的多种实现方式,包括Hoare法、挖坑法、前后指针法以及非递归实现(栈和队列)。
1. Hoare法
快速排序的核心思想是选择一个基准元素(通常选择序列的第一个元素),通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,然后分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
单趟排序步骤:
- 选定一个基准元素(key),初始化左右指针L和R
- R从右向左查找比key小的元素
- L从左向右查找比key大的元素
- 交换L和R位置的元素
- 重复步骤2-4,直到L和R相遇
- 最后将基准元素与相遇位置的元素交换
单趟排序的意义:
- 基准元素被放置到正确的位置
- 将序列分割成两个子区间,左侧元素都小于基准元素,右侧元素都大于基准元素
单趟排序实现:
void PartSort(int* a, int begin, int end) {
if (begin >= end)
return;
int keyi = begin;
int key = a[keyi];
int Left = begin;
int Right = end;
while (Left < Right) {
while (Left < Right && a[Right] >= key)
Right--;
while (Left < Right && a[Left] <= key)
Left++;
Swap(&a[Left], &a[Right]);
}
Swap(&a[keyi], &a[Left]);
}
快速排序递归实现:
void QuickSort(int *a, int begin, int end) {
assert(a);
if (begin >= end)
return;
int meet = PartSort(a, begin, end);
QuickSort(a, begin, meet - 1);
QuickSort(a, meet + 1, end);
}
2. 挖坑法
挖坑法是快速排序的另一种实现方式,其核心思想是通过"挖坑填数"的方式进行元素交换,可以减少代码中的边界条件判断,使代码更加简洁。
单趟排序步骤:
- 选择一个基准元素(key),将其位置设为"坑"
- R从右向左查找比key小的元素,找到后填入"坑"中,当前位置变成新的"坑"
- L从左向右查找比key大的元素,找到后填入"坑"中,当前位置变成新的"坑"
- 重复步骤2-3,直到L和R相遇
- 最后将基准元素填入相遇位置的"坑"中
单趟排序实现:
int QuickPastSort2(int* a, int begin, int end) {
assert(a);
int keyi = GetMidIndex(a, begin, end);
int key = a[keyi];
Swap(&a[keyi], &a[begin]);
keyi = begin;
int hole = begin;
int left = begin;
int right = end;
while (left < right) {
while (left < right && a[right] >= key)
right--;
a[hole] = a[right];
hole = right;
while (left < right && a[left] <= key)
left++;
a[hole] = a[left];
hole = left;
}
a[hole] = key;
return hole;
}
3. 前后指针法
前后指针法是快速排序的另一种实现方式,其核心思想是使用两个指针(prev和cur)进行元素交换,可以进一步简化代码逻辑。
单趟排序步骤:
- 初始化prev指向基准元素,cur指向基准元素的下一个位置
- prev和cur同时向前移动,直到prev的下一个位置是比基准元素大的数
- prev停在比基准元素大的数前面,cur继续向后查找比基准元素小的数
- cur找到比基准元素小的值后,prev先++,然后prev和cur位置的数交换
- 最后cur越界时,key与prev指向的位置交换
单趟排序实现:
int QuickPartSort3(int* a, int begin, int end) {
assert(a);
int keyi = GetMidIndex(a, begin, end);
Swap(&a[keyi], &a[begin]);
int key = a[begin];
keyi = begin;
int prev = begin;
int cur = begin + 1;
while (cur <= end) {
if (a[cur] < key && ++prev != cur)
Swap(&a[cur], &a[prev]);
cur++;
}
Swap(&a[begin], &a[prev]);
return prev;
}
4. 快排的非递归实现
快速排序的递归实现可能会导致栈溢出问题,特别是在数据量较大时。因此,可以使用栈或队列来实现非递归版本的快速排序。
4.1 栈实现快排非递归
使用栈实现非递归快速排序时,可以按照前序遍历的顺序,先处理左子区间,再处理右子区间。
void StackQuickSort(int* a, int n) {
assert(a);
ST st;
STInit(&st);
StackPush(&st, n - 1);
StackPush(&st, 0);
while (!StackEmpty(&st)) {
int begin = StackTop(&st);
StackPop(&st);
int end = StackTop(&st);
StackPop(&st);
if (begin >= end)
continue;
int meet = QuickPastSort1(a, begin, end);
StackPush(&st, end);
StackPush(&st, meet + 1);
StackPush(&st, meet - 1);
StackPush(&st, begin);
}
STDestroy(&st);
}
4.2 队列实现快排非递归
使用队列实现非递归快速排序时,可以按照层序遍历的顺序,依次处理每个子区间。
void QueueQuickSort(int* a, int n) {
assert(a);
Queue q;
QueueInit(&q);
QueuePush(&q, 0);
QueuePush(&q, n - 1);
while (!QueueEmpty(&q)) {
int begin = QueueFront(&q);
QueuePop(&q);
int end = QueueFront(&q);
QueuePop(&q);
if (begin >= end)
continue;
int meet = QuickPastSort1(a, begin, end);
QueuePush(&q, begin);
QueuePush(&q, meet - 1);
QueuePush(&q, meet + 1);
QueuePush(&q, end);
}
QueueDestroy(&q);
}
使用栈和队列实现非递归快速排序时,空间复杂度为O(1),但在实际应用中,递归实现更为简洁和方便。
热门推荐
腊月腌咸鱼,最佳时间揭秘
别大意!半年便秘拖成肠癌,这4个症状可能是肠癌的先兆
稳定学习预后标志物,多种癌症生存曲线证实!清华最新成果登Nature顶级子刊
结直肠癌分期与精准治疗
糖尿病患者如何摆脱“糖分焦虑”
糖尿病患者居家管理:三忌三慎保健康
火星蚁进化之谜被破解!
四川盆地揭秘:三叠纪-侏罗纪之交的昆虫与植物“相爱相杀”
都二霞教授带你探秘昆虫世界
中国科学技术大学:权威榜单前三的学术实力
中国科大螺旋机器人创新突破,展现广阔应用前景
中国科学技术大学与中国移动签署战略合作协议
麦克斯韦方程组
雅思口语高分技巧:六大类连贯性用语详解
“3·15”小周爆料 | 在交易猫买卖游戏账号,为何钱号两空?
桂平到贵港两日游:西山风景名胜区深度游览攻略
黄精养生新潮流:这样吃最有效!
2024数博会:首批全国一体化算力网应用优秀案例发布
白蚁:地球的“清道夫”还是害虫?
白蚁基因组计划揭秘:从社会结构到生态应用
白蚁巢寄生的秘密:共存与智慧的生存艺术
深圳市白蚁防治服务中心教你防白蚁护农田
糖尿病患者饮酒小心心血管“爆表”
糖尿病患者饮酒指南:小心肝脏报警!
销项税普票也能抵扣?真相揭秘
磁控胶囊胃镜:无创检查精度达94%,双体位设计更人性化
胶囊内镜:无痛检查新选择,这些情况仍需传统胃肠镜
对话系统如何读懂你的心?
动态再平衡策略助力基金投资:定期调整持仓比例
10年期国债收益率降至2.06%,债市投资价值凸显