七个常用的排序算法---快排\归排\希尔\插入\选择\冒泡\堆排(一)

简介: 七个常用的排序算法---快排\归排\希尔\插入\选择\冒泡\堆排(一)

冒泡排序

冒泡排序的思想是每一趟排序都将最值放到最右边,比如现在要排的是升序,则一趟冒泡排序就可以将最大值放到右边,每一趟都将剩余数的最大值放到最右边。需要进行 n - 1趟排序。第一次比较 n - 1次,第二次 n - 2次,以此类推

以下为动图演示与代码实现:


5b5ed7e93e4b40658385564656c5bba1.gif

//冒泡排序
void Bubble(vector<int>& v) {
  for (int i = 0; i < v.size() - 1; ++i) {
    for (int j = 0; j < v.size() - i - 1; ++j) {
      //比较交换
      if (v[j] > v[j + 1]) {
        int tmp = v[j];
        v[j] = v[j + 1];
        v[j + 1] = tmp;
      }
    }
  }
}

冒泡排序是一种稳定的排序,其时间复杂度为O(N ^ 2)。效率并不是很好,不管是最好还是最坏情况。

选择排序

选择排序的思想与冒牌排序很相似。选择排序也是需要比较大小,以升序为例,其每一趟排序都会把数组的头元素当成是最小元素,然后遍历数组找到除头元素外的最小元素,最后两个元素比较将最小的元素放置数组头元素,这样一趟排序下来最小的元素就到了数组首元素了。需要进行n - 1趟排序,第一次比较 n - 1次,第二次 n - 2次,以此类推

以下为动图演示与代码实现:


96a6f06a75744b63b08af66d0601711f.gif

//选择排序
void Select(vector<int>& v) {
  for (int i = 0; i < v.size() - 1; ++i) {
    int mini = i + 1;
    for (int j = i + 1; j < v.size(); ++j) {
      if (v[j] < v[mini])
        mini = j;
    }
    if (v[i] > v[mini]) {
      int tmp = v[i];
      v[i] = v[mini];
      v[mini] = tmp;
    }
  }
}

选择排序的时间复杂度为O(N ^ 2),效率较低且不稳定。

插入排序

插入排序的思想为:遍历数组,每一个元素前的所有元素都看成是一个有序数组,然后将当前元素寻找在前面那个数组的合适位置进行插入。需要进行n - 1 趟排序

以下为动图演示与代码实现:


3b1c7dd216d040c5a1d4969485afae4d.gif

//插入排序
void Insert(vector<int>& v) {
  for (int i = 0; i < v.size() - 1; ++i) {
    int end = i;
    int tmp = v[end + 1];
    while (end >= 0) {
      if (tmp < v[end]) {
        v[end + 1] = v[end];
        --end;
      }
      else
        break;
    }
    v[end + 1] = tmp;
  }
}

插入排序的时间复杂度为O(N ^ 2),但是数组越接近有序它的效率就越高。是一个稳定的排序

希尔排序

希尔排序法的基本思想是先选定一个整数,把待排序文件中所有记录分成整数个组,所有距离为的记录分在同一组内,并对每一组进行插入排序。然后改变整数值,重复上述分组和排序的工作。当到达整数=1时最后一次整体插入排序

c40fe516bc02b24c93314d616e31dae8.png


因为插入排序在接近有序的情况下效率会更好,所以希尔排序是对直接插入排序的一种优化,通过预排序达到接近有序的目的

//希尔排序
void Shell(vector<int>& v) {
  int gap = v.size();
  while (gap > 1) {
    //gap值
    gap = gap / 3 + 1;
    //每一组的每个元素进行插入排序
    for (int i = 0; i < v.size() - gap; ++i) {
      int end = i;
      int tmp = v[end + gap];
      while (end >= 0) {
        if (tmp < v[end]) {
          v[end + gap] = v[end];
          end -= gap;
        }
        else
          break;
      }
      v[end + gap] = tmp;
    }
  }
}

希尔排序是一种不稳定的排序,但是其本身的分析非常的复杂。《数据结构(C语言版)》 — 严蔚敏,这本书提到希尔排序的时间是所取“增量”序列的函数,涉及一些数学上还未解决的难题。

对于希尔排序的gap值可以采用Knuth提出的方法取值,这种方法取值得出的复杂度在 O(n ^ 1.25) – O(1.6 * n ^ 1.25)这个区间内,可见效率还是比较可观的

堆排序

堆排序的思想与其大堆和小堆的性质相关。以升序为例:首先可以知道的是大堆的根节点是整个堆中最大的,因此当升序时只需要将根节点和最后的节点一交换,最大值就跑到了最后面了,这就符合了升序的规则。以此类堆下去,每一次交换后就固定住往下走的那个大节点,交换到根节点的就向下调整,每次都保证除了固定住的节点外其他节点符合大堆,这样每次都可以保证根节点是当前的最大值


2bb90a1a451afc40341d00e8a098644b.png

关于向下调整

向下调整的思路就是将当前指定的节点向下去找合适的位置,以大堆为例,就拿当前节点和其最大的那个子节点去比较,如果子节点比其大,那两个就交换。以此反复直至遇到当前节点比其最大的子节点还要大或者整颗树的节点已经遍历完了。下图示例单个节点向下调整情况

cd0e3063fabaf182cf73226dcbed9280.gif

关于堆排

对一个数组进行堆排序,以升序为例:首先需要将数组建成大堆结构,常用的方法为从数组的倒数第一个非叶子节点开始直至根节点,依次向下调整。数组为堆结构后,循环n - 1次,每一次都将当前根节点也就是当前最大值固定到数组后面,固定一次后,要将交换后的根节点向下调整保证堆结构不被破坏

代码示例:

//堆排序
//向下调整
void AdJustDown(vector<int>& v, int n, int parent) {
  //因为父节点可能会有两个子节点,因此需要找到最大的子节点
  //先默认左子节点为大的,在进行比较
  int minchild = parent * 2 + 1;
  while (minchild < n) {
    //找出最小子节点
    if (v[minchild] < v[minchild + 1] && minchild + 1 < n)
      ++minchild;
    //大堆
    if (v[parent] < v[minchild]) {
      swap(v[parent], v[minchild]);
      parent = minchild;
      minchild = parent * 2 + 1;
    }
    else
      break;
  }
}
void Heap(vector<int>& v) {
  //建堆
  //从倒数第一个非叶子节点开始调整
  for (int i = (v.size() - 1 - 1) / 2; i >= 0; i--)
  {
    AdJustDown(v, v.size(), i);
  }
  //排序
  int i = 1;
  while (i < v.size())
  {
    //交换根节点与最后一个节点
    swap(v[0], v[v.size() - i]);
    //根节点向下调整
    AdJustDown(v, v.size() - i, 0);
    ++i;
  }
}

排升序建大堆,降序建小堆—思路都是将最大或最小节点调整到根节点方便与最后节点进行交换

堆排序的效率很高,时间复杂度为O(N * logN),空间复杂度O(1)

是一个不稳定的排序方法


目录
相关文章
|
1月前
|
数据可视化 搜索推荐 Python
Leecode 刷题笔记之可视化六大排序算法:冒泡、快速、归并、插入、选择、桶排序
这篇文章是关于LeetCode刷题笔记,主要介绍了六大排序算法(冒泡、快速、归并、插入、选择、桶排序)的Python实现及其可视化过程。
13 0
|
1月前
|
搜索推荐 算法
排序算法---冒泡&选择&插入总结
排序算法---冒泡&选择&插入总结
14 0
|
3月前
|
搜索推荐 算法
十大排序算法-快排-希尔-堆排-归并-冒泡-桶排-选择-插入-计数-基数
十大排序算法-快排-希尔-堆排-归并-冒泡-桶排-选择-插入-计数-基数
十大排序算法-快排-希尔-堆排-归并-冒泡-桶排-选择-插入-计数-基数
|
3月前
|
数据采集 搜索推荐 算法
【高手进阶】Java排序算法:从零到精通——揭秘冒泡、快速、归并排序的原理与实战应用,让你的代码效率飙升!
【8月更文挑战第21天】Java排序算法是编程基础的重要部分,在算法设计与分析及实际开发中不可或缺。本文介绍内部排序算法,包括简单的冒泡排序及其逐步优化至高效的快速排序和稳定的归并排序,并提供了每种算法的Java实现示例。此外,还探讨了排序算法在电子商务、搜索引擎和数据分析等领域的广泛应用,帮助读者更好地理解和应用这些算法。
41 0
|
5月前
|
人工智能 算法 搜索推荐
蓝桥杯宝藏排序题目算法(冒泡、选择、插入)
以下是内容的摘要: 本文介绍了三种排序算法:冒泡排序、选择排序和插入排序。冒泡排序通过不断交换相邻的逆序元素逐步排序,最坏情况下需要 O(n^2) 次比较。选择排序在每轮中找到剩余部分的最小元素并放到已排序序列的末尾,同样具有 O(n^2) 时间复杂度。插入排序则是将每个元素插入到已排序序列的正确位置,时间复杂度也是 O(n^2),但空间复杂度为 O(1)。
|
24天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
9天前
|
算法 数据挖掘 数据安全/隐私保护
基于FCM模糊聚类算法的图像分割matlab仿真
本项目展示了基于模糊C均值(FCM)算法的图像分割技术。算法运行效果良好,无水印。使用MATLAB 2022a开发,提供完整代码及中文注释,附带操作步骤视频。FCM算法通过隶属度矩阵和聚类中心矩阵实现图像分割,适用于灰度和彩色图像,广泛应用于医学影像、遥感图像等领域。
|
10天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。
|
11天前
|
存储 算法 决策智能
基于免疫算法的TSP问题求解matlab仿真
旅行商问题(TSP)是一个经典的组合优化问题,目标是寻找经过每个城市恰好一次并返回起点的最短回路。本文介绍了一种基于免疫算法(IA)的解决方案,该算法模拟生物免疫系统的运作机制,通过克隆选择、变异和免疫记忆等步骤,有效解决了TSP问题。程序使用MATLAB 2022a版本运行,展示了良好的优化效果。
|
10天前
|
机器学习/深度学习 算法 芯片
基于GSP工具箱的NILM算法matlab仿真
基于GSP工具箱的NILM算法Matlab仿真,利用图信号处理技术解析家庭或建筑内各电器的独立功耗。GSPBox通过图的节点、边和权重矩阵表示电气系统,实现对未知数据的有效分类。系统使用MATLAB2022a版本,通过滤波或分解技术从全局能耗信号中提取子设备的功耗信息。