C语言排序之快速排序

       快速排序是一种高效的排序算法。它采用了分治的策略,通过选择一个基准元素,将待排序的序列划分为两部分,一部分的元素都比基准元素小,另一部分的元素都比基准元素大,然后对这两部分分别进行快速排序,从而实现整个序列的有序排列。

以下是快速排序的基本步骤:

  1. 选择一个基准元素:通常可以选择序列的第一个元素、最后一个元素或者中间元素作为基准。
  2. 分区操作:通过一系列的比较和交换,将序列中小于基准的元素移到基准的左边,大于基准的元素移到基准的右边。
  3. 对分区后的左右两部分子序列,分别重复步骤 1 和 2,直到整个序列有序。

例如,对于序列 [12, 11, 13, 5, 6] ,选择第一个元素 12 作为基准:

  • 经过分区操作后,序列变为 [5, 11, 6, 12, 13] 。
  • 然后对 [5, 11, 6] 和 [12, 13] 分别继续进行快速排序。

快速排序的平均时间复杂度为 (O(nlogn)) ,空间复杂度为(Ologn)  。在大多数情况下,它的性能非常出色,但在最坏情况下,时间复杂度会退化为(On2)  。不过,通过合理选择基准元素,可以很大程度上避免最坏情况的发生。

快速排序的单趟排序

       通过一趟排序将数据分割成独立的两部分,其中一部分的所有数据都比另一组数据的数值小,然后再次使用相同的方法分别将这两部分再分成两部分,从而完成快速排序。

1.霍尔法

“霍尔法”通常指的是快速排序(Quick Sort)中的一种实现方式,也被称为霍尔划分。

快速排序是一种分治算法,其基本思想是通过一趟排序将待排序的序列划分成两部分,然后对这两部分分别进行排序,以实现整个序列的有序。

霍尔法的具体实现步骤如下:

  1. 选取一个基准元素 key,通常选择数组的第一个元素、中间元素或最后一个元素,也可以选择这三个元素的中间值等,然后将这个基准元素与数组第一个元素进行交换。
  2. 定义两个指针 left 和 right,left 从数组的第一个元素开始(即左边)向右遍历,right 从数组的最后一个元素开始(即右边)向左遍历。
  3. 首先,right 指针从右向左移动,找到比 key 小的值时停下来。然后,left 指针从左向右移动,找到比 key 大的值时停下来。
  4. 交换 left 和 right 所指向的值,这一步的目的是将比 key 小的值往左放,将比 key 大的值往右放。
  5. 重复步骤 3 和 4,直到 left 和 right 相遇。
  6. 当 left 和 right 相遇后,将第一个元素(即 key)与它们相遇位置的值交换。此时,key 左边的元素都比它小,右边的元素都比它大,但左右两边的子序列并不一定是有序的。
  7. 对 key 左边的子序列和右边的子序列,分别重复上述步骤,进行递归排序,直到子序列不存在或者只有一个元素时结束递归。

key——>基准下标值;

指针 left指向最左端的元素也就是第一个元素(找比key大的元素)

        right指向最右端的元素也就是最后一个元素(找比key小的元素)

<1>

      left记录左下标,从左向右遍历

      right记录右下标,从右向左遍历,以第一个数作为基准值

    5       2     7      3     1     4     8   6

^left                                                                                                            ^right                         

 key=5                                                                       

<2>

      right先出发,找比key小的值,找到停下

    5       2     7      3     1     4     8   6

^left                                                                                ^right

key=5                                                                       

<3>

      left出发,找比key大的值,找到停下并与right进行交换

    5       2     7      3     1     4     8   6

                                       ^left                                         ^right                                                                    key=5                                                                       

    5       2     4      3     1     7     8   6

                                       ^left                                         ^right                                                                    key=5                                                                       

<4>

     重复步骤<2>,<3>,直到left与right指向同一个数

    5       2     4      3     1     7     8   6

                                                                      ^right   

                                                                       ^left                                                                                                                          key=5     

<5>

    将left/right与key交换,完成单趟排序

    1       2     4      3     5     7     8   6

                                                                      ^right   

                                                                       ^left                                                                                                                          key=5     

总的来说就是把比key小的放在key左边,比key大的放在key右边,以下是代码实现单趟排序

#include <stdio.h>// 交换两个整数的值
void swap(int* a, int* b) {int tmp = 0;tmp = *a;*a = *b;*b = tmp;
}// 获取数组中 begin、mid、end 三个位置元素的中间值,并与数组第一个元素交换
int getMid(int* a, int begin, int end) {int mid = (begin + end) / 2;if (a[begin] > a[mid]) {if (a[mid] > a[end]) {return mid;} else if (a[end] > a[begin]) {return begin;} else {return end;}} else {if (a[begin] > a[end]) {return begin;} else if (a[end] > a[mid]) {return mid;} else {return end;}}
}// 霍尔法快速排序的核心函数
void quickSortHoare(int* a, int begin, int end) {int left = begin;int right = end;if (left >= right) {return;}int mid = getMid(a, begin, end);swap(&a[begin], &a[mid]); 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); 
}// 打印数组函数
void printArray(int arr[], int size) {for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}int main() {int arr[] = {12, 11, 13, 5, 6};int n = sizeof(arr) / sizeof(arr[0]);printf("排序前的数组为:");printArray(arr, n);quickSortHoare(arr, 0, n - 1); printf("排序后的数组为:");printArray(arr, n);return 0;
}

2.挖坑法

        与霍尔法不同的是,将key基准值用变量保存起来,然后将key空出来,也就是形成一个坑,left先指向这个坑right找比key小的值放进坑里,并将自己的位置设为坑,left找比key大的放进坑里,自己又变成坑,如此循环。

<1>

     先定义变量key,存储数组第一个值作为key,此时left指向坑

52731486

  ^key       ^left                                                                                            ^right

<2>

     right开始找比key小的数,放进坑里,自己变为坑

52731486

  ^key       ^left                                                                 ^right

54273186

  ^key       ^left                                                                 ^right

<3>

     left开始找比key大的数,同上

54273186

  ^key                                   ^left                                      ^right

54231786

  ^key                                   ^left                                   ^right

<4>

     重复步骤<2>,<3>,直到left与right相遇

54213    786

  ^key                                                              ^left                 

                                                                       ^right

<5>

     将key放在相遇的坑里,排序完毕,将key下标返回

4213    5786

                                                                       ^left   

                                                                       ^key 

                                                                       ^right

以下是代码实现:

int PartSort(int *arr,int left,int right){
int key=arr[left];
int hole=left;
while(left<right){
while(arr[right]>=key&&left<right){
right--;
}
arr[hole]=arr[right];
hole=right;
while(left<right&&arr[left]<=key){
left++;
}
arr[hole]=arr[left];
hole=left;
}
arr[hole]=key;
return hole;
}

 3.前后指针

       将数组的第一个元素作为基准值key,定义前指针prev指向数组第一个元素,后指针cur指向数组第二个数,由cur遍历数组,找出比key小的数,prev在cur找到后向后移动一位并与cur交换,保证较小数在prev之前。

<1>

开始时prev指向数组第一个元素,cur指向第二个元素,此时cur的值比key小,prev向后移动一位后与cur交换,无变化

    5       2     7      1     3     4     8   6

^prev          ^cur                     

 key=5        ^prev+1                                 

<2>

cur找到比key大的数此时cur继续向后移动,prev不变

    5       2     7      1     3     4     8   6

                                      ^cur                     

 key=5        ^prev                                 

<3>

cur找到比key小的数此时prev向后移动一位,并与cur交换

    5       2     7      1     3     4     8   6

                                                      ^cur                     

 key=5        ^prev                   

 

    5       2     7      1     3     4     8   6

                                                      ^cur                     

 key=5                          ^prev                   

 
    5       2     1      7     3     4     8   6

                                                      ^ cur                   

 key=5                          ^prev                   

<4>

     重复步骤<2>,<3>,直到cur完成遍历

    5       2     1      7     3     4     8   6

                                                                       ^cur                     

 key=5                                           ^prev                   

                                        交换3和7

    5       2     1      3     7     4     8   6

                                                                                       ^cur                     

 key=5                                                          ^prev                   

                                       交换4和7

    5       2     1      3     4     7     8  6

                   

 key=5                                                           ^prev                   

 

<5>

     cur完成遍历后,将prev与key进行交换,返回key的下标

    4       2     1      3     5     7     8  6

                                                                         key=5                                                                                                                      ^prev                   

 以下是代码实现:

int PastSort(int *arr,int  left,int right){
int key=left;
int prev=left;
int cur=left+1;
while(cur<=right){
if(arr[cur]<=arr[key]&&++prev!=cur){
swap(&arr[cur],&arr[prev];
}
cur++;
}
swap(&arr[key],&arr[prev]);
return prev;
}

 其实快速排序最核心的代码就是单趟排序,以上三种是单趟排序的三种方法,大家任选一种牢记就好,主要函数的代码如下(递归实现)

void QuickSort(int *arr,int begin,int end){
if(begin>=end){
return;
}
int key=PartSort(arr,begin,end);
QuickSort(arr,begin,key-1);
QuickSort(arr,key+1,end);
}

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

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

相关文章

前端开发工具

Lodash 有普通的 CommonJS 版本&#xff08;通常称为 lodash&#xff09;和 ES6 模块版本&#xff08;称为 lodash-es&#xff09;。它们的主要区别包括&#xff1a; 模块化&#xff1a;lodash 是传统的 CommonJS 模块&#xff0c;可使用 require 或 import 引入&#xff1b;lo…

2024年,搞AI就别卷模型了

你好&#xff0c;我是三桥君 2022年11月30日&#xff0c;OpenAI发布了一款全新的对话式通用人工智能工具——ChatGPT。 该工具发布后&#xff0c;仅用5天时间就吸引了100万活跃用户&#xff0c;而在短短2个月内&#xff0c;其活跃用户数更是飙升至1亿&#xff0c;成为历史上增…

ARP协议介绍与ARP协议的攻击手法

ARP是什么&#xff1f; ARP是通过网络地址&#xff08;IP&#xff09;来定位机器MAC地址的协议&#xff0c;它通过解析网络层地址&#xff08;IP&#xff09;来找寻数据链路层地址&#xff08;MAC&#xff09;的网络传输协议。 对个定义不能理解的话&#xff0c;可以结合 TCP/I…

《恋与深空》2.0上线肉鸽模式,乙游玩家会买账吗?

乙游和肉鸽&#xff0c;看似八竿子打不着的两个赛道&#xff0c;被叠纸给融合起来了。 根据《恋与深空》官方消息&#xff0c;即将在7月15日更新的2.0交错视界版本中&#xff0c;会上线全新常驻玩法“混沌深网”&#xff0c;配置高随机性Roguelike模式&#xff0c;并搭载了管理…

理想文档发布了~一个集合了多个优秀开源项目的在线云文档

两年前我做了一个简单的在线云文档项目&#xff0c;选择了开源的思维导图、白板、流程图、幻灯片等项目&#xff0c;在它们基础上添加了云存储的功能&#xff0c;然后写了一个简单的工作台管理文件夹和文件&#xff1a; 放在了自己的个人网站上使用&#xff0c;同时写了一篇水文…

【Leetcode 每日一题】349. 两个数组的交集

给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,2,1], nums2 [2,2] 输出&#xff1a;[2]示例 2&#xff1a; 输入&#xff1a;nums…

[web]-代码审计-运维失误

打开页面可以看到如下&#xff1a; 1、查看源代码&#xff0c;发现验证码功能是正常生成的随机的&#xff0c;输入也没有过滤&#xff0c;无法采用爆破。 2、根据题目提示运维失误&#xff0c;使用dirsearch扫描&#xff0c;发现提交的地址check.php, 使用php5、.bak可以打开&…

2.The DispatcherServlet

The DispatcherServlet Spring的Web MVC框架与许多其他Web MVC框架一样&#xff0c;是请求驱动的&#xff0c;围绕一个中央Servlet&#xff08;即DispatcherServlet&#xff09;设计&#xff0c;该Servlet将请求分派给控制器&#xff0c;并提供其他功能以促进Web应用程序的开发…

创建I/O文件fopen

#include〈stdio.h〉 int mian(int argc,char *argv[]){ FILE *fp;//结构体fp fpfopen&#xff08;“1.txt”&#xff0c;“r”&#xff09;; }

程序的控制结构——if-else语句(双分支结构)【互三互三】

目录 &#x1f341; 引言 &#x1f341;if-else语句&#xff08;双分支结构&#xff09; &#x1f449;格式1&#xff1a; &#x1f449;功能&#xff1a; &#x1f449;程序设计风格提示&#xff1a; &#x1f449;例题 &#x1f449;格式2&#xff1a; &#x1f449;…

Monaco 使用 ColorProvider

Manco 中可以使用调色板对色值进行修改&#xff0c;首先看一下调色版效果。 调色板是 Monaco-Editor 中一个特别的组件&#xff0c;通过两个方法实现呼出调色板&#xff0c;provideColorPresentations 显示调色窗口&#xff0c;provideDocumentColors 监听页面的变更&#xff0…

如何将libwebsockets库编译为x86架构

在之前的文章中&#xff0c;我们已经详细介绍了如何交叉编译libwebsockets并将其部署到ELF 1开发板上。然而在调试阶段&#xff0c;发现将libwebsockets在Ubuntu环境下编译为x86架构可能更为方便和高效。 通过在主机环境中编译运用x86架构下的libwebsockets库&#xff0c;可以…

阿里ChatSDK使用,开箱即用聊天框

介绍&#xff1a; 效果&#xff1a;智能助理 ChatSDK&#xff0c;是在ChatUI的基础上&#xff0c;结合阿里云智能客服的最佳实践&#xff0c;沉淀和总结出来的一个开箱即用的&#xff0c;可快速搭建智能对话机器人的框架。它简单易上手&#xff0c;通过简单的配置就能搭建出对…

Flowable工作流引擎核心事件详细解释说明

Flowable工作流引擎核心事件详细解释说明 流程执行事件 需要了解全部详细事件的请看这个链接Flowable&#xff08;一个开源的工作流和业务流程管理引擎&#xff09;中与事件相关的一些核心概念 流程开始和结束事件 PROCESS_STARTED&#xff1a;标记流程实例的开始。PROCESS…

公益快报 | 中科亿海微以企业奖学金为纽带,深化校企合作

近日&#xff0c;为回报母校、激励湖南大学机器人视觉感知与控制技术国家工程研究中心广大学生&#xff0c;中科亿海微电子科技&#xff08;苏州&#xff09;有限公司&#xff08;简称“中科亿海微”&#xff09;捐赠设立企业奖学金。此项奖学金的设立标志着校企合作迈向全方位…

【C++】C++中struct结构体和class类的区别

在C中&#xff0c; struct 和 class 在很多方面都非常相似&#xff0c;它们都可以包含数据成员&#xff08;变量&#xff09;和成员函数&#xff08;方法&#xff09;。然而&#xff0c;它们之间还是存在一些关键的区别&#xff1a; 1. 默认访问权限 struct 的成员默认是 pub…

实现组件存储 WinSxS 文件夹解析

目录 背景 目录名的组成 解析目录结构 更新&总结 文章出处链接&#xff1a;[https://blog.csdn.net/qq_59075481/article/details/140385969]. 背景 WinSxS 文件夹位于 Windows 文件夹中&#xff0c;例如 C: \Windows\WinSxS。它是 Windows 组件存储文件的位置。 Wind…

深入理解Spring Boot中的日志框架选择

深入理解Spring Boot中的日志框架选择 大家好&#xff0c;我是微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 1. 背景与需求 在开发和运维中&#xff0c;日志是不可或缺的重要组成部分。Spring Boot作为一个流行的Java开…

idea启动vue项目一直卡死在51%,问题分析及其如何解决

如果你的项目也一直卡在百分之几十&#xff0c;你可以参考下面的方法&#xff0c;试一试能否解决 问题描述&#xff1a; 通过在idea终端中输入命令 npm run serve 启动vue项目&#xff0c;启动进程一直卡在51% 如何解决&#xff1a; 检查 < template > 标签中的html内容…

深度学习中的超参管理方法:argparse模块

在深度学习方法中我们不可避免地会遇到大量超参数如&#xff08;batch_size、learning_rate等&#xff09;。不同的超参数组合可以得到不同的训练/测试结果。所以在训练和测试过程中我们需要不断调整超参数获得理想的结果&#xff08;炼丹&#xff09;&#xff0c;如果每一次去…