快速排序(排序中篇)

1.快速排序的概念及实现

2.快速排序的时间复杂度

3.优化快速排序

4.关于快速排序的细节

5.总代码

1.快速排序的概念及实现

1.1快速排序的概念

快速排序的单趟是选一个基准值,然后遍历数组的内容把比基准值大的放右边,比基准值小的放在左边,下面的动图可以简单了解是这么排序的。

选第一个6作为基准,然后让俩个变量作为下标值去遍历数组,L要选出一个比key(6)要大的值然后停下,R要找到一个比key要小的值然后停下,最后把L和R停下的位置对应的值进行交换,交换完后有继续走,直到L与R相遇,最后把碰面的地方对应的值与key交换,完成一次单趟的快排 ,这样key的左边都是比key小的值,而右边都是比key大的值,与之前比较变得相对有序。

走完单趟后,后面就会以key的左右边重读第一次操作,就是把数组以key为分界线分为俩个数组(逻辑上),再选出一个基准值,使得基准值的俩边要么比key大要么比key小,这样就会想到递归,一直分到后面就会出现只有一个值的数组,或者不存在的(后面会提到),递归完后的数组就是排好序的了。

1.2快速排序的实现

代码:

#include<stdio.h>
void swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void QuickSort(int* a,int left, int right)
{if (left >= right)return;int begin = left;int end =right;int keyi = left;while (begin< end){while (begin < end && a[keyi] <= a[end]){end--;}while (begin < end && a[keyi] >= a[begin]){begin++;}swap(&a[begin], &a[end]);}swap(&a[keyi], &a[begin]);keyi = begin;QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);
}
int main()
{int arr[] = { 11,7,5,9,6,1,4,3,8,8,8,101 };int size = sizeof(arr) / sizeof(arr[0]);QuickSort(arr,0,size-1);for (int i = 0; i < size; i++){printf("%d ", arr[i]);}return 0;
}

代码分析:

创建begin和end来作为L和R去遍历数组,while循环执行的条件是begin<end(这个是begin和end相遇的情况)与基准值比end下标对应的值小,满足就end--(就是往前走),begin就去找比基准值大的值才会执行条件,否则就begin++(往后走),但俩个while都执行完,就说明L与R都停下来了,就可以进行交换,交换完后while循环结束,出循环后就交换基准值与相遇地方对应的值,然后递归去keyi两边的数组排序,则keyi+-1就是它们的新边界,最前面的if判断是否存在可以排序,因为一个和不存在是排不了的。

下图是不存在的情况:

2.快速排序的时间复杂度

本文只是简单计算快速排序的时间复杂度。

 

3.优化快速排序 

3.1三数取中

快速排序遇到有序的数组就会出现问题,时间复杂度会变为O(N^2).

 基准值到最后还是原来的位置,则递归时,每次只会去掉第一个元素,不会像对半分那种,这样每层大约n个,层数也为n个,时间复杂度就为O(N^2),速度会慢很多,所以基准值不应该每次取第一个,可以用随机函数来获取一个拟随机数作为key值,但是也是有可能是最小或者最大(在排升序或者降序会慢),所以用三数取中的方法来定key,但是得到key值还是要和第一个值交换一下,保证上面的代码逻辑不变,假如下标为3的值适合作key就把它和第一个交换。

在有序的情况下插入排序都比快速排序快很多,而且在数据量大的时候会发生栈溢出,是一直递归开辟空间而导致的。

三数取中就是在数组中找到一个值不是最大也不是最小。

三数取中代码实现:

int GetMidi(int*a,int left, int right)
{int midi = (left + right) / 2;if (a[midi] < a[right]){if (a[midi] > a[left]){return midi;}else{if (a[left] > a[right])return right;elsereturn left;}}else//a[midi]>a[right]{if (a[midi] > a[left]){if (a[right] > a[left])return right;elsereturn left;}elsereturn midi;}
}

代码分析:

就是在数组的最左右边及它们和一半值共三个数,然后比较三个值找到处于中间值的下标,这个值一定不是最小或者最大,比较数组的值返回下标,这样尽管处理有序数组也不会有栈溢出的风险,也能提高运行速率。

 3.2小区间优化

因为在二叉树中知道越到后面递归的次数越多,最下面的一层是占总的约一半,而快速排序也有这样的问题,在区间小的时候递归会非常多,所以在小区间就可以用其他的排序方法来代替,但区间小于一个范围时可以采用插入排序来排。

代码实现:

void QuickSort(int* a,int left, int right)
{if (left >= right)return;if ((right - left + 1) < 10){InsertSort(a+left, right - left + 1);}else{int begin = left;int end = right;int keyi = GetMidi(a, left, right);while (begin < end){while (begin < end && a[keyi] <= a[end]){--end;}while (begin < end && a[keyi] >= a[begin]){++begin;}swap(&a[begin], &a[end]);}swap(&a[keyi], &a[begin]);keyi = begin;QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);}}

这里需要注意的是用插入排序时参数应该是a+left而不是a,因为在递归过程中数组已经被分成很多个区间了(逻辑上),插入排序的是指定位置且指定大小排。 

4.关于快速排序的细节

在代码实现里是让R先走的,R停下来才到L走,都停下来才交换,最后在交换L和key的值,为什么key的值一定比L与R相遇的位置对应的值小呢?

下面是分析:

1.L遇R:R先走然后找到了比key小的值停下来了,L开始走,但是没有找到比key大的值,最后于R相遇,此时相遇的地方对应的值是比key小的。

2.R遇L:R找到比key小的值停下来,L也遇到比key大的值停下来了,然后交换,此时R继续走,但是没有找到比key小的值了,就会一直走与L相遇,此时L所在的位置是上一次交换完的位置,也就是说L的位置的值原本是在R上一次停下来的位置对应的值,而这个值是一定比key小的,不然R是不会停下来的。

综上可知,最后L所在的位置是一定小于key对应的值的。

5.总代码:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>void swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];end--;}elsebreak;}a[end + 1] = tmp;}
}
int GetMidi(int*a,int left, int right)
{int midi = (left + right) / 2;if (a[midi] < a[right]){if (a[midi] > a[left]){return midi;}else{if (a[left] > a[right])return right;elsereturn left;}}else//a[midi]>a[right]{if (a[midi] > a[left]){if (a[right] > a[left])return right;elsereturn left;}elsereturn midi;}
}
void QuickSort(int* a,int left, int right)
{if (left >= right)return;if ((right - left + 1) < 10){InsertSort(a+left, right - left + 1);}else{int begin = left;int end = right;int keyi = GetMidi(a, left, right);while (begin < end){while (begin < end && a[keyi] <= a[end]){--end;}while (begin < end && a[keyi] >= a[begin]){++begin;}swap(&a[begin], &a[end]);}swap(&a[keyi], &a[begin]);keyi = begin;QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);}}void TestOP()
{srand(time(0));const int N = 100000;int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);int* a4 = (int*)malloc(sizeof(int) * N);int* a5 = (int*)malloc(sizeof(int) * N);int* a6 = (int*)malloc(sizeof(int) * N);int* a7 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; ++i){// Öظ´²»¶àa1[i] = rand() + i;// Öظ´½Ï¶à//a1[i] = rand();a2[i] = a1[i];a3[i] = a1[i];a4[i] = a1[i];a5[i] = a1[i];a6[i] = a1[i];a7[i] = a1[i];}int begin1 = clock();InsertSort(a1, N);int end1 = clock();int begin5 = clock();QuickSort(a1, 0, N - 1);int end5 = clock();printf("InsertSort:%d\n", end1 - begin1);printf("QuickSort:%d\n", end5 - begin5);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);
}int main()
{int arr[] = { 11,7,5,9,6,1,4,3,8,8,8,101 };int size = sizeof(arr) / sizeof(arr[0]);//TestOP();//InsertSort(arr, size);QuickSort(arr,0,size-1);for (int i = 0; i < size; i++){printf("%d ", arr[i]);}return 0;
}

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

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

相关文章

一本企业画册怎么制作成二维码分享

​在这个数字化时代&#xff0c;二维码已经成为一种便捷的分享方式。企业画册&#xff0c;作为展示企业形象、宣传产品和服务的重要工具&#xff0c;也可以通过二维码进行分享。现在我来教你如何将一本企业画册制作成二维码分享。 1. 准备好制作工具&#xff1a;FLBOOK在线制作…

Springboot校园食堂智能排餐系统-计算机毕业设计源码85935

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对校园食堂智能排餐系统等问题&#xff0c;对…

ubuntu--Linux使用

Linux使用 Linux 系统简介 linux Linux 就是一个操作系统&#xff0c;与 Windows 和 Mac OS 一样是操作系统 。 操作系统在整个计算机系统中的角色 : Linux 主要是 系统调用 和 内核 那两层。使用的操作系统还包含一些在其上运行的应用程序&#xff0c;比如vim、google、vs…

Golang | Leetcode Golang题解之第123题买卖股票的最佳时机III

题目&#xff1a; 题解&#xff1a; func maxProfit(prices []int) int {buy1, sell1 : -prices[0], 0buy2, sell2 : -prices[0], 0for i : 1; i < len(prices); i {buy1 max(buy1, -prices[i])sell1 max(sell1, buy1prices[i])buy2 max(buy2, sell1-prices[i])sell2 m…

C++的List

List的使用 构造 与vector的区别 与vector的区别在于不支持 [ ] 由于链表的物理结构不连续,所以只能用迭代器访问 vector可以排序,list不能排序(因为快排的底层需要随机迭代器,而链表是双向迭代器) (算法库里的排序不支持)(需要单独的排序) list存在vector不支持的功能 链…

网站建设方案书

网站建设方案书是指一份书面计划&#xff0c;用于描述关于建立和运营一个网站所需的资源和步骤。这份方案书的目的是确保网站建设过程中的顺利和成功&#xff0c;并最终获得对其所期望的效果。 在撰写方案书时&#xff0c;我们应该遵循以下几个步骤&#xff1a; 一、确定网站的…

[SWPUCTF 2023 秋季新生赛]Junk Code

方法一&#xff1a;手动去除 将所有E9修改为90即可 方法二&#xff1a;花指令去除脚本 start_addr 0x0000000140001454 end_addr 0x00000001400015C7 print(start_addr) print(end_addr) for i in range(start_addr,end_addr):if get_wide_byte(i) 0xE9:patch_byte(i,0x9…

自定义类型:结构体类型

在学习完指针相关的知识后将进入到c语言中又一大重点——自定义类型&#xff0c;在之前学习操作符以及指针时我们对自定义类型中的结构体类型有了初步的了解&#xff0c;学习了结构体类型的创建以及如何创建结构体变量&#xff0c;还有结构体成员操作符的使用&#xff0c;现在我…

win+mac通用的SpringBoot+H2数据库集成过程。

有小部分大学的小部分老师多毛病&#xff0c;喜欢用些晦涩难搞的数据库来折腾学生&#xff0c;我不理解&#xff0c;但大受震撼。按我的理解&#xff0c;这种数据库看着好像本地快速测试代码很舒服&#xff0c;但依赖和数据库限制的很死板&#xff0c;对不上就是用不了&#xf…

Linux基础之进程等待

目录 一、进程等待的基本概念 二、进程等待的重要性 三、进程等待的方法 四、获取子进程status 五、options选项 一、进程等待的基本概念 进程等待是指一个进程在执行过程中暂时停止&#xff0c;并等待某个条件满足后再继续执行的状态。这种等待通常是由于某些事件需要发生…

qt按钮的autoRepeat属性和default属性

autoRepeat属性&#xff1a;按住按钮不松&#xff0c;表示一直在点击按钮 default属性&#xff1a;点击Enter键表示在点击按钮

无缝接入GPT-4o:智创聚合API平台的创新与实践

在2024年5月13日&#xff0c;美国开放人工智能研究中心&#xff08;OpenAI&#xff09;发布了最新版本的ChatGPT——GPT-4o。这一更新标志着人工智能领域的又一重大进步&#xff0c;引起了全球科技界的广泛关注。GPT-4o的“o”代表“omni”&#xff08;全能&#xff09;&#x…

动态规划算法:背包问题

背包问题概述 背包问题 (Knapsack problem) 是⼀种组合优化的 NP完全问题 。 问题可以描述为&#xff1a;给定⼀组物品&#xff0c;每种物品都有⾃⼰的重量和价格&#xff0c;在限定的总重量内&#xff0c;我们如何选择&#xff0c;才能使得物品的总价格最⾼。 根据物品的个…

【刷题】初探递归算法 —— 消除恐惧

送给大家一句话&#xff1a; 有两种东西&#xff0c; 我对它们的思考越是深沉和持久&#xff0c; 它们在我心灵中唤起的惊奇和敬畏就会日新月异&#xff0c; 不断增长&#xff0c; 这就是我头上的星空和心中的道德定律。 -- 康德 《实践理性批判》 初探递归算法 1 递归算…

AI预测体彩排3采取888=3策略+和值012路一缩定乾坤测试6月2日预测第9弹

今天继续基于8883的大底进行测试&#xff0c;今天继续测试&#xff0c;好了&#xff0c;直接上结果吧~ 首先&#xff0c;888定位如下&#xff1a; 百位&#xff1a;5,4,7,3,2,9,1,0 十位&#xff1a;4,6,5,7,2,9,1,0 个位&#xff1a;3,4,2,5,…

车流量智能监测识别摄像机

车流量智能监测识别摄像机是一项革命性的技术&#xff0c;正在为城市交通管理带来巨大改变。这种摄像机利用先进的人工智能和图像识别技术&#xff0c;能够实时监测道路上的车流量&#xff0c;并对车辆进行智能识别和分类&#xff0c;从而实现对交通流量的精准监测和管理。 与传…

Day02 设计首页导航条

设计首页导航条 导航条的样式&#xff0c;主要是从Material DesignThemes UI 拷贝过来修改的,项目用了这个UI组件库。就看项目需要什么&#xff0c;就去源码拷过来使用。 直接下载源码&#xff0c;编译运行就可以看到Demo 了 下载后且正常编译成功了&#xff0c;是能正常跑起来…

iOS——类与对象底层探索

类和对象的本质 当我们使用OC创建一个testClass类并在main函数创建它的实例对象的时候&#xff0c;OC的底层到底是什么样的呢&#xff1f; 首先&#xff0c;我们要了解OC对象的底层结构&#xff0c;那么我们就得知道&#xff1a;OC本质底层实现转化其实都是C/C代码。 使用下面…

spoon工具的常用基础操作

一些常用转换工具 1、emp表输入->excel表输出 emp表输入&#xff0c;可以进行预览查看数据有没有过来excel表输出 成功执行后&#xff0c;可以到保存的excel位置进行查看。 2、excel输入->表输出 运行转换后可以在oracle进行查看是否有成功创建这个表 3、对部门最高…

【JAVA WEB实用与优化技巧】Maven自动化构建与Maven 打包技巧

文章目录 一、MavenMaven生命周期介绍maven生命周期命令解析 二、如何编写maven打包脚本maven 配置详解setting.xml主要配置元素setting.xml 详细配置 使用maven 打包springboot项目maven 引入使用package命令来打包idea打包 三、使用shell脚本自动发布四、使用maven不同环境配…