文章目录

  • 引言:
    • 1.归并排序(MergeSort)
    • 2.快速排序的优化(顶级优化)
    • 3.快速排序的三种思路的代码实现及分析
    • 4.归并排序和快排第3原理的测试

引言:

刚刚去回顾了一下递归实现的几个小代码,感觉递归真的是很神奇的一个东西,现在感觉递归确实也是比较不好理解的东西,所以这边我提供几个好的学习和理解递归的方法,递归无非就是两种,一种是求出递归的递归,另一种是起作用的递归函数的递归,两种是有一定的区别的。我们先讲一下要求出递归的递归,这种就类似于用递归求一个数的阶乘,必须要从头开始算,算出了头才可以算尾,这个就叫要求出递归的递归,学习这种递归首先就是要弄明白这个递归的原理,然后再在纸上把每一个递归的值给算出来,当然是要从最头上的那个开始算,另一种就是不需要算的递归,只要可以发挥出函数的作用就可以了,这种递归是比较的抽象的,因为这种作用型递归函数都是非常长的一串代码,所以当我们要理解这种递归的时候,我们可以画出它的递归展开图,就是一幅图一幅图的画,递归一次你就画一幅图,并且在画图理解的同时可以调试代码,按照图来调试代码,一遍不行再一遍,直到理解(这时想要理解这种递归的唯一方法),所以递归可以说是非常的不好搞。现在北京时间 2022/12/27/0:43 ,今天可以看出是超出了平常写博客的时间,但是我们还是要坚持写博客,并且我刚刚看了一下我最开始写的博客,我觉得当时的博客写的很有味道,现在的博客总感觉没什么味道了,以前写博客每一个字,都是肺腑之言一般,现在的博客除了引言中的内容,好像大部分都有一些不够活灵活现的感觉,以前的每一句话写的真的都很好玩的感觉,就算是那种死板的语言也可以被我们自己理解然后写的非常的有趣搞笑,不想现在的,有一些刻板,就像是以前都在写真心话,现在写的都是客套话的感觉,并且有一种以前写的博客是真真实实的写给自己看的,而现在的博客更不像是不是写给自己看的;总感觉现在的博客和以前区别很大,可能是我真的经历了吧!经历了改变了,以前刚开始写博客,写完之后睡觉好像都是想住别人在浏览你的博客,然后你的解释是如何如何的好,语句是多么多么的清楚明了,自己在句里行间写了多少多少的细节在里面,然后别人发现这些细节的时候是怎样的感觉,之类的想法,当时好像都会有,但是现在的我,好像……,可能是明白了CSDN的推荐和不推荐,关注和不关注之类的问题和当今时代文章好像确实是没有人看,更没有人会一个字一个字的仔细看,并且自己作为一个小白,写的内容也都是最基本的,好像确实是不需要看之类的这一系列额想法在里面,最终导致了我们博客风格的改变吧!行文来到这个位置,我接上一篇博客分享一下我的昨晚睡觉体验(如我意料之中,是我许久以来睡的最舒服的一次),缩小了位置,并不会影响我的睡觉体验,但是却可以让我睡的暖和,…… ,天大寒,码字不易,引言就这样吧!今天我们讲一下什么是归并排序和快速排序的另外两种思路。

1.归并排序(MergeSort)

时间复杂度:O(N*logN)

1.1 归并排序原理:我们可以假设一个数组它的左半区间有序 右半区间也有序 ,然后在这个前提之下,然后我们使用归并算法:依次比较左半区间和右半区间中的元素然后取小的元素放到新创建的临时数组中。

按照归并排序的原理,此时我需要有两个新的个区间,如何获得两个区间呢?就是将int mid = (left + right) / 2; 这样我们就可以利用这个中间值进行一个数组分成两个区间,有了区间我们此时就假设左半区间[ left , mid ] 和右半区间 [ mid+1 ,right ] 是有序的,那么我们就可以进行归并,让我的左半区间和右半区间有序(只有这样,才可以进行我的归并算法实现),分治递归实现。

图示如下:

问题:归并之前,左右区间没有序?怎么办?
此时这个问题就涉及到我们归并排序中的递归问题
所以想要解决这个问题就需要我们进行一个分治递归的思想,从而实现我们的代码
所以我们此时如何使我的左半区间和右半区间有序呢?
分治递归:

 _MergeSort(arr, left, mid, tmp);//假设我使用归并算法已经把左半区间排成有序了_MergeSort(arr, mid + 1, right, tmp);//假设我使用归并算法已经把半右区间排成有序了

这样我们就可以让我们的一个数组的左半区间和右半区间都有序了,此时就可以进行归并排序了

具体动图如下:


1.2 归并排序代码实现

#include<stdlib.h>
void _MergeSort(int* arr, int left, int right, int* tmp)
{if (left >= right)//递归条件{return;}int mid = (left + right) >> 1;//分治递归_MergeSort(arr, left, mid, tmp);_MergeSort(arr, mid + 1, right, tmp);//归并算法的代码实现:int begin1 = left;int end1 = mid;int begin2 = mid + 1;int end2 = right;int index = left;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[index++] = arr[begin1++];}else{tmp[index++] = arr[begin2++];} }while (begin1 <= end1){tmp[index++] = arr[begin1++];}while (begin2 <= end2){tmp[index++] = arr[begin2++];}//将新数组中的元素拷贝回原数组int i;for (i = left; i <= right; i++){arr[i] = tmp[i];}}
void MergeSort(int* arr, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);_MergeSort(arr, 0, n - 1, tmp);free(tmp);
}

注意点: 为了防止待会递归这个函数的时候每一次都还要重新malloc一块空间,所以我们就要把这个函数的作用外包到外面去,不然每次都要malloc一块空间效率下降,所以我就可以在外部重新写一个这个函数的子函数void _MergeSort(int* arr, int left, int right, int* tmp) 并且此时的这个函数其实还是MergeSort函数,只是多了一个 _ 而已,_ 表示的就是子函数的意思

2.快速排序的优化(顶级优化)

(注释详细)

学到这个位置我们意识到了,快排是有一定的缺陷的,当我转参的数组是一个有序的数组的时候(例:1 2 3 4 5 6 7 8 9 ),想要使用快排进行排序(因为快排的原理,每次都是取arr[0]或arr[end-1]),所以就会导致第一次是N,第二次是N-1,……,最后0,然后等差数列相加(就会导致快排的时间复杂度是:O(N^2))
所以快排是有一定的缺陷的,所以为了避免这个缺陷(我们就要对key的值进行优化),就会使用一个叫三数取中的方法来获取key的值:所以我就要对这个快排的代码就行改进

第一个优化:快排的三数取中法优化, 函数如下int GetMidIndex(int* arr, int left, int right)
第二个优化:小区间优化

int GetMidIndex(int* arr, int left, int right)
{//三数取中法代码int mid = (left + right) >> 1;//这句代码=> int mid = (left + right)/2; 只是移位的效率会高一点点//下面的这些if条件的判断就是为了取到三个数中不是最大也不是最小的那个数而已if (arr[left] < arr[mid]){if (arr[mid] < arr[right]){return mid;}else if(arr[left]>arr[right])//程序来到这个位置就是说明,此时我的mid是我的最大值,然而我的目的是为了找到中间的那个值,所以这边我就比较一下left和right就行,大的那个就是中间值{return left;}else{return right;//程序来到这里就说明right大,那么right就是中间值,是中间值就return}}else  //此时这个else的意思就是:arr[left] > arr[mid]{if (arr[left] < arr[right])//这边三数取中的写法很多,可以有不一样的写法,不怕{return left;}else if (arr[mid] < arr[right]){return right;}else{return mid;}}}
void QuickSort(int* arr, int left, int right)
{if (left >= right){return;}int index = GetMidIndex(arr, left, right);Swap(&arr[left], &arr[index]);//下面的代码基本不变,就是这两步用来获取我的中间值就行了int begin = left;int end = right;int pivot = begin;int key = arr[begin];while (begin < end){while (begin < end && arr[end] >= key){end--;}arr[pivot] = arr[end];pivot = end;while (begin < end && arr[begin] <= key){begin++;}arr[pivot] = arr[begin];pivot = begin;}pivot = begin;arr[pivot] = key;//此时代码来到这个位置就有一些不好的地方(比如刚刚我们使用了三数取中的函数,如果此时需要排序的数据非常大(1000000),有这么多个数据,此时如果要一直调用三数取中的函数就会导致效率问题)//并且当我们需要排序的数据剩下的不是非常多的时候(此时正是调用次数最多的时候因为快排是一个logN的排序 越到后面,执行次数越多(例:2^19 2^18)),所以此时我们可以来一个小区间优化//就是当需要排序的数据没有那么多时,我们就不使用快速排序,我们使用直接插入排序就行//原递归代码://QuickSort(arr, left, pivot - 1);//QuickSort(arr, pivot + 1, right);//所以优化代码如下:if (pivot - 1 - left > 10){QuickSort(arr, left, pivot - 1);}else{InsertSort(arr + left, pivot - 1 - left + 1);}if (right - (pivot + 1) > 10){QuickSort(arr, pivot + 1, right);}else{InsertSort(arr + pivot + 1, right - (pivot + 1) + 1);}}

3.快速排序的三种思路的代码实现及分析

3.1 快排的第一种思想挖坑法

int PartQuickSort1(int* arr, int left, int right)//此时这个表示的是一个单趟排序的挖坑法快速排序方法
{int index = GetMidIndex(arr, left, right);Swap(&arr[left], &arr[index]);//下面的代码基本不变,就是这两步用来获取我的中间值就行了int begin = left;int end = right;int pivot = begin;int key = arr[begin];while (begin < end){while (begin < end && arr[end] >= key){end--;}arr[pivot] = arr[end];pivot = end;while (begin < end && arr[begin] <= key){begin++;}arr[pivot] = arr[begin];pivot = begin;}pivot = begin;arr[pivot] = key;return pivot;
}

3.2 快排的第二种思路的实现(前后指针法)

int PartQuickSort3(int* arr, int left, int right)
{int index = GetMidIndex(arr, left, right);Swap(&arr[left], &arr[index]);int keyi = left;int prev = left;int cur = left + 1;while (cur <= right){if (arr[cur] < arr[keyi]){prev++;Swap(&arr[prev], &arr[cur]);}cur++;}Swap(&arr[keyi], &arr[prev]);return prev;}

3.3 快排的第三种方法的实现(挖坑法的变形(左右指针法))

理解:注释很详细


int PartQuickSort2(int* arr, int left, int right)
{int index = GetMidIndex(arr, left, right);Swap(&arr[left], &arr[index]);int begin = left;int end = right;int keyi = begin;//左右指针法的快排while (begin < end)//表示两指针找元素,直到相遇为止{while (begin < end && arr[end] >= arr[keyi])//就是在右边找比key小的数{end--;        //注意点:记得要有等号,因为如果没有等号就可能会死循环,因为如果左边和右边,两边同时找到一个和keyi相等的数(进不去循环,就导致end和begin不变),此时就会导致这两个数相等的数一直换过来换过去,就会导致死循环}while (begin < end && arr[begin] <= arr[keyi]){begin++;}//程序来到这个位置就说明我已经找到了比key大的数和比key小的数了,此时只要把这两个数给交换一下,然后我就完成了快排的单趟排序了(剩下的就交给递归去搞定就行了)Swap(&arr[begin], &arr[end]);//交换完,我就完成了快排的单趟排序了(剩下的就交给递归去搞定就行了)}Swap(&arr[begin], &arr[keyi]);//这步就是为了把我的keyi这个关键位置给放到中间的那个位置(同时也是正确的那个位置)return begin;//就是返回中间位置(虽然可能不是中间,但是就是要中间的意思)}

2.1 快排的三种原理实现测试


void QuickSort2(int* arr, int left, int right)
{if (left >= right){return;}//三种思路的测试(不可以同时调用)int keyIndex = PartQuickSort1(arr, left, right);int keyIndex = PartQuickSort2(arr, left, right);int keyIndex = PartQuickSort3(arr, left, right);if (keyIndex - 1 - left > 10){QuickSort2(arr, left, keyIndex - 1);}else{InsertSort(arr + left, keyIndex - 1 - left + 1);}if (right - (keyIndex + 1) > 10){QuickSort2(arr, keyIndex + 1, right);}else{InsertSort(arr + keyIndex + 1, right - (keyIndex + 1) + 1);}
}

4.归并排序和快排第3原理的测试

数据结构之排序【归并排序和快排的顶级优化和快排的三种原理的实现及分析】 内含动态演示图相关推荐

  1. python读csv最快方法_使用Python读写csv文件的三种方法

    Python读写csv文件 觉得有用的话,欢迎一起讨论相互学习~Follow Me 前言 逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是 ...

  2. 在“双减”政策下,利用《陈老师排课12.0》排主课、早自习、课后服务三种不同时段的综合课表

    陈老师排课12.0是排课11.0的升级版.可以对不同时段分别排课,最后把几张课表综合在一起.排课12.0把VB程序与EXCEL课表打印系统完美结合,先对全校原始课表进行课时智能分布优化,按<排课 ...

  3. 【图解数据结构】排序全面总结(一)

    目录 一.前言 学习目标: 二.基本概念 1.排序 2.排序方法的稳定性 3.内部和外部排序 三.插入类排序 1.直接插入排序 2.折半插入排序 3.希尔排序 四.交换类排序 1.冒泡排序 2.快速排 ...

  4. 三种线性排序算法 计数排序、桶排序与基数排序-BYVoid

    转自:BYVoid [非基于比较的排序] 在计算机科学中,排序是一门基础的算法技术,许多算法都要以此作为基础,不同的排序算法有着不同的时间开销和空间开销.排序算法有非常多种,如我们最常用的快速排序和堆 ...

  5. 三种线性排序算法 计数排序、桶排序与基数排序—— 转自:BYVoid

    三种线性排序算法 计数排序.桶排序与基数排序 [非基于比较的排序] 在计算机科学中,排序是一门基础的算法技术,许多算法都要以此作为基础,不同的排序算法有着不同的时间开销和空间开销.排序算法有非常多种, ...

  6. 快排堆排归排三种排序的比较

    目录 快排 堆排序 归并排序 三种排序的比较 快排 快速排序中最简单的(递归调用) 注:倒序,和 列表中有大量重复元素时,时间复杂度很大 快排例子 注:快排代码实现(类似于二叉树 递归调用) 时间复杂 ...

  7. 八大排序算法之快速排序(上篇)(未经优化的快排)

    目录 一.关于快速排序的总体算法思想 1.冒泡排序(交换排序) (以排升序为例) 2.快速排序的总体思想简介(以排升序为例) 二.快速排序单趟排序的算法接口设计(以排升序为例) 单趟排序实现的方法一: ...

  8. 【C语言简单实现数据结构】排序之交换排序和归并排序

    心有所向,日复一日,必有精进. 目录 前言 一.交换排序 冒泡排序 算法思想: 代码实现: 冒泡排序的特性总结: 快速排序 Hoare版本: 挖坑法 前后指针法 快速排序的特性总结: 归并排序 递归实 ...

  9. 【数据结构】排序算法及优化整理

    排序算法 排序算法 选择排序 Selection Sort 插入排序 Insertion Sort 归并算法 Merge Sort 快速排序 Quick Sort 堆排序 Heap Sort 二叉堆的 ...

最新文章

  1. delphi xe http 收不到反馈消息_20款途乐4.0 xE 办公室开票
  2. flask 继承模版的基本使用1
  3. [Leedcode][JAVA][第98题][验证二叉搜索树]
  4. mysql 数据透视_sql怎么做数据透视表
  5. 关于T_SQL中声明变量类型的基础知识。
  6. 微信小程序商城源码,带前后端,基于node
  7. Diffusion Model扩散模型原理
  8. 如何将Excel工作表另存为PDF
  9. windows 保护色设置
  10. 小米路由器r2d_小米路由器R2D刷机变砖维修过程记录
  11. 金额转换,阿拉伯数字的金额转换成中国传统的形式
  12. layui设置按钮不可点击_layui upload 模块点击选择文件按钮的禁用与启用功能
  13. java毕设项目广东省梅州市宇恒节能科技有限公司(附源码)
  14. 实验二 XML之数学公式和化学分子式
  15. 网络舆情分析系统的研究与设计
  16. 小型电梯尺寸_家用别墅小型电梯介绍:品牌、大概价格及尺寸等
  17. C#,数值计算,数据测试用的对称正定矩阵(Symmetric Positive Definite Matrix)的随机生成算法与源代码
  18. 用Python下载一个网页保存为本地的 HTML文件
  19. 一个最简单的局域网私人云盘SnapFile
  20. 【附源码】计算机毕业设计SSM线上花店购物商城

热门文章

  1. UnityShader35:光晕光效
  2. Pyplot tutorial
  3. 鸿蒙系统p50什么时候上市,华为P50Pro和MatePad Pro2什么时候上市 搭载鸿蒙系统吗...
  4. win10通过局域网共享文件夹
  5. Linux挂载硬盘后无法进入系统,Centos7 fstab盘符挂载硬盘导致重启系统失败解决办法...
  6. Wi-Fi 6时代,华为的情怀和野心
  7. android leap motion,motionleap安卓-motionleap免费版预约 _5577安卓网
  8. 图片制作神器哪个好?易可图免费使用
  9. LGR 2022 提高组 初赛模拟试题 错题解析
  10. 使用python获取股票指定日期交易代码数据