[C][数据结构][排序][下][快速排序][归并排序]详细讲解

文章目录

  • 1.快速排序
    • 1.基本思想
    • 2.hoare版本
    • 3.挖坑法
    • 4.前后指针版本
    • 5.非递归版本改写
  • 2.归并排序


1.快速排序

1.基本思想

  • 任取待排序元素序列的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

2.hoare版本

请添加图片描述

  • 选key – 一般是最左边或者最右边的值
    • 左作key,让右边先走
    • 右作key,让左边先走
  • 为什么左边做key,要让右边先走?
    • 要保证相遇位置的值,比key小/就是key
      • R先走,R停下,L去遇到R
        • 相遇位置就是R停下来的位置,必定比key小
      • R先走,R没有找到比key小的值,R去遇到了L
        • 相遇位置就是L上一轮停下来的位置,比key小/就是key
  • left向前找大
  • right向后找小
  • 单趟排完以后:
    • 左边都比key小
    • 右边都比key大
  • 实现:
    void QuickSort1(int* a, int begin, int end)
    {//结束条件  --  只有一个数 --> begin == end || 区间不存在 --> begin > endif (begin >= end){return;}if (end - begin > 0){//hoare版本int left = begin, right = end;int keyi = left;while (left < right){//右边先走,找小while (left < right && a[right] >= a[keyi])  //left < right是为了防止越界{right--;}//左边再走,找大while (left < right && a[left] <= a[keyi]){left++;}//交换Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);keyi = left;//key已经正确的位置上,左边都比key小,右边都比key大//递归,分治 --  [begin,keyi - 1]  keyi  [keyi + 1,end]QuickSort1(a, begin, keyi - 1);QuickSort1(a, keyi + 1, end);}else{//直接插入排序InsertSort(a + begin, end - begin + 1);}
    }
    

3.挖坑法

请添加图片描述

  • 本质同hoare,但是好理解些
  • 右边找小,填到左边的坑里去,这个位置形成新的坑
  • 左边找大,填到右边的坑里去,这个位置形成新的坑
  • 实现:
    void QuickSort2(int* a, int begin, int end)
    {//结束条件  --  只有一个数 --> begin == end || 区间不存在 --> begin > endif (begin >= end){return;}if (end - begin > 0){//挖坑法int key = a[begin];int piti = begin;int left = begin, right = end;while (begin < end){//右边找小,填到左边的坑里去,这个位置形成新的坑while (begin < end && a[right] >= key){right--;}a[piti] = a[right];piti = right;//左边找大,填到右边的坑里去,这个位置形成新的坑while (begin < end && a[left] <= key){left++;}a[piti] = a[left];piti = left;}a[piti] = key;//key已经正确的位置上,左边都比key小,右边都比key大//递归,分治 --  [begin,keyi - 1]  keyi  [keyi + 1,end]QuickSort2(a, begin, piti - 1);QuickSort2(a, piti + 1, end);}else{//直接插入排序InsertSort(a + begin, end - begin + 1);}
    }
    

4.前后指针版本

请添加图片描述

  • cur向前找小,遇到小,则prev++,且交换cur、prev
  • 实现:
    void QuickSort3(int* a, int begin, int end)
    {//结束条件  --  只有一个数 --> begin == end || 区间不存在 --> begin > endif (begin >= end){return;}if (end - begin > 0){//前后指针版本int prev = begin;int cur = begin + 1;int keyi = begin;//加入三数取中的优化int midi = GetMidIndex(a, begin, end);Swap(&a[keyi], &a[midi]);while (cur <= end)  //一直往后走,遇到小则停下来处理{//cur找小if (a[cur] < a[keyi] && ++prev != cur)  //防止prev和cur重合时,重复交换{Swap(&a[prev], &a[cur]);}cur++;}Swap(&a[keyi], &a[prev]);keyi = prev;//key已经正确的位置上,左边都比key小,右边都比key大//递归,分治 --  [begin,keyi - 1]  keyi  [keyi + 1,end]QuickSort3(a, begin, keyi - 1);QuickSort3(a, keyi + 1, end);}else{//直接插入排序InsertSort(a + begin, end - begin + 1);}
    }
    

5.非递归版本改写

  • 为何需要改写非递归?
    • 极端场景下,若深度太深,会出现栈溢出
  • 方法:用数据结构模拟递归过程
  • 实现:
    //用栈模拟递归 -- 先进后出
    void QuickSortNonR(int* a, int begin, int end)
    {Stack st;StackInit(&st);StackPush(&st, end);StackPush(&st, begin);while (!isStackEmpty(&st)){//从栈中取出两个数,作为区间int left = StackTop(&st);StackPop(&st);int right = StackTop(&st);StackPop(&st);//排序,取keyiint keyi = PartSort3(a, left, right);//此时分成了两个区间 [left, keyi-1] keyi [keyi+1, right]//继续压栈if (keyi + 1 < right){StackPush(&st, right);StackPush(&st, keyi + 1);}if (left < keyi - 1){StackPush(&st, keyi - 1);StackPush(&st, left);}}StackDestroy(&st);
    }
    
  • 特性总结:
    • 综合性能&使用场景比较好
    • 时间复杂度:O(N*logN)
    • 空间复杂度:O(logN)
    • 稳定性:不稳定
  • [快速排序]优化方案
    • 三数取中法选key
      • 防止key都是极端数字 --> 递归过深 --> 栈溢出
    • 递归到小的子区间时,考虑使用插入排序
      • 大幅减少递归次数,提高效率
      • 例:最后一层递归占了总递归次数的50%,但可能只有极少量的数

2.归并排序

请添加图片描述

  • 基本思想:

  • 分治思维

    • 将已有序的子序列合并,得到完全有序的序列
    • 即先使每个子序列有序,再使子序列段间有序。
    • 若将两个有序表合并成一个有序表,称为二路归并
  • 归并排序核心步骤:
    请添加图片描述

  • 实现:

    //函数名前加_表示这个函数是内部函数,不对外提供接口 - 子函数
    //后序思想
    void _MergeSort(int* a, int begin, int end, int* tmp)
    {if (begin >= end){return;}int mid = (begin + end) / 2;//[begin, mid] [mid+1, end] 分治递归,让子区间有序_MergeSort(a,begin,mid,tmp);_MergeSort(a,mid+1,end,tmp);//归并 [begin, mid] [mid+1, end]int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin1;while (begin1 <= end1 && begin2 <= end2)  //有一组结束则结束{if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}//已经有一组结束了,拷贝另一组剩余的while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}//把归并数据拷贝回原数组memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
    }//时间复杂度:O(N*logN)
    //空间复杂度:O(N)
    void MergeSort(int* a, int n)
    {int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("MergeSort");exit(-1);}_MergeSort(a, 0, n - 1, tmp);free(tmp);
    }
    
  • 非递归版本改写

  • 实现:

  • void MergeSortNonR(int* a, int n)
    {int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc:");exit(-1);}//手动归并int gap = 1;  //每次归并的元素个数while (gap < n){for (int i = 0; i < n; i += 2 * gap){//[i, i+gap-1] [i+gap,i+2*gap-1]int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;//越界处理 - 修正边界if (end1 >= n){end1 = n - 1;//[begin2, end2]修正为不存在区间begin2 = n;end2 = n - 1;}else if (begin2 >= n){// [begin2, end2]修正为不存在区间begin2 = n;end2 = n - 1;}else if (end2 >= n){end2 = n - 1;}//归并int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}}//全部归并完后,拷贝回原数组memcpy(a, tmp, sizeof(int) * n);gap *= 2;}free(tmp);
    }
    
  • void MergeSortNonR(int* a, int n)
    {int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc:");exit(-1);}//手动归并int gap = 1;  //每次归并的元素个数while (gap < n){for (int i = 0; i < n; i += 2 * gap){//[i, i+gap-1] [i+gap,i+2*gap-1]int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;//越界处理//end1越界或者begin2越界,则可以不归并了if (end1 >= n || begin2 >= n){break;}else if (end2 >= n){end2 = n - 1;}//归并int m = end2 - begin1 + 1;int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * m);}gap *= 2;}free(tmp);
    }
    
  • 特性总结:

    • 更多解决在磁盘中的外排序问题
    • 时间复杂度:O(N*logN)
    • 空间复杂度:O(N) – 缺点
    • 稳定性:稳定

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

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

相关文章

目标检测中的anchor机制

目录 一、目标检测中的anchor机制 1.什么是anchor boxes&#xff1f; 二、什么是Anchor&#xff1f; ​编辑三、为什么需要anchor boxes&#xff1f; 四、anchor boxes是怎么生成的&#xff1f; 五、高宽比&#xff08;aspect ratio&#xff09;的确定 六、尺度(scale)的…

工业高温烤箱:现代工业的重要设备

工业高温烤箱&#xff0c;作为现代工业生产中不可或缺的关键设备&#xff0c;以其独特的高温烘烤能力&#xff0c;为各种工业产品的加工与制造提供了强有力的支持。斯博欣将对工业高温烤箱的原理、特点、应用领域及未来发展进行简要介绍。 一、工业高温烤箱的特点 1、高温性能优…

怎么修改Visual Studio Code中现在github账号

git config --global user.name “你的用户名” git config --global user.email “你的邮箱” git config --global --list git push -u origin your_branch_name git remote add origin

FastAPI 作为H5中流式输出的后端

FastAPI 作为H5中流式输出的后端 最近大家都在玩LLM&#xff0c;我也凑了热闹&#xff0c;简单实现了一个本地LLM应用&#xff0c;分享给大家&#xff0c;百分百可以用哦&#xff5e;^ - ^ 先介绍下我使用的三种工具&#xff1a; Ollama&#xff1a;一个免费的开源框架&…

centos7 xtrabackup mysql 基本测试(4)---虚拟机环境 mysql 修改datadir(有问题)

centos7 xtrabackup mysql 基本测试&#xff08;4&#xff09;—虚拟机环境 mysql 修改datadir 参考 centos更改mysql数据库目录 https://blog.csdn.net/sinat_33151213/article/details/125079593 https://blog.csdn.net/jx_ZhangZhaoxuan/article/details/129139499 创建目…

锌,能否成为下一个“铜”?

光大期货认为&#xff0c;今年以来&#xff0c;市场关注锌能否接棒铜价牛市。铜需求增长空间大&#xff0c;而锌消费结构传统&#xff0c;缺乏新亮点。虽然在供应的扰动上锌强于铜&#xff0c;但因需求乏善可陈&#xff0c;金融属性弱势&#xff0c;锌很难接棒铜&#xff0c;引…

数据质量守护者:数据治理视角下的智能数据提取策略

一、引言 在信息化和数字化高速发展的今天&#xff0c;数据已成为企业决策、运营和创新的核心要素。然而&#xff0c;随着数据量的快速增长和来源的多样化&#xff0c;数据质量问题逐渐凸显&#xff0c;成为制约企业数据价值发挥的关键因素。数据治理作为确保数据质量、提升数…

KEIL5.39 5.40 fromelf 不能生成HEX bug

使用AC6 编译,只要勾选了生成HEX。 结果报如下错误 暂时没有好的解决办法 1.替换法 2.在编译完后用命令生成HEX

蚓链研究院告诉你:蚓链数字化营销如何帮助力助你打造品牌!

在打造产品品牌的过程中&#xff0c;数字化营销会带来哪些利弊影响&#xff1f;如何消除或减少弊端&#xff1f;蚓链来和你一起分析、解决。 利处&#xff1a; 1.高度精准的目标定位&#xff1a;凭借大数据和先进算法&#xff0c;能精确锁定潜在客户&#xff0c;使营销资源得到…

数栈xAI:轻量化、专业化、模块化,四大功能革新 SQL 开发体验

在这个数据如潮的时代&#xff0c;SQL 已远远超越了简单的查询语言范畴&#xff0c;它已成为数据分析和决策制定的基石&#xff0c;成为撬动企业智慧决策的关键杠杆。SQL 的编写和执行效率直接关系到数据处理的速度和分析结果的深度&#xff0c;对企业洞察市场动态、优化业务流…

针对k8s集群已经加入集群的服务器进行驱逐

例如k8s 已经有很多服务器&#xff0c;现在由于服务器资源过剩&#xff0c;需要剥离一些服务器出来 查找节点名称&#xff1a; kubectl get nodes设置为不可调度&#xff1a; kubectl cordon k8s-node13恢复可调度 kubectl uncordon k8s-node13在驱逐之前先把需要剥离驱逐的节…

File及典型案例

File File对象表示一个路径&#xff0c;可以是文件的路径&#xff0c;也可以是文件夹的路径 这个路径可以是存在的&#xff0c;也允许不存在 常见的构造方法 图来自黑马程序员网课 package com.lazyGirl.filedemo;import java.io.File;public class Demo1 {public static vo…

立式护眼台灯十大品牌哪个好?立式护眼台灯十大品牌排行

立式护眼台灯十大品牌哪个好?根据国际市场的研究数据表明&#xff0c;我国在日常生活中对电子产品的依赖度极高&#xff0c;每天看电子产品的时间超过8小时&#xff0c;出现眼睛酸痛、干涩、视觉疲劳的人群也不再少数&#xff0c;而给眼睛带来伤害的除了电子产品中所含的蓝光之…

Vue3-滑动到最右验证功能

1、思路 1、在登录页面需要启动向右滑块验证 2、效果图 3、文章地址&#xff1a;滑动验证码的实现-vue-simple-verify 2、成分分析 1、由三块构成&#xff0c;分别是底部条、拖动条、拖动移动部分 2、底部条&#xff1a;整体容器&#xff0c;包括背景、边框和文字&#xf…

端午假期新房销售较去年下降16%,6月核心城市有望继续好转

内容提要 国常会强调政策措施落地见效&#xff0c;继续研究新去库存、稳市场政策。多城市二手房市场活跃&#xff0c;新房成交回暖缓慢。端午假期新房销售下降&#xff0c;核心城市市场有望好转。 文章正文 6月7日&#xff0c;国常会强调“着力推动已出台政策措施落地见效&am…

ai 人工智能免费网站免费生成图片生成ppt

豆包 Kimi.ai - 帮你看更大的世界 生成ppt 讯飞智文 - AI在线生成PPT、Word 大家如有其它免费的欢迎推荐!!!

五分钟看完WWDC24

大家好&#xff0c;我是小编阿文。欢迎您关注我们&#xff0c;经常分享有关Android出海&#xff0c;iOS出海&#xff0c;App市场政策实时更新&#xff0c;互金市场投放策略&#xff0c;最新互金新闻资讯等文章&#xff0c;期待与您共航世界之海。 北京时间6月11日凌晨1点&…

2024中国翻译行业发展报告

来源&#xff1a;中国翻译协会 近期历史回顾&#xff1a; 2024国内工商业储能市场研究报告.pdf 2023幸福企业白皮书.pdf 2024年欧亚地区移动经济报告.pdf 内容供应链变革 2023人工智能与首席营销官&#xff08;CMO&#xff09; AI科技对PC产业的影响.pdf 金融业数据应用发展报…

01本地图像导入及参数设置

左边工具栏&#xff1a;采集-》图像源&#xff0c;点击后 拉到流程窗口中 在右边有三个按钮可以添加图像和图像文件夹。 双击 图像源 可以打开 参数设置 参数说明&#xff1a; 像素格式&#xff1a;MONO8 表示图像为黑白图像&#xff0c;RGB24为彩色图像。看你想以什么图像打开…

通用大模型VS垂直大模型,你更青睐哪一方?

AI大模型之辩&#xff1a;通用与垂直&#xff0c;谁将引领未来&#xff1f; 在人工智能&#xff08;AI&#xff09;领域&#xff0c;大模型技术的崛起无疑为整个行业带来了革命性的变革。然而&#xff0c;随着技术的深入发展&#xff0c;AI大模型的战场似乎正在悄然分化&#…