数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意)

上次讲了选择排序和堆排序:数据结构排序——选择排序与堆排序

今天就来快排和冒泡


文章目录

  • 1.快排
    • 1.1基本介绍
    • 1.2不同的分区方法及代码实现
      • 1.2.1Hoare版
      • 1.2.2挖坑版
      • 1.2.3 前后指针版
    • 1.3快排的优化
      • 1.3.1三数取中选key
      • 1.3.2递归到小的子区间时,可以考虑使用插入排序
      • 1.3.3大量重复数据采用三路划分
    • 1.4快排非递归
    • 2.冒泡排序


1.快排

1.1基本介绍

快速排序(Quick Sort)是一种常用的排序算法,它是由英国计算机科学家Tony Hoare于1959年发明的。快速排序的基本思想是通过分治的策略将一个数组分成两个子数组,然后分别对这两个子数组进行排序。具体步骤如下:

  1. 选择一个基准元素(通常是数组的第一个元素,右边先行)。
  2. 将数组分割成两部分,使得左边的元素都小于等于基准元素,右边的元素都大于基准元素。这个过程叫做分区(Partition)
  3. 对分割后的两个子数组分别重复步骤1和2(利用递归),直到子数组的大小为1或0,此时数组已经有序

==优化:==如果本身就很接近有序,那效率就慢了(一个逆序变升序,keyi就一直在左边,递归也只有右侧,所以选择三个数来找中间大小,能让keyi尽量向数组中间靠近),所以设计了Getmid函数来取中间大小的数

1.2不同的分区方法及代码实现

1.2.1Hoare版

使用两个索引,一个从数组的左边开始向右移动,另一个从数组的右边开始向左移动,直到它们相遇。在这个过程中,如果左指针指向的元素大于基准元素且右指针指向的元素小于基准元素,则交换这两个元素。当两个指针相遇时,将基准元素(keyi指向的)与相遇位置的元素交换,这样基准元素就归位了

请添加图片描述

void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}int GetMid(int* a,int left, int right)//找中间的
{// a[left]      a[mid]           a[right]int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right])  // mid是最大值{return left;}else{return right;}}else // a[left] > a[mid]{if (a[left] < a[right]){return left;}else if (a[mid] < a[right]){return right;}else{return mid;}}
}
//一次排序
int OneSort1(int* a, int left, int right)//使keyi位置的元素处于正确的位置上
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);//现在left处是三者的中间值了//左边第一个为key,右边先走才能保证相遇处比啊a[keyi]小int keyi = left;while (left < right){while (a[right] >= a[keyi] && left < right)//右边先走{right--;}while (a[left] <= a[keyi] && left < right)//左侧找大的{left++;}Swap(&a[left], &a[right]);//找到一个大和一个小的就交换}Swap(&a[keyi], &a[left]);//把keyi放相遇位置return left;//返回相遇的索引
}void QuickSort(int* a, int begin, int end)//升序
{if (begin >= end){return;}// [begin, keyi-1] keyi [keyi+1, end]int keyi = OneSort1(a, begin, end);//找到keyi索引,才能分左右QuickSort(a, begin, keyi - 1);//左侧QuickSort(a, keyi + 1, end);//右侧
}int main()
{int a[] = { 6,1,2,7,9,3,4,5,10,8 };printf("排序前:");for (int i = 0; i < sizeof(a) / sizeof(int); i++){printf("%d ", a[i]);}printf("\n");QuickSort(a, 0,sizeof(a) / sizeof(int)-1);printf("排序后:");for (int i = 0; i < sizeof(a) / sizeof(int); i++){printf("%d ", a[i]);}printf("\n");return 0;
}

请添加图片描述

1.2.2挖坑版

选择基准元素后,先将基准元素保存到临时变量中,然后使用左右索引的方式找到需要交换的元素,将这些元素填入基准元素的位置,最后将基准元素填入最后一个空出来的位置

请添加图片描述

int OneSort2(int* a, int left, int right)//挖坑
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);//现在left处是三者的中间值了int key = a[left];//保存基准元素int hole = left;//储存坑下标,不能直接赋值为0while (left < right){while (a[right] >= key && left < right)//右边先走,没有等号两侧出现相同值会死循环{right--;}//找到了就去赋值到第一个“坑”a[hole] = a[right];hole = right;//更新坑while (a[left] <= key && left < right)//左侧找大的{left++;}a[hole] = a[left];hole = left;}Swap(&key, &a[left]);//把keyi放相遇位置return left;//返回相遇的索引
}

1.2.3 前后指针版

pre指向第一个,cur指向下一个。cur找小后,pre++,然后交换(做到大的向后推),最后cur出数组结束

prev的情况有两种:

  1. 在cur还没遇到比key大的值时候,prev紧跟着cur
  2. 在cur还遇到比key大的值时候,prev在比key大的一组值的前面

本质是把一段大于key的区间,往后推,同时小的甩到左边去

请添加图片描述
请添加图片描述

int OneSort3(int* a, int left, int right)//挖坑
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int keyi = left;int pre = left;int cur = left + 1;while (cur <= right){if (a[cur] < a[keyi]){pre++;Swap(&a[cur], &a[pre]);}cur++;}Swap(&a[pre], &a[keyi]);return pre;
}

1.3快排的优化

1.3.1三数取中选key

从待排序数组的首、尾和中间位置各选取一个元素,然后取它们的中间值作为基准元素,确保选择的基准元素相对中间位置的元素更为接近

代码在Hoare版已经展示过了

int GetMid(int* a,int left, int right)//找中间的
{// a[left]      a[mid]           a[right]int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right])  // mid是最大值{return left;}else{return right;}}else // a[left] > a[mid]{if (a[left] < a[right]){return left;}else if (a[mid] < a[right]){return right;}else{return mid;}}
}

1.3.2递归到小的子区间时,可以考虑使用插入排序

当递归到小的子区间时,可以考虑使用插入排序来优化快速排序。因为插入排序在小规模数据上的排序效率通常比快速排序更高==(当数量比较少时,也没必要在递归好几层了)==

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 (a[end] > tmp){a[end + 1] = a[end];}else{break;}end--;}a[end + 1] = tmp;}
}int OneSort3(int* a, int left, int right)//挖坑
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int keyi = left;int pre = left;int cur = left + 1;while (cur <= right){if (a[cur] < a[keyi]){pre++;Swap(&a[cur], &a[pre]);}cur++;}Swap(&a[pre], &a[keyi]);return pre;
}void QuickSort(int* a, int begin, int end)//升序
{if (begin >= end){return;}if ((end - begin + 1) > 10){// [begin, keyi-1] keyi [keyi+1, end]int keyi = OneSort3(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);}else{//用插入排序InsertSort(a + begin, end - begin + 1);}
}

1.3.3大量重复数据采用三路划分

快速排序的三路划分通过将数组分为小于等于=和大于基准元素的三个部分==,有效地处理了重复元素,提高了算法的效率

快速排序的三路划分算法的基本思想是使用三个指针/下标来维护三个区域:小于基准元素的区域、等于基准元素的区域和大于基准元素的区域。在每一次遍历数组的过程中,将数组分为这三个区域,并将指针移动到合适的位置。最终,数组会被划分成小于基准元素、等于基准元素和大于基准元素的三个部分

基本步骤:

请添加图片描述

void QuickSort(int* a, int left, int right)
{if (left >= right){return;}int begin = left;int end = right;int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int cur = left + 1;int key = a[left];//储存一下,后面比较来用,用a[left]会被替代while (cur <= right){if (a[cur] < key){Swap(&a[cur], &a[left]);cur++;left++;}else if (a[cur] == key){cur++;}else{Swap(&a[cur], &a[right]);right--;}}QuickSort(a, begin, left - 1);QuickSort(a, right + 1, end);
}

1.4快排非递归

快速排序的非递归实现通常使用栈来模拟递归调用的过程。在快速排序的递归实现中,每次递归调用都将函数参数压入栈中,然后在递归返回时再从栈中弹出参数(二者性质相同)。

非递归实现则需要手动维护一个栈,将需要处理的子序列的起始和结束位置压入栈中,然后循环处理栈中的元素,直到栈为空(递归一次就用一次)

void QuickSortNonR(int* a, int begin, int end)//利用栈,先想好先排左侧再排右侧
{ST st;STInit(&st);STPush(&st,end);//把各个区间的两侧的索引(整形)插入进Stack中STPush(&st,begin);//栈(后进先出),先排左侧所以后入左while (!STEmpty(&st)){int left = STTop(&st);STPop(&st);int right = STTop(&st);STPop(&st);int keyi = OneSort1(a, left, right);//得到基准元素下标// [begin, keyi-1] keyi [keyi+1, end]if (keyi + 1 < right)//等于说明就一个,没必要{STPush(&st, right);STPush(&st, keyi);}if (left < keyi-1){STPush(&st, keyi-1);STPush(&st, left);}}STDestroy(&st);
}

2.冒泡排序

它重复地遍历要排序的列表,比较每对相邻的元素,并按照大小顺序交换它们。重复这个过程直到整个列表排序完成

步骤:

  1. 从列表的第一个元素开始,依次比较相邻的两个元素,如果它们的顺序不正确就交换它们。
  2. 继续遍历列表,重复上述比较和交换的过程,直到没有任何一对相邻元素需要交换为止。
  3. 列表排序完
void BobbleSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){for (int j = 0; j < n - 1 - i; j++){if (a[j] > a[j + 1]){Swap(&a[j], &a[j + 1]);}}}
}

好啦,这次内容就到这里啦,下次带来归并排序,感谢大家支持!!!

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

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

相关文章

正面PK智驾,华为与博世「硬扛」

12月20日&#xff0c;随着奇瑞星纪元ES的亮相上市&#xff0c;华为与博世&#xff0c;分别作为新旧时代的供应商角色&#xff0c;首次在高阶智驾赛道进行正面PK。 11月28日&#xff0c;奇瑞和华为合作的首款车型智界S7上市&#xff0c;作为星纪元ES的兄弟车型&#xff0c;搭载华…

STL之list

目录 list定义和结构 list容器模板接受两个参数&#xff1a; list容器的特点 双向性 动态大小 不连续存储 实例 代码输出 需要注意的点 list常用函数 代码示例 list定义和结构 list的使用频率不高&#xff0c;在做题时极少遇到需要使用list的情景。 list是一种双向…

2023 年崭露头角的七款不为人知的 Linux 发行版

今年有哪些成功的发行版发布呢&#xff1f; 让我重点介绍最好的几个。 这些发行版在 2023 年引起了人们的关注&#xff01; 每年我们都会推出一些令人兴奋的新发行版&#xff0c;它们尝试以不同的方式工作&#xff0c;或者提供一些有意义的东西&#xff0c;而不仅仅是“又一个发…

面试官:请说一下Mysql中count(1)、count(*)以及count(列)的区别?

近期在Review项目代码时&#xff0c;发现同事们在查询MySQL行数时存在多样的方式&#xff0c;有的使用COUNT(1), 有的用COUNT(id), 还有人选择了COUNT(*)。这混杂的选择引发了我的思考。当然这三种count的方式也是众说纷纭&#xff0c;其中最大的分歧点就是COUNT(*)和COUNT(1)查…

数据库mysql no.3

1.排序查询 order by 排序列表 【asc/desc】 排序列表&#xff1a;可以是单个字段、多个字段、表达式、函数、别名。 asc 升序 desc 降序 如果没有写那就是默认升序 2.常见函数 select 函数名&#xff08;&#xff09;&#xff1b; 定义&#xff1a;函…

创建mysql普通用户

一、创建mysql普通用户的原因&#xff1a; 权限控制&#xff1a;MySQL的权限系统允许您为每个用户分配特定的权限。通过创建普通用户&#xff0c;您可以根据需要为每个用户分配特定的数据库和表权限&#xff0c;而不是将所有权限授予一个全局管理员用户。这有助于提高数据库的…

[算法与数据结构][c++]:Static关键字和全局变量

Static关键字和全局变量 1. 生命周期、作用域和初始化时机2. 全局变量3. Static 关键字3.1 面向过程3.1.1 静态全局变量3.1.2 静态局部变量&#xff08;单例中会使用&#xff09;3.1.3 静态函数 3.2 面向对象3.2.1 类内静态成员变量3.2.2 类内静态成员函数 Reference 写在前面&…

Taro+vue3 实现选座位 功能 以及座位显示

1.类似选座位那种功能 我的功能座位 不是html元素 而是 座位图片 都是图片 const onConfirm () > {// const area_arr selectedSeat.value.map((item) > {// return item.areaId;// });// const abc isRepeat(area_arr);// if (!abc) {// Taro.showToast({//…

水经微图安卓版APP正式上线!

在水经微图APP&#xff08;简称“微图APP”&#xff09;安卓版已正式上线&#xff01; 在随着IOS版上线约一周之后&#xff0c;安卓版终于紧随其后发布了。 微图安卓版APP下载安装 自从IOS版发布之后&#xff0c;就有用户一直在问安卓版什么时候发布&#xff0c;这里非常感谢…

【复盘】quartz job 停止调度原因调查解决

场景 项目中的定时任务由 quartz 调度&#xff0c;划分了多个模块。测试组发现了其中A模块的定时任务不执行了&#xff0c;这就让人很头疼。 排查 1、job 不执行的原因有可能是 quartz 线程池满导致的问题 2、代码中未正确配置&#xff0c;上一个job 未执行完成下一个job 继…

Windows下安装mariadb10.5数据库及配置详细教程

1、简介 MariaDB数据库管理系统是一款MySQL的替代数据库。MariaDB由MySQL的创始人麦克尔维德纽斯主导开发&#xff0c;是可扩展的&#xff0c;可靠的SQL服务器的合乎逻辑的选择&#xff0c;MariaDB 10.5 是 MariaDB 当前的稳定系列。 2、下载 下载地址&#xff1a;Download M…

Linux和windows进程同步与线程同步那些事儿(五):Linux下进程同步

Linux和windows进程同步与线程同步那些事儿&#xff08;一&#xff09; Linux和windows进程同步与线程同步那些事儿&#xff08;二&#xff09;&#xff1a; windows线程同步详解示例 Linux和windows进程同步与线程同步那些事儿&#xff08;三&#xff09;&#xff1a; Linux线…

Linux基础知识(文件类型、目录、文件权限、权限修改)

Linux基础知识&#xff08;文件类型、目录、文件权限、权限修改&#xff09; 文章目录 Linux基础知识&#xff08;文件类型、目录、文件权限、权限修改&#xff09;0x01 文件类型0x02 常用目录0x03 文件系统权限0x04 权限修改 0x01 文件类型 Linux中的文件分类主要基于其内容和…

基于ssm快餐店点餐结算系统的设计与实现+vue论文

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装快餐店点餐结算系统软件来发挥其高效地信息处理的作用&…

前端计算精度丢失问题

// 精度丢失异常数据汇总 10.3950 * 3935.00 期望40904.33&#xff0c; 异常结果40904.32 7.3950 * 3835.00 期望28359.83&#xff0c; 异常结果28359.82 11.777 * 4215 期望49640.06&#xff0c; 异常结果49640.05 12.445 * 4005 期望49842.23&#xff0c;异常结果49842.2…

区间预测 | Matlab实现CNN-LSTM-KDE的卷积长短期神经网络结合核密度估计多变量时序区间预测

区间预测 | Matlab实现CNN-LSTM-KDE的卷积长短期神经网络结合核密度估计多变量时序区间预测 目录 区间预测 | Matlab实现CNN-LSTM-KDE的卷积长短期神经网络结合核密度估计多变量时序区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.CNN-LSTM-KDE多变量时间序列区…

使用 Windbg 分析软件异常时的诸多细节与技巧总结

目录 1、dump文件 1.1、dump文件的生成方式 1.2、dump文件的大小 2、pdb符号文件 2.1、pdb文件的路径设置 2.2、pdb文件的时间戳与名称问题 2.3、如何确定要找哪些pdb文件&#xff1f; 3、使用Windbg静态分析dump文件以及动态调试程序的一般步骤 4、确定发生异常或崩溃…

能力素质模型在企业中的应用

在我国经济飞速发展中&#xff0c;企业也获得了越来越多的业务。业务的增加带来的是人力管理上的不足&#xff0c;很多企业一味的着眼于业务的营收&#xff0c;而忽视了人力资源管理上的跟进&#xff0c;人力资源管理水平逐渐跟不上业务的迅猛发展&#xff0c;所以企业需要一套…

程序员的海外营收

在素材图片平台生成AI图片&#xff0c;通过下载赚取版税。 Wirestock这个平台能让你方便地把作品上传到多个素材市场&#xff0c;比如Adobe Stock、Shutterstock和iStock等。 你只需要上传一次&#xff0c;就能在多个平台上销售。 你可以开作坊、网上教程&#xff0c;或者在…

使用Netty实现Socket网络编程

** Netty初步讲解和认识 ** 网络通信模型 Netty支持多种网络通信模型&#xff0c;包括传统的阻塞I/O、非阻塞I/O、多路复用I/O和异步I/O。其中&#xff0c;非阻塞I/O和多路复用I/O是Netty的核心特性。 非阻塞I/O&#xff1a;Netty通过使用Java的NIO&#xff08;New I/O&…