剑指Offer-快速排序
剑指Offer上的快速排序的Partition函数与我在数据结构书上学到的不一样,因此就想要探索下这两种不同的处理方式。
基本思想
快速排序的基本思想是基于分治法。在待排序表L[1...n]中任取一个元素pivot作为基准,通过一趟排序将待排序表划分为独立的两部分L[1...k-1]和L[k+1...n],使得L[1...k-1]中所有元素小于pivot,L[k+1...n]中所有元素大于或等于pivot,则pivot放在了其最终位置L(k)上。而后递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。
剑指Offer上的Partition实现
// Partition为分割函数, length表示data数组的长度
int Partition(int data[], int length, int low, int high) {
if (data == nullptr || length <= 0 || low < 0 || high >= length) {
return 1;
}
// 在[low, high]区间中随机取一个基准值,并将其换到区间最末尾
int index = RandomInRange(low, high);
Swap(&data[index], &data[high]);
// small在low的前面一位
int small = low - 1;
for (index = low; index < high; ++index) {
if (data[index] < data[high]) {
// ++small后表示大于基准值的第一个数的下标
// 如果不存在大于基准值的数,则表示当前比较的数字下标
++small;
// 交换后的small表示小于基准值的最后一个数字的下标
if (small != index) {
Swap(&data[index], &data[small]);
}
}
}
// ++small后表示大于基准值的第一个数的下标
++small;
Swap(&data[small], &data[high]);
return small;
}
快速排序其它函数实现如下:
// 生成一个随机数,范围在[low,high),是一个前闭后开的区间
int RandomInRange(int low, int high) {
return rand() % (high - low) + low;
}
// 交换两个整数
void Swap(int* left, int* right) {
int temp = *left;
*left = *right;
*right = temp;
}
// 快速排序函数
void QuickSort(int data[], int length, int low, int high) {
if (low == high) {
return;
}
int index = Partition(data, length, low, high);
if (index > low) {
QuickSort(data, length, low, index - 1);
}
if (index < high) {
QuickSort(data, length, index + 1, high);
}
}
// 数据打印
void PrintData(int data[], int length) {
for (int i = 0; i < length; ++i) {
std::cout << data[i] << " ";
}
std::cout << std::endl;
}
int main() {
// 以系统时间作为随机函数种子
srand((unsigned)time(nullptr));
int const length = 10;
int data[length];
// 生成随机数
for (int i = 0; i < length; ++i) {
data[i] = RandomInRange(1, length*length);
}
// 打印生成的随机数
PrintData(data, length);
// 调用快排函数进行排序
QuickSort(data, length, 0, length - 1);
// 打印快速排序后的数
PrintData(data, length);
return 0;
}
初始顺序为{49, 38, 65, 97, 76, 13, 27},基准值为49的数据的第一趟快速排序的实现如下所示:
步骤 | small | index | |||||||
---|---|---|---|---|---|---|---|---|---|
0 | 49 | 38 | 65 | 97 | 76 | 13 | 27 | ||
1 | 27 | 38 | 65 | 97 | 76 | 13 | 49 | ||
2 | 0 | 0 | 27 | 38 | 65 | 97 | 76 | 13 | 49 |
3 | 1 | 1 | 27 | 38 | 65 | 97 | 76 | 13 | 49 |
4 | 1 | 2 | 27 | 38 | 65 | 97 | 76 | 13 | 49 |
5 | 1 | 3 | 27 | 38 | 65 | 97 | 76 | 13 | 49 |
6 | 1 | 4 | 27 | 38 | 65 | 97 | 76 | 13 | 49 |
7 | 2 | 5 | 27 | 38 | 13 | 97 | 76 | 65 | 49 |
8 | 3 | 6 | 27 | 38 | 13 | 49 | 76 | 65 | 97 |
第一趟排序完之后基准值49之前的{27, 38, 13}都比49小,基准值49之后的{76, 65, 97}都比49大。
一般教材上的Partition实现
// Partition为分割函数
int Partition(int data[], int low, int high) {
// 将当前表中第一个元素设为基准值,对表进行划分
int pivot = data[low];
// 循环跳出条件
while (low < high) {
while (low < high && data[high] >= pivot) {
--high;
}
// 将比基准值小的元素移动到左端
data[low] = data[high];
while (low < high && data[low] <= pivot) {
++low;
}
// 将比基准值大的元素移动到右端
data[high] = data[low];
}
// 基准元素存放到最终位置
data[low] = pivot;
// 返回存放基准值的最终位置
return low;
}
快速排序其它函数实现如下:
void QuickSort(int data[], int low, int high) {
// 递归跳出条件
if (low < high) {
// 将data[low...high]一分为二,pivotpos是基准值
int pivotpos = Partition(data, low, high);
// 对左子表进行递归排序
QuickSort(data, low, pivotpos - 1);
// 对右子表进行递归排序
QuickSort(data, pivotpos + 1, high);
}
}
初始顺序为{49, 38, 65, 97, 76, 13, 27},基准值为49的数据的第一趟快速排序的实现如下所示:
步骤 | low | high | |||||||
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 6 | 49 | 38 | 65 | 97 | 76 | 13 | 27 |
1 | 2 | 6 | 27 | 38 | 65 | 97 | 76 | 13 | 27 |
2 | 2 | 6 | 27 | 38 | 65 | 97 | 76 | 13 | 65 |
3 | 2 | 5 | 27 | 38 | 13 | 97 | 76 | 13 | 65 |
4 | 3 | 5 | 27 | 38 | 13 | 97 | 76 | 97 | 65 |
5 | 3 | 3 | 27 | 38 | 13 | 49 | 76 | 97 | 65 |
第一趟排序完之后基准值49之前的{27, 38, 13}都比49小,基准值49之后的{76, 97, 65}都比49大。
总结
可以看出,上述这两种思想导致的运行过程是不同的,前者是用small指代小于标准元素的最右位置,在遍历过程中进行交换操作来保证small左边都是小于标准元素的元素,而后者是用low,high分别表示小于和大于基准值的集合边界位置,所以后者更浅显易懂。
个人主页:
剑指Offer-快速排序的更多相关文章
- 剑指 Offer 40. 最小的k个数 + 优先队列 + 堆 + 快速排序
剑指 Offer 40. 最小的k个数 Offer_40 题目描述 解法一:排序后取前k个数 /** * 题目描述:输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7. ...
- [读]剑指offer
研二的开始找工作了,首先祝愿他们都能够找到自己满意的工作.看着他们的身影,自问明年自己这个时候是否可以从容面对呢?心虚不已,赶紧从老严那儿讨来一本<剑指offer>.在此顺便将自己做题所想 ...
- 剑指OFFER——调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 剑指offer书里的版本, ...
- 剑指Offer——顺丰笔试题+知识点总结
剑指Offer--顺丰笔试题+知识点总结 情景回顾 时间:2016.10.16 19:00-20:40 地点:山东省网络环境智能计算技术重点实验室 事件:顺丰笔试 知识点总结 快排 霍尔排序(快排) ...
- 剑指Offer——知识点储备-常用算法
剑指Offer--知识点储备-常用算法 快速排序 注:若排序是有序的,采用快排,则退化为冒泡排序. 解决这个问题,采用两个选取基准的方法 (1)随机选取基数(在这个区间内随机取一个数) 出现的恶劣情况 ...
- 剑指Offer——好未来视频面知识点储备+面后总结
剑指Offer--好未来视频面知识点储备+面后总结 情景介绍 时间:2016.10.12 13:00- 地点:宿舍 事件:好未来视频面 知识点储备 数据结构 单链表反转 public class Li ...
- 剑指Offer——分治算法
剑指Offer--分治算法 基本概念 在计算机科学中,分治法是一种很重要的算法.字面上的解释是"分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更 ...
- 剑指offer:调整数组顺序使奇数位于偶数前面
题目 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分. 分析 事实上,这个题比较简单,很多种方式都可以实现,但是其时间复杂度或空间复 ...
- 关于数据结构,剑指offer上面的
我很喜欢那些javascript解决的编程题,感觉非常的有意思.我在博客园上面看到了一个同学的博客,他一共发了34篇剑指offer的编程题,还给出了非常详细的解答. 接下来的工作,我做的就是搬运工,不 ...
- 《剑指offer》内容总结
(1)剑指Offer——Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是统计和排序大量的字符串(但不仅限于字符串),所以经常 ...
随机推荐
- 【Python开发】Python之re模块 —— 正则表达式操作
Python之re模块 -- 正则表达式操作 这个模块提供了与 Perl 相似l的正则表达式匹配操作.Unicode字符串也同样适用. 正则表达式使用反斜杠" \ "来代表特殊形式 ...
- 递归拷贝目录与删除目录 WindowsAPI C++
/*判断一个路径是否是已存在的目录*/ bool IsDirectory(const std::wstring& pstrPath) { DWORD dw = GetFileAttribute ...
- jump用户管理命令
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa手动拷贝id_rsa.pub的内容到其他机器 或者用 ssh-copy,但你得知道对方root密码 ssh-copy-i ...
- 【Linux-驱动】printk的打印级别
级别: 日志级别用宏表示,日志级别宏展开为一个字符串,在编译是由预处理器将它和消息本文拼接成一个字符串,因此printk函数中日志级别宏和格式字符串间不能有逗号.printk的日志级别定义在 /inc ...
- python实现:递归删除文件并保存删除记录
说明: 此脚本用于删除指定目录及子目录下符合删除规则的文件,并将删除的文件记录到指定目录下的指定文件,便于查看删除记录. 注意: 此脚本删除的文件,不会进入回收站,将被直接彻底删除,请谨慎操作!!! ...
- Java 的不可变类 (IMMUTABLE CLASS) 和 可变类 (MUTABLE CLASS)
一.简单定义不可变对象(Immutable Objects)即对象一旦被创建,它的状态(对象的数据,也即对象属性值)就不能改变,反之即为可变对象(Mutable Objects).当满足以下条件时,对 ...
- JavaScript和JSON转化
1, JSON转JavaScript JSON.parse():https://www.runoob.com/json/json-parse.html 2, JavaScript转JSON JSON. ...
- 06: zabbix常见面试题
1.1 zabbix架构 1.zabbix理论 1)Zabbix是一个企业级的.开源的.分布式的监控套件,Zabbix可以监控网络和服务的监控状况. 2)Zabbix利用灵活的告警机制,允许用户对事件 ...
- 使用antd List组件实现轮播图
import { List, Avatar, Carousel } from 'antd'; import { connect } from 'dva'; import './lamp.less' c ...
- oa_mvc_easyui_登录完成(2)
1.使用MVC特有的ajax方式:异步表单提交方式 js文件引用:<script src="~/Scripts/jquery.unobtrusive-ajax.min.js" ...