c语言快速排序(霍尔法、挖坑法、双指针法)图文详解

2023-12-15 01:04

本文主要是介绍c语言快速排序(霍尔法、挖坑法、双指针法)图文详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

快速排序介绍:

  快速排序是一种非常常用的排序方法,它在1962由C. A. R. Hoare(霍尔)提的一种二叉树结构的交换排序方法,故因此它又被称为霍尔划分,它基于分治的思想,所以整体思路是递归进行的。

整体思路:

1.先选取一个key,关于key值的选取,一般是选数组第一个元素,数组中间元素,数组最后一个元素,这三个元素的中间值,并将这个元素与数组第一个元素进行交换。

2.将key放入整个区间中正确的位置,即为key左边的元素都比key小,右边的元素都比key要大,此时的key就是它排好序的位置,注意key左边的元素都比它小,但不一定有序,右边也是一样,然后根据递归的思想,再对key左边的区间进行上面一样的操作和key右边的区间进行上面一样的的操作,当区间不存在或者区间只有一个元素时返回。

如何将key放入正确的位置:

  将key放入正确的位置正是每趟递归需要做的,那么具体该如何实现呢?

  实现过程目前有三种方法,每种方法虽然写法不同,但总体思路一样,所以效率是相同的,只要完全理解快速排序,写哪种都一样。

1.霍尔版本(传统方法)

第一步:定义一个right从数组最后一个元素开始即数组的右边开始向左边遍历,如果找到比key小的值就停下来。

第二步:定义一个left从数组第一个元素开始即数组的左边开始向右遍历,如果找到比key大的值就停下来。

第三步:left和right都停下来之后,交换left和right的值,这一步的目的就是将比key小的值往左放,将比key大的值。

第四步:当left和right相遇后,将第一个元素(即为key的值)与它们相遇位置的值交换。

第五步:让他们相遇位置的左区间和右区间同样执行上述四步(即为递归)。

动态思路图:

  

代码实现:

void Swap(int* a, int* b)
{int tmp = 0;tmp = *a;*a = *b;*b = tmp;
}int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;if (a[begin] > a[midi]){if (a[midi] > a[end]){return midi;}else if (a[end] > a[begin]){return begin;}else{return end;}}else{if (a[begin] > a[end]){return begin;}else if (a[end] > a[midi]){return midi;}else{return end;}}
}void QuickSortHoare(int* a, int begin, int end)
{int left = begin;int right = end;if (left >= right){return;}int midi = GetMidi(a, begin, end);Swap(&a[begin], &a[midi]);int keyi = begin;while (left < right){while (left < right && a[right] >= a[keyi]){right--;}while (left < right && a[left] < a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;QuickSortHoare(a, begin, keyi - 1);QuickSortHoare(a, keyi + 1, end);
}

2.挖坑法:

第一步:将key的位置(即为第一个元素的位置)作为第一个坑位,将key的值一直保存在变量key中。

第二步:定义一个right从数组最后一个元素开始即为数组右边开始向左遍历,如果找到比key小的值,right停下来,将right下标访问的元素赋值到上一个坑位,并将right作为新的坑位。

第三步:定义一个left从数组第一个元素开始即为数组左边开始向右遍历,如果找到比key大的值,left停下来,将left下标访问的元素赋值到上一个坑位,并将left作为新的坑位。

第四步:当right和left相遇时,此时它们访问的元素绝对是坑位,只需将key里保存的key值放入坑位即可。

第五步:让他们相遇位置的左区间和右区间同样执行上述四步(即为递归)。

思路图:

代码实现:

void Swap(int* a, int* b)
{int tmp = 0;tmp = *a;*a = *b;*b = tmp;
}int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;if (a[begin] > a[midi]){if (a[midi] > a[end]){return midi;}else if (a[end] > a[begin]){return begin;}else{return end;}}else{if (a[begin] > a[end]){return begin;}else if (a[end] > a[midi]){return midi;}else{return end;}}
}
void QuickSortHole(int* a, int begin, int end)
{int left = begin;int right = end;if (begin >= end){return;}int midi = GetMidi(a, begin, end);Swap(&a[begin], &a[midi]);int key = a[begin];int hole = begin;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;int keyi = hole;QuickSortHole(a, begin, keyi - 1);QuickSortHole(a, keyi + 1, end);
}

3.双指针法(新手推荐)

第一步:定义两根指针cur和prev,初始位置如下图所示:

第二步:cur开始往后走,如果遇到比key小的值,则++prev,然后交换prev和cur指向的元素,再++cur,如果遇到比key大的值,则只++cur。

第三步:当cur访问过最后一个元素后,将key的元素与prve访问的元素交换位置。cur访问完整个数组后的各元素位置如下图所示:

第四步:让prev的左区间和右区间同样执行上述三步(即为递归)。

代码实现:

void Swap(int* a, int* b)
{int tmp = 0;tmp = *a;*a = *b;*b = tmp;
}int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;if (a[begin] > a[midi]){if (a[midi] > a[end]){return midi;}else if (a[end] > a[begin]){return begin;}else{return end;}}else{if (a[begin] > a[end]){return begin;}else if (a[end] > a[midi]){return midi;}else{return end;}}
}void QuickSortD(int* a, int begin, int end)
{if (begin >= end){return;}int midi = GetMidi(a, begin, end);Swap(&a[begin], &a[midi]);int keyi = begin;int prev = begin;int cur = begin + 1;while (cur <= end){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[keyi], &a[prev]);keyi = prev;QuickSortD(a, begin, keyi - 1);QuickSortD(a, keyi + 1, end);
}

下期预告:非递归

  这期讲的三种快速排序方法均是采用递归的方法来实现的,那么如何使用非递归来实现快速排序呢?下期我将发布快速排序的非递归法。

这篇关于c语言快速排序(霍尔法、挖坑法、双指针法)图文详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/494542

相关文章

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

一文详解如何在Python中从字符串中提取部分内容

《一文详解如何在Python中从字符串中提取部分内容》:本文主要介绍如何在Python中从字符串中提取部分内容的相关资料,包括使用正则表达式、Pyparsing库、AST(抽象语法树)、字符串操作... 目录前言解决方案方法一:使用正则表达式方法二:使用 Pyparsing方法三:使用 AST方法四:使用字

Python列表去重的4种核心方法与实战指南详解

《Python列表去重的4种核心方法与实战指南详解》在Python开发中,处理列表数据时经常需要去除重复元素,本文将详细介绍4种最实用的列表去重方法,有需要的小伙伴可以根据自己的需要进行选择... 目录方法1:集合(set)去重法(最快速)方法2:顺序遍历法(保持顺序)方法3:副本删除法(原地修改)方法4:

go 指针接收者和值接收者的区别小结

《go指针接收者和值接收者的区别小结》在Go语言中,值接收者和指针接收者是方法定义中的两种接收者类型,本文主要介绍了go指针接收者和值接收者的区别小结,文中通过示例代码介绍的非常详细,需要的朋友们下... 目录go 指针接收者和值接收者的区别易错点辨析go 指针接收者和值接收者的区别指针接收者和值接收者的

python logging模块详解及其日志定时清理方式

《pythonlogging模块详解及其日志定时清理方式》:本文主要介绍pythonlogging模块详解及其日志定时清理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录python logging模块及日志定时清理1.创建logger对象2.logging.basicCo

前端CSS Grid 布局示例详解

《前端CSSGrid布局示例详解》CSSGrid是一种二维布局系统,可以同时控制行和列,相比Flex(一维布局),更适合用在整体页面布局或复杂模块结构中,:本文主要介绍前端CSSGri... 目录css Grid 布局详解(通俗易懂版)一、概述二、基础概念三、创建 Grid 容器四、定义网格行和列五、设置行

Node.js 数据库 CRUD 项目示例详解(完美解决方案)

《Node.js数据库CRUD项目示例详解(完美解决方案)》:本文主要介绍Node.js数据库CRUD项目示例详解(完美解决方案),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考... 目录项目结构1. 初始化项目2. 配置数据库连接 (config/db.js)3. 创建模型 (models/

SQL表间关联查询实例详解

《SQL表间关联查询实例详解》本文主要讲解SQL语句中常用的表间关联查询方式,包括:左连接(leftjoin)、右连接(rightjoin)、全连接(fulljoin)、内连接(innerjoin)、... 目录简介样例准备左外连接右外连接全外连接内连接交叉连接自然连接简介本文主要讲解SQL语句中常用的表

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

Python中局部变量和全局变量举例详解

《Python中局部变量和全局变量举例详解》:本文主要介绍如何通过一个简单的Python代码示例来解释命名空间和作用域的概念,它详细说明了内置名称、全局名称、局部名称以及它们之间的查找顺序,文中通... 目录引入例子拆解源码运行结果如下图代码解析 python3命名空间和作用域命名空间命名空间查找顺序命名空