【排序算法 下】带你手撕常见排序 (冒泡,快排,归并排序) (动图详解)

简介: 【排序算法 下】带你手撕常见排序 (冒泡,快排,归并排序) (动图详解)

95cfa829fcc324393942dd2d9642970b_060bad25320648609051cb9a6d622b8f.png

“只要有花可开,就不允许生命与黯淡为伍。”


前言:

承接上篇,继续带大家手撕常见排序算法,这次讲剩余的两类:交换排序和归并排序。

注:

这篇博客属于数据结构内容,建议学习完C语言的小伙伴食用~


目录

🐨Part1:交换排序

1.冒泡排序

1.1思想

1.2实现

2.快速排序

2.1Hoare经典版

2.2挖坑版

🐸Part2:归并排序

归并排序

思想

实现

🦄最后总结



Part1:交换排序


1.冒泡排序


1.1思想


冒泡排序相比大家都很熟悉了,这是学习C语言过程当中的第一个排序算法

它的思想就是一趟一趟走,每一次都找到最大的数字,放到靠后的位置,就像水底冒泡一样,越往上气泡越大。


动图:

e63d98f7905fb6eed71bc39dae7d4a7b_f827f785ea1d41fab1d7cff816cacd2c.gif


1.2实现


//冒泡排序
void BubbleSort(int* a, int n)
{
  for (int j = 0; j < n; j++)
  {
    for (int i = 1; i < n-j; i++)
    {
      if (a[i - 1] > a[i])
      {
        Swap(&a[i - 1], &a[i]);
      }
    }
  }
}


特征分析:

像选择排序,思想易理解,也好实现,适合教学;

时间复杂度:最好:O(N^2)  最坏:O(N^2);

空间复杂度:O(1);

稳定性:稳如老狗


2.快速排序


在这里,讲快速排序三种常见的实现方法:Hoare经典版本,挖坑版本,前后指针版本。


2.1Hoare经典版


因为这个算法是霍尔大佬发明的,所以以他的名字命名,这是最经典的快速排序:

7cadf39db9427643ab7a8d5ce3e8cd17_01bbce6e05cc421383e1721b3dfb5753.gif


思想:

先找一个参考值 key,定义左右两端的下标/指针 R , L ,向内靠拢;

注意:左边做key,右边先走,可使相遇点比key小;

R 找比 key 值小的位置,L 找比 key 值大的位置;

都找到后,R L 对应位置的数字交换;

R L 相遇,相遇位置对应的数字与 key 交换。


这样就保证了数组左侧数字都小于 key,右侧数字都大于 key 。

到这里还没完呢,可不能保证左右侧数字都是有序的,所以还要对左右两侧进行相同操作,没错,可以用递归

b7d60f99cef4393304121e1499448e32_2badff8f1f2640ac968545c0e7de41c6.png

有点类似与二叉树的分治。

代码实现:

//快排(Hoare)
void QuickSort1(int* a, int left, int right)
{
  if (left >= right)
    return;
  int begin = left, end = right;
  int keyi = left;
  while (left < right)
  {
    //右找小
    while (left < right && a[right] >= a[keyi])
      right--;
    //左找大
    while (left < right && a[left] <= a[keyi])
      left++;
    Swap(&a[left], &a[right]);
  }
  Swap(&a[keyi], &a[left]);
  keyi = left;
  //递归
  QuickSort1(a, begin, keyi - 1);
  QuickSort1(a, keyi + 1, end);
}


特性分析:  

快速排序的综合性能和适用场景都比较好,大多数库里的排序都是快排;

时间复杂度:最好:O(N*logN)  最坏:O(N^2)  平均取O(N*logN);

空间复杂度:O(logN)   (递归创建函数栈帧)

稳定性:不稳定

以下另外两种版本与此版本的性能相同,便不再进行此特性分析


2.2挖坑版


这个方式的本质思想是没有变化的,就是把 key 值另外保存起来,形成一个坑位,在数据交换的过程中不断更新坑位:

c8350faf17fd2289f2d417f8b9c679d6_7aa24e08c4384c2d8c889f0225a58b82.gif

这种思路下,就不必让 R 优先走了,也可以让 L 优先走,因为最终都能保证相遇点比 key 小,图示默认让 R 优先走;


代码实现:

//快排(挖坑法)
void QuickSort2(int* a, int left, int right)
{
  if (left >= right)
    return;
  int begin = left, end = right;
  int key = a[left];
  int hole = left;
  while (left < right)
  {
    //右找小
    while (left < right && a[right] >= key)
      right--;
    a[hole] = a[right];
    hole = right;
    //左找大
    while (left < right && a[left] <= key)
      left++;
    a[hole] = a[left];
    hole = left;
  }
  a[hole] = key;
  //递归
  QuickSort2(a, begin, hole - 1);
  QuickSort2(a, hole + 1, end);
}


特征同霍尔版本  


2.3前后指针版


这个版本是最方便,最好理解的;

定义 curprev 两个下标,对应位置的值与 key 比较:

d8095c58bdb894d1dc51dce15897aaec_b4c457a8aa78485a94c149fdc9a837e0.gif

无论比较的值是大是小,cur 都要++。


代码实现:

//部分快排(前后指针法)
int PartSort3(int* a, int left, int right)
{
  int keyi = left;
  int key = a[left];
  int prev = left;
  int cur = left + 1;
  while (cur <= right)
  {
    if (a[cur] < key && ++prev != cur) // 保证 prev 在 cur 的左侧
      Swap(&a[cur], &a[prev]);
    cur++;
  }
  Swap(&a[keyi], &a[prev]);//与下标为keyi的交换
  keyi = prev;
  return keyi;
}

 

特征同霍尔版本


Part2:归并排序


归并排序


思想


所谓归并排序,就是采用分治法,将已有的有序子序列合并,得到完全有序的序列,以此类推,最终并得到的就是一个有序的数组。

90bd5dc53f8fb1c109e48d7206e47eb2_1485665f874343e682bcb7cfd2fcbeff.gif


实现


实现过程中,我们需要一块辅助空间来帮助归并,最先归并好的结果在那块辅助空间当中,最后再把结果拷贝到原数组空间中。

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);
  //拷贝
  int begin1 = begin,begin2 = mid + 1;
  int end1 = mid,end2 = end;
  int i = begin;                            
  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, sizeof(int) * (end - begin + 1));
}
//归并排序
void MergeSort(int* a, int n)
{
  int* tmp = (int*)malloc(sizeof(int) * n);
  if (tmp == NULL)
  {
    perror("malloc fail");
    return;
  }
    // 这里不能自己递归自己,否则会不断开辟空间
  _MergeSort(a, 0, n - 1, tmp);
  free(tmp);
}


特征分析:

归并排序效率不低,但需要开辟一块大小为N的空间,当数据量过大时,可以考虑在磁盘中排序。

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

空间复杂度:O(N)

稳定性:稳定


最后总结


屏幕截图 2023-04-16 190134.png

各排序对比


总结:

到这里,其实排序还并没有完结,我在后期会更新快速排序的优化以及快速排序和归并的非递归实现方式,有点小期待呢~

目录
相关文章
|
2月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
150 7
|
2月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
124 8
|
3月前
|
算法 搜索推荐 Shell
数据结构与算法学习十二:希尔排序、快速排序(递归、好理解)、归并排序(递归、难理解)
这篇文章介绍了希尔排序、快速排序和归并排序三种排序算法的基本概念、实现思路、代码实现及其测试结果。
52 1
|
3月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
44 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
3月前
|
数据可视化 搜索推荐 Python
Leecode 刷题笔记之可视化六大排序算法:冒泡、快速、归并、插入、选择、桶排序
这篇文章是关于LeetCode刷题笔记,主要介绍了六大排序算法(冒泡、快速、归并、插入、选择、桶排序)的Python实现及其可视化过程。
25 0
|
3月前
|
算法
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
36 0
|
3月前
|
存储 搜索推荐 算法
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
|
12天前
|
机器学习/深度学习 算法
基于改进遗传优化的BP神经网络金融序列预测算法matlab仿真
本项目基于改进遗传优化的BP神经网络进行金融序列预测,使用MATLAB2022A实现。通过对比BP神经网络、遗传优化BP神经网络及改进遗传优化BP神经网络,展示了三者的误差和预测曲线差异。核心程序结合遗传算法(GA)与BP神经网络,利用GA优化BP网络的初始权重和阈值,提高预测精度。GA通过选择、交叉、变异操作迭代优化,防止局部收敛,增强模型对金融市场复杂性和不确定性的适应能力。
145 80
|
6天前
|
机器学习/深度学习 算法
基于遗传优化的双BP神经网络金融序列预测算法matlab仿真
本项目基于遗传优化的双BP神经网络实现金融序列预测,使用MATLAB2022A进行仿真。算法通过两个初始学习率不同的BP神经网络(e1, e2)协同工作,结合遗传算法优化,提高预测精度。实验展示了三个算法的误差对比结果,验证了该方法的有效性。
|
8天前
|
机器学习/深度学习 数据采集 算法
基于PSO粒子群优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真
本项目展示了基于PSO优化的CNN-GRU-SAM网络在时间序列预测中的应用。算法通过卷积层、GRU层、自注意力机制层提取特征,结合粒子群优化提升预测准确性。完整程序运行效果无水印,提供Matlab2022a版本代码,含详细中文注释和操作视频。适用于金融市场、气象预报等领域,有效处理非线性数据,提高预测稳定性和效率。

热门文章

最新文章