数据结构——排序算法第二幕(交换排序:冒泡排序、快速排序(三种版本) 归并排序:归并排序(分治))超详细!!!!

在这里插入图片描述

文章目录

  • 前言
  • 一、交换排序
    • 1.1 冒泡排序
    • 1.2 快速排序
      • 1.2.1 hoare版本 快排
      • 1.2.2 挖坑法 快排
      • 1.2.3 lomuto前后指针 快排
  • 二、归并排序
  • 总结

前言

继上篇学习了排序的前面两个部分:直接插入排序选择排序
今天我们来学习排序中常用的交换排序以及非常稳定的归并排序
快排可是有多种方法的,高速列车,即将发车,fellow me

一、交换排序

交换排序基本思想:
所谓交换,就是根据序列中两个记录键值的比较结果对换这两个记录在序列中的位置
交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动

1.1 冒泡排序

冒泡排序是一种最基础的交换排序。之所以叫做冒泡排序,因为每一个元素都可以像小气泡一样,根据自身大小一点一点向数组的一侧移动。这个算法在平常算法题中基本不用(因为太慢了),只能说具有教学意义。
就简单实现一下代码啦

void BubbleSort(int* a,int n)
{int exchange = 0;for (int i = 0; i < n; i++){for (int j = 0; j < n - 1 - i; j++){if (a[j] > a[j + 1]){exchange = 1;swap(a[j], a[i]);}}if (!exchange)break;}
}

冒泡排序的特性总结
时间复杂度: O(N^2)
空间复杂度: O(1)

1.2 快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

快速排序实现主框架:
其实快排主要就是递归,把一个大区间不断划分成子区间

void QuickSort(int* a, int left, int right)
{if (left >= right){return;}//_QuickSort用于按照基准值将区间[left,right)中的元素进行划分int meet = _QuickSort(a, left, right);QuickSort(a, left, meet - 1);QuickSort(a, meet + 1, right);
}

1.2.1 hoare版本 快排

算法思路 :
创建左右指针,确定基准值
从右向左找出比基准值小的数据,从左向右找出比基准值大的数据,左右指针数据交换,进入下次循环
其实就是确定一个数为基准值,然后根据基准值,把当前区域的数据分成两部分,左边小于基准值,右边大于基准值
然后再递归到分好的区域,继续重复操作,一个大问题划分成无数个一样的子问题。

讲到这里大概都知道怎么写啦,上代码

int _QuickSort(int* a, int left, int right)
{int keyi = left;   //  先定义区间第一个数为基准值   left++;  // left++  对除基准值以外的数据进行判断操作   while (left <= right)   //   只要left<right  就继续循环   {					//   我们这里right是找比基准值小的数据   left是找比基准值大的数据   然后进行调换  while (left <= right && a[right] > a[keyi])//  当右边的值大于基准值时  right--  直到找到小于基准值的再跳出循环{right--;}while (left <= right && a[left] < a[keyi])//  当左边的数据小于基准值时  left++  直到找到大于基准值的再跳出循环{left--;}    //   两个循环跳出后  left对应的数据大于基准值  right对应的数据小于基准值  //   对数据进行调换   这样就把小的放在左边  大的放在右边   if (left <= right)   {swap(a[right--], a[left++]);}}   //   当left>right的时候  交换最开始的基准值的位置 这个时候新的基准值就取right  swap(a[right], a[keyi]);return right;    //   到此  新的区间划分就处理好了   新的基准值返回就好啦  
}

第一个版本实现完毕了,可能会有些疑问

为什么跳出循环后right位置的值一定不大于key?
当left > right 时,即right走到left的左侧,而left扫描过的数据均不大于key,因此right此时指向的数据一定不大于key

可以试着自己模拟一下下面的流程图
在这里插入图片描述

问题2:为什么left 和 right指定的数据和key值相等时也要交换?
相等的值参与交换确实有一些额外消耗。实际还有各种复杂的场景,-假设数组中的数据大量重复时,相等也交换能进行有效的分割排序。

在这里插入图片描述

如果不相等才交换的话,假设数据全是相同的数据,那每次基准值只能找到初始基准值的下一个,时间复杂度会变成O(N^2)

快排是挺快的,但好东西总有缺陷

快排 :hoare版本的时间复杂度
划分区间递归的时间复杂度为 logn
每次区间内找新的基准值时间复杂度为 n
时间复杂度为 N*logN
但是在数据有序的时候,时间复杂度还是O(N^2),新的基准值只能找到key的下一个数字,划分区间的效率很低

1.2.2 挖坑法 快排

思路:
创建左右指针。首先从右向左找出比基准小的数据找到后立即放入左边坑中当前位置变为新的"坑"然后从左向右找出比基准大的数据找到后立即放入右边坑中当前位置变为新的"坑",结束循环后将最开始存储的分界值放入当前的"坑"中,返回当前"坑"下标(即分界值下标)。
就是先从右往左找,再从左往右找,不断循环,直到left>right,过程中数值一直在迭代交换,这个时候最后一个坑刚好放最开始挖的值。

相比hoare还是有差别的
在这里插入图片描述

int _QuickSort1(int* a, int left, int right)
{int hole = left;   //  找到第一个坑  int key = a[left];  //   把第一个坑保存起来   while (left < right)   //  left==right时跳出循环  最后一个坑{while (left < right && a[right] >= key)   //  从右开始往左找{right--;}a[hole] = a[right];   //  当right--的循环跳出后  这个时候right对应的值小于key  把当前right的值换到坑里hole = right;  				// right变成新的坑  while (left < right && a[left] <= key)  //  从左开始往右找  {left++;     }a[hole] = a[left];   //  当left++的循环跳出后  这个时候left对应的值大于key  把当前left的值换到坑里  hole = left;		//  left变成新坑  }a[hole] = key;   //  大循环结束后  left=rightreturn hole;    //  这个新坑留给一开始的key值   返回新的基准值下边  
}

挖坑法完毕

挖坑法和hoare版本的时间复杂度一样 n*logn
但是在特殊情况也会有不好的地方 在数据有序的时候 时间复杂的还是会变成 O(N^2)

1.2.3 lomuto前后指针 快排

创建前后指针,从左往右找比基准值小的进行交换,使得小的都排在基准值的左边。
前后指针是我认为最好理解,也是代码最简单的一个
就是定义一个cur指针向前走,一个prev指针在后面跟着,cur找比基准值小的数据
在这里插入图片描述

在这里插入图片描述
话不多说,上代码

int _QuickSort1(int* a, int left, int right)
{int prev = left;   //  定义prev  cur  指针  int key = left;int cur = left + 1;while (cur <= right){if (a[cur] < a[key] && ++prev != cur)   //  当cur对应的值小于key时,可以考虑将prev与cur对应的值交换{										//  但如果这个时候,cur刚好是prev的下一个值,时没有必要交换的swap(a[prev], a[cur]);				//  所以要判断  prev++与cur是否相等  }++cur;   //  每次循环  cur++一次   }swap(a[key], a[prev]); // 循环结束之后,prev对应的值时小于key的prev的下一个就是大于key的  这个时候调换key和prev的值return prev;			//		找到新的基准值下标返回
}								

仔细了解前后指针的流程,想必也会感觉到,当数据有序或者是全部相同时
前后指针也是O(N^2)的时间复杂度,比起hoare和挖坑法 缺陷又多了一个数据全部相同时
想想数据全部相同或者有序,其实也没有排序的需要了,除非是算法题卡了数据相同的样例
所以快排的三种方法还是可行的

快速排序特性总结:
时间复杂度: O(nlogn)
空间复杂度: O(logn)

快排的基本内容就到这里啦


二、归并排序

归并排序算法思想:
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
在这里插入图片描述
底层核心还是递归,把一个大区间逐渐分成无数个小区间,(一个大问题分成无数个相同的子问题),快排和归并都是用到了递归,想想递归真的好用。

博客链接:合并两个有序数组

还有一个问题就是在递归到最后一层之后,怎么合并两个子区间让他们有序,这里我想到前面我们做过的习题,上面的链接供参考。

话不多说上代码

void _MergeSort(int* arr, int left, int right, int* tmp)  //  把大区间分配数个小空间  
{														// 两个小空间  排序成一个空间  用tmp接受  返回赋值给原数组 		if (left >= right)  //  递归出口   {return;}int mid = left + (right - left) / 2;//  分成两个区间  采用二分_MergeSort(arr, left, mid, tmp);     // 左区间_MergeSort(arr, mid + 1, right, tmp);// 右区间  //  递归处理之后  现在就是合并两个子区间  使他们有序  int begin1 = left, end1 = mid;       //  第一个区间的begin和endint begin2 = mid + 1, end2 = right;  // 第二个区间的  begin和endint index = begin1;    //  新的下标  对应tmp数组  while (begin1 <= end1 && begin2 <= end2)   //  合并两个数组的流程  不多赘述啦{if (arr[begin1] < arr[begin2]){tmp[index++] = arr[begin1++];}else{tmp[index++] = arr[begin2++];}}while (begin1 <= end1)      //  有数组没有全部传给tmp的情况  {tmp[index++] = arr[begin1++];}while (begin2 <= end2){tmp[index++] = arr[begin2++];}for (int i = left; i <= right; i++)  //  赋值返回原数组 {arr[i] = tmp[i];}
}
void MergeSort(int* arr, int n)
{int* tmp = (int*)malloc(sizeof(int)*n);   //  我们这里传一个新开的tmp数组空间进去,辅助合并两个子区间_MergeSort(arr, 0, n - 1, tmp);free(tmp);
}

仔细回看,归并其实也不难,就是一个递归的处理,然后再合并两个区间而已,洒洒水啦

实话实说,归并稳定,时间复杂度一直是O(nlogn) 不管数据是否有序是否相同
归并排序特性总结:
时间复杂度: O(nlogn)
空间复杂度: O(n)

总结

都说快排是个大家伙,现在学完来看,也就一般般嘛
回顾今天学习的内容,从快排的三种方式,到递归合并的归并排序
差不多都是围绕递归在展开排序,虽然快排有些许缺陷,但影响不大
现在想想,归并排序,又稳又好,就是代码有点多 哈哈哈哈
今天的学习就到这里啦,下一篇将深究一下快排以及非递归实现快排,不要走开,小编持续更新中~~~~

有差错的地方还请各位指出,小编必然马不停蹄来修改~~~~

在这里插入图片描述

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

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

相关文章

华为云云连接+squid进行正向代理上网冲浪

1 概述 ‌Squid‌是一个高性能的代理缓存服务器&#xff0c;主要用于缓冲Internet数据。它支持多种协议&#xff0c;包括FTP、gopher、HTTPS和HTTP。Squid通过一个单独的、非模块化的、I/O驱动的进程来处理所有的客户端请求&#xff0c;这使得它在处理请求时具有较高的效率‌。…

杰发科技AC7803——不同晶振频率时钟的配置

计算公式 PLL_POSDIV [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62] PLL_PREDIV_1 1 2 4 USE_XTAL 24M SYSCLK_FREQ 64M SYSCLK_DIVIDER 1 VCO USE_XTAL*…

攸信技术:运动文化激发企业活力,赋能体育行业新未来

在攸信技术&#xff0c;运动文化如同春日暖阳&#xff0c;温暖着每一位员工的心。这份文化&#xff0c;源自盈趣科技的深厚底蕴&#xff0c;橙色不仅传递着3POS文化中的激情与活力&#xff0c;更成为了攸信人共同的精神标识。公司的每一个角落&#xff0c;都洋溢着对运动的热爱…

【ubuntu24.04】GTX4700 配置安装cuda

筛选显卡驱动显卡驱动 NVIDIA-Linux-x86_64-550.135.run 而后重启:最新的是12.6 用于ubuntu24.04 ,但是我的4700的显卡驱动要求12.4 cuda

LightRAG - 更快更便宜的GraphRAG

检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;已经成为提升大型语言模型&#xff08;LLMs&#xff09;能力的重要方法之一&#xff0c;通过整合外部知识&#xff0c;显著改善了生成内容的质量和相关性。 RAG 的局限性 传统的 RAG 系统虽然表现优…

TCP/IP协议攻击与防范

一、TCP/IP协议攻击介绍 1.1 Internet的结构​ LAN&#xff1a;局域网 WAN&#xff1a;广域网 WLAN&#xff1a;无线局域网 私有IP地址与公有IP地址&#xff1f; 私有地址&#xff1a;A类&#xff1a;10.0.0.0~10.255.255.255 B类&#xff1a;172.16.0.0~172.31.255.255…

机器学习模型——线性回归

文章目录 前言1.基础概念2.代价函数3.单变量线性回归3.1加载数据3.2初始化超参数3.3梯度下降算法3.3.1初次梯度下降3.3.2 多次梯度下降3.3.3结果可视化 前言 随着互联网数据不断累积&#xff0c;硬件不断升级迭代&#xff0c;在这个信息爆炸的时代&#xff0c;机器学习已被应用…

Flink CDC 使用实践以及遇到的问题

背景 最近公司在做一些业务上的架构调整&#xff0c;有一部分是数据从mysql采集到Starrocks&#xff0c;之前的一套方法是走 debezium 到 puslar 到 starrocks,这一套下来比较需要配置很多东西&#xff0c;而且出现问题以后&#xff0c;需要修改很多配置&#xff0c;而且现阶段…

Pgsql:json字段查询与更新

1.查询json字段的值 SELECT attribute_data->>设施类别 mycol, * FROM gis_coord_data WHERE attribute_data->>设施类别阀门井 查询结果如下&#xff1a; 2.更新json字段中的某个属性值 UPDATE gis_coord_data SET attribute_data(attribute_data::jsonb ||{&quo…

DAMODEL丹摩 | 关于我部署与使用FLUX.1+ComfyUI生成了一位三只手的jk美少女这回事

DAMODEL丹摩 | 关于我部署与使用FLUX.1ComfyUI生成了一位三只手的jk美少女这回事 最终效果图FLUX.1简介部署流程1. 创建资源2. 登录实例3. 部署ComfyUI4. 部署FLUX.1 使用流程1. 运行FLUX.1 导入工作流 声明&#xff1a;非广告&#xff0c;为用户使用体验分享 最终效果图 FLUX.…

d3-contour 生成等高线图

D3.js 是一个强大的 JavaScript 库&#xff0c;用于创建动态、交互式数据可视化。d3-contour 是 D3.js 的一个扩展模块&#xff0c;用于生成等高线图&#xff08;contour plots&#xff09;。 属性和方法 属性 x: 一个函数&#xff0c;用于从数据点中提取 x 坐标。y: 一个函…

微信小程序 城市点击后跳转 并首页显示被点击城市

在微信小程序中&#xff0c;渲染出城市列表后&#xff0c;如何点击城市&#xff0c;就跳转回到首页&#xff0c;并在首页显示所点击的城市呢&#xff1f; 目录 一、定义点击城市的事件 二、首页的处理 首页&#xff1a;点击成都市会跳转到城市列表 城市列表&#xff1a;点击…

Web 学习笔记 - 网络安全

前言 作为 前端开发者&#xff0c;了解一点 Web 安全方面的基本知识是有很必要的&#xff0c;未必就要深入理解。本文主要介绍常见的网络攻击类型&#xff0c;不作深入探讨。 正文 网络攻击的形式种类繁多&#xff0c;从简单的网站敏感文件扫描、弱口令暴力破解&#xff0c;…

JavaEE---计算机是如何工作的?

1.了解冯诺依曼体系结构 2.CPU的核心概念,CPU的两个重要指标(核心数和频率) 3.CPU执行指令的流程(指令表,一条一条指令,取指令,解析指令,执行指令) 4.操作系统核心概念(管理硬件,给软件提供稳定的运行环境) 5.进程的概念(运行起来的程序和可执行文件的区别) 6.进程的管理(…

【pyspark学习从入门到精通21】机器学习库_4

目录 评估模型的性能 保存模型 参数超参数调整 网格搜索 评估模型的性能 显然&#xff0c;我们现在想测试我们的模型表现得如何。PySpark 在包的 .evaluation 部分提供了一些分类和回归的评估方法&#xff1a; import pyspark.ml.evaluation as ev 我们将使用 BinaryClas…

788页页大型集团财务集中管控平台项目总体规划方案全文深入解读

“大型集团公司财务集中管控平台项目”的总体规划方案&#xff0c;内容全面且详细&#xff0c;主要涵盖以下几个方面&#xff1a; 1. 项目概述&#xff1a;介绍了项目的背景、目标、预期收益、设计思路与方法及原则。项目旨在全面提升财务集中管控能力&#xff0c;提高财务价值…

mac下安装Ollama + Open WebUI + Llama3.1

本文介绍mac下安装Ollama Open WebUI Llama3.1 8b具体步骤。 目录 推荐配置Ollama Open WebUI Llama3.1简介安装Ollama安装Open WebUI 推荐配置 m1以上芯片&#xff0c;16g内存&#xff0c;20g以上硬盘空间 Ollama Open WebUI Llama3.1简介 Ollama: 下载&#xff0c;管理…

C 语言函数递归探秘:从基础概念到复杂问题求解的进阶之路

我的个人主页 我的专栏&#xff1a;C语言&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 目录 什么是函数递归递归的基本组成递归的工作原理递归的优缺点递归的经典案例 5.1 阶乘计算5.2 斐波那契数列5.3 汉诺塔问题5.4 二分查找 递归的高级…

Rust语言俄罗斯方块(漂亮的界面案例+详细的代码解说+完美运行)

tetris-demo A Tetris example written in Rust using Piston in under 500 lines of code 项目地址: https://gitcode.com/gh_mirrors/te/tetris-demo 项目介绍 "Tetris Example in Rust, v2" 是一个用Rust语言编写的俄罗斯方块游戏示例。这个项目不仅是一个简单…

Web开发:使用stackexchange.redis库对redis进行增删改查

一、安装第三方库 二、官网 StackExchange.Redis |通用型 redis 客户端 三、连接示例 private static string redisConnectionString "localhost:6379,passwordyourpassword,defaultDatabase0,syncTimeout10000";private static string redisConnectionString &q…