数据结构排序——详解快排及其优化和冒泡排序(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.hqwc.cn/news/339180.html

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

相关文章

数据结构之单调栈、单调队列

今天学习了单调栈还有单调队列的概念和使用&#xff0c;接下来我将对其定义并配合几道习题进行讲解&#xff1a; 首先先来复习一下栈与队列&#xff1a; 然后我们来看一下单调栈的定义&#xff1a; 单调栈中的元素从栈底到栈顶的元素的大小是按照单调递增或者单调递减的关系进…

如何在SpringBoot中优雅地重试调用第三方API?

1.引言 在实际的应用中,我们经常需要调用第三方API来获取数据或执行某些操作。然而,由于网络不稳定、第三方服务异常等原因,API调用可能会失败。为了提高系统的稳定性和可靠性,我们通常会考虑实现重试机制。 2.重试机制的必要性 第三方API调用可能面临各种不可预测的问题…

c++最值查找

目录 min和max函数 min_element和max_element 例 nth_element函数 例 例题 题目描述 输入描述 输出描述 解 min和max函数 只能传入两个值或一个列表 时间复杂度为O(1),数组O(n)&#xff0c;n为元素个数 min_element和max_element min_element(st,ed)返回地址[st,…

鸿蒙开发(三)鸿蒙DevEco4.x开发环境搭建

上篇我们使用DevEco3.x的版本进行了鸿蒙开发环境的搭建&#xff0c;并且成功运行了第一行代码-Hello World。本篇介绍下如何基于企业版DevEco4.x版本搭建开发环境。 目录 一、下载和安装DevEco4.x 二、配置环境 三、配置sdk 1、解压缩sdk 2、配置sdk目录 四、创建模拟器…

Navicat 技术干货 | 为 MySQL 表选择合适的存储引擎

MySQL 是最受欢迎的关系型数据库管理系统之一&#xff0c;提供了不同的存储引擎&#xff0c;每种存储引擎都旨在满足特定的需求和用例。在优化数据库和确保数据完整性方面&#xff0c;选择合适的存储引擎是至关重要的。今天&#xff0c;我们将探讨为 MySQL 表选择合适的存储引擎…

uniapp中实现H5录音和上传、实时语音识别(兼容App小程序)和波形可视化

文章目录 Recorder-UniCore插件特性集成到项目中调用录音上传录音ASR语音识别 在uniapp中使用Recorder-UniCore插件可以实现跨平台录音功能&#xff0c;uniapp自带的recorderManager接口不支持H5、录音格式和实时回调onFrameRecorded兼容性不好&#xff0c;用Recorder插件可避免…

(超详细)2-YOLOV5改进-添加SimAM注意力机制

1、在yolov5/models下面新建一个SimAM.py文件&#xff0c;在里面放入下面的代码 代码如下&#xff1a; import torch import torch.nn as nnclass SimAM(torch.nn.Module):def __init__(self, e_lambda1e-4):super(SimAM, self).__init__()self.activaton nn.Sigmoid()self…

将WebGL打包的unity项目部署至Vue中

一、webgl打包 创建一个空项目&#xff08;或者直接使用现成的项目都可以&#xff09;这里以该空项目为例子 注意&#xff1a; 如果你的unity项目中有文字&#xff0c;不需要使用unity默认的字体&#xff0c;需要更改它的字体&#xff0c;否则在最后生成的页面中会显示不出来…

el-dialog 内部添加固定定位 ,背景颜色超出问题 (粘性定位)

使用固定定位会出现背景颜色超出的问题 position: fixed;left: 0;bottom: 0;width: 80%;height: 50px;line-height: 50px;text-align: center;background-color: #fff;overflow: hidden; 解决办法 粘性定位 position: sticky;bottom: 0;width: 100%;height: 50px;line-height…

高德地图Web服务使用方法——电子围栏

1 高德地图Web服务 1.1 添加Key 注册高德地图&#xff0c;进入控制台&#xff0c;创建新应用&#xff0c;添加Key&#xff0c;选择Web服务&#xff0c;不添加域名白名单&#xff0c;勾选同意政策。 刷新界面&#xff0c;记住获取到的Key。 1.2 下载安装Postman https://www…

MYSQL篇--事务机制高频面试题

事务 1 什么是数据库事务&#xff1f; 事务是一个不可分割的数据库操作序列&#xff0c;也是数据库并发控制的基本单位&#xff0c;其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作&#xff0c;要么都执行&#xff0c;要么都不执行。…

模型评估:ROC曲线

二值分类器&#xff08;Binary Classifier&#xff09;是机器学习领域中最常见也是应用最广泛的分类器。评价二值分类器的指标很多&#xff0c;比如precision、recall、F1 score、P-R曲线等。相比而言&#xff0c;ROC曲线有很多优点&#xff0c;经常作为评估而知分类器最重要的…