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

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

冒泡排序

冒泡排序的思想是每一趟排序都将最值放到最右边,比如现在要排的是升序,则一趟冒泡排序就可以将最大值放到右边,每一趟都将剩余数的最大值放到最右边。需要进行 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)

是一个不稳定的排序方法


目录
相关文章
|
2月前
|
算法 搜索推荐 JavaScript
NodeJ实现冒泡算法
NodeJ实现冒泡算法
26 0
|
28天前
|
搜索推荐 算法
【排序】数据结构——排序算法概念及代码详解(插入、冒泡、快速、希尔)
【排序】数据结构——排序算法概念及代码详解(插入、冒泡、快速、希尔)
|
24天前
|
人工智能 算法 搜索推荐
蓝桥杯宝藏排序题目算法(冒泡、选择、插入)
以下是内容的摘要: 本文介绍了三种排序算法:冒泡排序、选择排序和插入排序。冒泡排序通过不断交换相邻的逆序元素逐步排序,最坏情况下需要 O(n^2) 次比较。选择排序在每轮中找到剩余部分的最小元素并放到已排序序列的末尾,同样具有 O(n^2) 时间复杂度。插入排序则是将每个元素插入到已排序序列的正确位置,时间复杂度也是 O(n^2),但空间复杂度为 O(1)。
|
2月前
|
存储 算法 搜索推荐
【算法】七大经典排序(插入,选择,冒泡,希尔,堆,快速,归并)(含可视化算法动图,清晰易懂,零基础入门)
【算法】七大经典排序(插入,选择,冒泡,希尔,堆,快速,归并)(含可视化算法动图,清晰易懂,零基础入门)
104 1
|
2月前
|
搜索推荐
图解基础排序算法(冒泡、插入、选择)(山东大学实验二)
图解基础排序算法(冒泡、插入、选择)(山东大学实验二)
|
1天前
|
算法 数据安全/隐私保护
基于GA遗传优化算法的Okumura-Hata信道参数估计算法matlab仿真
在MATLAB 2022a中应用遗传算法进行无线通信优化,无水印仿真展示了算法性能。遗传算法源于Holland的理论,用于全局优化,常见于参数估计,如Okumura-Hata模型的传播损耗参数。该模型适用于150 MHz至1500 MHz的频段。算法流程包括选择、交叉、变异等步骤。MATLAB代码执行迭代,计算目标值,更新种群,并计算均方根误差(RMSE)以评估拟合质量。最终结果比较了优化前后的RMSE并显示了SNR估计值。
15 7
|
3天前
|
算法 数据挖掘
MATLAB数据分析、从算法到实现
MATLAB数据分析、从算法到实现
|
10天前
|
机器学习/深度学习 算法 调度
Matlab|基于改进鲸鱼优化算法的微网系统能量优化管理matlab-源码
基于改进鲸鱼优化算法的微网系统能量管理源码实现,结合LSTM预测可再生能源和负荷,优化微网运行成本与固定成本。方法应用于冷热电联供微网,结果显示经济成本平均降低4.03%,提高经济效益。代码包括数据分段、LSTM网络定义及训练,最终展示了一系列运行结果图表。
|
10天前
|
算法
基于Dijkstra算法的最优行驶路线搜索matlab仿真,以实际城市复杂路线为例进行测试
使用MATLAB2022a实现的Dijkstra算法在城市地图上搜索最优行驶路线的仿真。用户通过鼠标点击设定起点和终点,算法规划路径并显示长度。测试显示,尽管在某些复杂情况下计算路径可能与实际有偏差,但多数场景下Dijkstra算法能找到接近最短路径。核心代码包括图的显示、用户交互及Dijkstra算法实现。算法基于图论,不断更新未访问节点的最短路径。测试结果证明其在简单路线及多数复杂城市路况下表现良好,但在交通拥堵等特殊情况下需结合其他数据提升准确性。
|
15天前
|
算法 安全 数据库
基于结点电压法的配电网状态估计算法matlab仿真
**摘要** 该程序实现了基于结点电压法的配电网状态估计算法,旨在提升数据的准确性和可靠性。在MATLAB2022a中运行,显示了状态估计过程中的电压和相位估计值,以及误差随迭代变化的图表。算法通过迭代计算雅可比矩阵,结合基尔霍夫定律解决线性方程组,估算网络节点电压。状态估计过程中应用了高斯-牛顿或莱文贝格-马夸尔特法,处理量测数据并考虑约束条件,以提高估计精度。程序结果以图形形式展示电压幅值和角度估计的比较,以及估计误差的演变,体现了算法在处理配电网状态估计问题的有效性。