7大排序算法-- 直接插入,希尔,冒泡,选择 --精解(上)

简介: 7大排序算法-- 直接插入,希尔,冒泡,选择 --精解

直接插入排序:

插入排序整体来看还是一个挺简单的排序  可以这么比喻 有一群士兵 每个人都有属于自己的编号 但编号是随机的 现在要求他们迅速地按照编号排成一排 那每个士兵都要根据自己的编号去找属于自己的位置  插入排序的思想 跟这个类似

71b0fc818ff3494fbe0a876a19ede131.gif

如上图 我们一开始的数据是有序的 现在插入个数 我们要保证插入数据后我们的这4个数据也是有序的 我们实现的步骤:

① 把插入的数据有一个变量存储(这里我用tmp)

② 从插入数据的前一个数据依次遍历 与tmp比较 如果比tmp大则该数据往后移一位 如果比tmp小 则tmp填入它的前面


如果说给我们一个数组 我们只需把第一个数据看成有序 第二个数据是插入的数据 把他们排成有序 根据这样的思路以此类推 得出这样的代码:

void InsertSort(int* a, int n)//插入排序
{
  for (int i = 1; i < n; i++)
  {
    int end = i;
    int tmp = a[end];
    while (end > 0)
    {
      if (tmp < a[end - 1])
      {
        a[end] = a[end - 1];
        end--;
      }
      else
      {
                a[end] = tmp;
        break;
      }
    }
  }
}

 我这里是把插入数据的位置作为起始的遍历的位置 把它与前一个数进行比较 当然也可以把插入数据的前一个数据作为比较的起始位置 只不过最后填入数据的时候是 a[end+1]=tmp 具体的边界判断大家下去可以试试


回归我们之前的问题 大家看了上面的代码 觉得这样对嘛? 


其实有一个边界的问题不知道大家是否注意到了没有

就是假设现在有两个数据 第二个数据为插入的数据 此时我们的end指向第二个数据(end=1) 我们与第一个数据比较 假设比较的结果是 第二个数据 要和 第一个数据 交换那么交换后end指向第一个数据 的位置(end=0) 此时我们想把tmp放进去 可是 此时还能进入while的循环嘛?  是不是就不能了 可是我们的tmp还没放进去呢 这样不就出问题了吗?


 也许你会想 “那我们把while(end>0)改成while(end>=0)不就行了嘛” 真的这样就可以纠正问题了嘛?

如果改成那样  现在我们继续刚刚的操作 我们此时 end=0 进去了 比较的时候我们与 end-1 位置的数据比较 0-1=-1 这样是不是越界了呢?所以这样是不可行的

咋们正确的做法应该是在while循环的外面进行tmp的填入:

void InsertSort(int* a, int n)//插入排序
{
  for (int i = 1; i < n; i++)
  {
    int end = i;
    int tmp = a[end];
    while (end > 0)
    {
      if (tmp < a[end - 1])
      {
        a[end] = a[end - 1];
        end--;
      }
      else
      {
        break;
      }
    }
    a[end] = tmp;
  }
}

大家这样看 我们此时无论是end=0跳出来 还是比较时跳出来 都会执行tmp的填入 这样不就解决了嘛 大家看是不是这个道理?


直接插入排序的时间复杂度:

现在大家看完插入排序的代码实现 大家算一算这个插入排序的时间复杂度时多少呢?


 首先 我们的最坏的时间复杂度时O(n^2) :这种情况就是我们每次进行插入数据的操作 每次插入数据的前面的每个数据都会往后移动 也就是 9 8 7 6 5 4 3 2 1 排成升序

其次 我们最好的时间复杂度是O(n) 也就是 给我们的数据本身就是有序的 也就只会遍历一遍数据就没了

虽然我们最坏的时间复杂度是O(n^2) 但是这并不代表我们的插入排序就不好了 毕竟一个完全没有顺序的数组还是少见 这样看来内 我们的时间复杂也就没那么大了

我们的直接插入排序整体来讲还是挺好的

稳定性:稳定


冒泡排序:

相信对于这个排序大家应该也不陌生  我们就是 依次遍历 两两比较 每次遍历结束 至少有一个数会被放到它该有的位置

void swap(int* p, int* q)
{
  int tmp = *p;
  *p = *q;
  *q = tmp;
}
void BubbleSort(int* a, int n)//我这里的n是总的数组大小
{
  for (int i = 0; i < n - 1; i++)
  {
    for (int j = 0; j < n - 1 - i; j++)
    {
      if (a[j] > a[j + 1])
      {
        swap(a + j, a + j + 1);
      }
    }
  }
}


冒泡排序的时间复杂度:

这样写出的代码大家来看一下我们的时间复杂度是多少呢?


很显然 我们最坏的时间复杂度:O(n^2)  稳定性:稳定

我们最好的时间复杂度:O(n^2) (因为哪怕我们现在给的数组是有序的 我们还是会全部遍历完才结束)  

那我们是不是可以改进下呢 让它不必每次都要去遍历?

void BubbleSort(int* a, int n)
{
  for (int i = 0; i < n - 1; i++)
  {
        int flag=0;
    for (int j = 0; j < n - 1 - i; j++)
    {
      if (a[j] > a[j + 1])
      {
                flag=1;
        swap(a + j, a + j + 1);
      }
    }
        if(!flag) break;
  }
}

 大家看这样是不是就解决了呢 我们第一遍遍历 如果没有发生交换 那么flag依旧等于0 下一次遍历之前判断 flag是否等于0 如果等于说明 数组已经有序不必再继续遍历了


那现在我们的插入排序 和冒泡排序(改过后)的时间复杂度是差不多的 那到底哪一个会更好一点内? 

75ef313f99ea4170b2aab1e57c031e54.png

大家觉得上面的数组 用插入排序 和 冒泡排序 哪一个会更好呢?

我们比较 他们数之间的比较次数就可以了  大家数一下是多少呢?

插入排序:8次 冒泡排序:7+6次 是不是这样呢

所以我们这样来看的话 还是我们的直接插入排序会更好一点!


总的来讲:如果顺序有序的话 那插入和冒泡是一样的  但如果是局部有序的话或者说是接近有序 那么插入排序的适应性就更好 比较次数也更少  

目录
相关文章
|
2月前
|
算法 搜索推荐 JavaScript
NodeJ实现冒泡算法
NodeJ实现冒泡算法
25 0
|
2月前
|
算法 搜索推荐 Java
Java实现冒泡算法
Java实现冒泡算法
22 0
|
2月前
|
算法
电子好书发您分享《超全算法笔试 模拟题精解合集》
电子好书发您分享《超全算法笔试 模拟题精解合集》
32 3
|
17天前
|
搜索推荐 算法
【排序】数据结构——排序算法概念及代码详解(插入、冒泡、快速、希尔)
【排序】数据结构——排序算法概念及代码详解(插入、冒泡、快速、希尔)
|
13天前
|
人工智能 算法 搜索推荐
蓝桥杯宝藏排序题目算法(冒泡、选择、插入)
以下是内容的摘要: 本文介绍了三种排序算法:冒泡排序、选择排序和插入排序。冒泡排序通过不断交换相邻的逆序元素逐步排序,最坏情况下需要 O(n^2) 次比较。选择排序在每轮中找到剩余部分的最小元素并放到已排序序列的末尾,同样具有 O(n^2) 时间复杂度。插入排序则是将每个元素插入到已排序序列的正确位置,时间复杂度也是 O(n^2),但空间复杂度为 O(1)。
|
2月前
|
算法 搜索推荐 Java
Java数据结构 -- 常见算法分析(查找算法、排序算法)精解详解!!!
Java数据结构 -- 常见算法分析(查找算法、排序算法)精解详解!!!
19 0
|
2月前
|
存储 算法 搜索推荐
【算法】七大经典排序(插入,选择,冒泡,希尔,堆,快速,归并)(含可视化算法动图,清晰易懂,零基础入门)
【算法】七大经典排序(插入,选择,冒泡,希尔,堆,快速,归并)(含可视化算法动图,清晰易懂,零基础入门)
103 1
|
2月前
|
搜索推荐
图解基础排序算法(冒泡、插入、选择)(山东大学实验二)
图解基础排序算法(冒泡、插入、选择)(山东大学实验二)
|
2月前
|
算法 C语言
C语言数组实例(冒泡算法、猜数字)
C语言数组实例(冒泡算法、猜数字)
22 0
|
4天前
|
算法 安全 数据库
基于结点电压法的配电网状态估计算法matlab仿真
**摘要** 该程序实现了基于结点电压法的配电网状态估计算法,旨在提升数据的准确性和可靠性。在MATLAB2022a中运行,显示了状态估计过程中的电压和相位估计值,以及误差随迭代变化的图表。算法通过迭代计算雅可比矩阵,结合基尔霍夫定律解决线性方程组,估算网络节点电压。状态估计过程中应用了高斯-牛顿或莱文贝格-马夸尔特法,处理量测数据并考虑约束条件,以提高估计精度。程序结果以图形形式展示电压幅值和角度估计的比较,以及估计误差的演变,体现了算法在处理配电网状态估计问题的有效性。