数据结构——排序算法第一幕(插入排序:直接插入排序、希尔排序 选择排序:直接选择排序,堆排序)超详细!!!!

ops/2024/11/26 9:10:51/

在这里插入图片描述

文章目录

  • 前言
  • 一、排序
  • 二、插入排序
    • 2.1 直接插入排序
    • 2.2 希尔排序
      • 希尔排序的时间复杂度
  • 三、选择排序
    • 3.1 直接选择排序
    • 3.2 堆排序
  • 总结

前言

时间很快,转眼间已经到数据结构算法>排序算法部分啦
今天我们来学习算法>排序算法当中的 插入排序选择排序
准备好上车了,fellow me

一、排序

1.1 概念

所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

1.2 常见的算法>排序算法

在这里插入图片描述

今天我们要学习的就是插入排序和选择排序

二、插入排序

基本思想:
直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

在这里插入图片描述
实际中我们玩扑克牌时,就用了插入排序的思想

2.1 直接插入排序

当插入第i(i>=1) 个元素时,前面的array[0],array[1],…,array[i-1] 已经排好序,此时用 array[i] 的排序码与array[i-1],array[i-2],… 的排序码顺序进行比较,找到插入位置即将array[i] 插入,原来位置上的元素顺序后移
就像打扑克时,抓到一张新的牌,就一一与前面的牌进行比较,找到适合的位置插入。

实现起来还是相对比较简单的

void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++)   //  一个一个插入    {int end = i;          // end 为以及排好序列的最后一个元素   int tmp = a[end + 1];    //  tmp  表示新插入进来的元素   while (end >= 0)    //   现在让新插入的元素  与前面的元素一一比较  {if (a[end] > tmp)   //  如果tmp比当前的元素小  就把当前的元素往后移动{a[end + 1] = a[end];end--;   //   end--  一个一个往前比较   }else  //  如果tmp比当前元素大  那就已经找到合适位置  break;   }a[end + 1] = tmp;  //  元素往后移动了一位 前面肯定是有空位的  把tmp插入就好啦  }    //  这里  end是最近比较过的元素  tmp大于end位置    end+1是空缺的   所以插入到 end+1的 位置    
}

结合着打扑克牌的插入的情景就很好理解了

直接插入排序的特性总结
1.== 元素集合越接近有序,直接插入算法>排序算法的时间效率越高==
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)

但 O(N^ 2) 终究还是 O(N^2) 太慢啦 下面我来优化优化 直接插入排序的进阶版本

2.2 希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数(通常是gap = n/3+1),把待排序文件所有记录分成各组,所有的距离相等的记录分在同一组内,并对每一组内的记录进行排序然后gap=gap/3+1得到下一个整数,再将数组分成各组,进行插入排序,当gap=1时,就相当于直接插入排序。
它是在直接插入算法>排序算法基础上进行改进而来的,综合来说它的效率肯定是要高于直接插入算法>排序算法的。

有图才能有真相
在这里插入图片描述
一组一组来使用直接插入,组的间隙慢慢变小,最后一层就是直接插入排序,这种方式相比于直接插入排序
外层的 ++循环变成了,gap的递减,从O(N)到 O(logN)
代码优化起来也很简单,在原来的基础上改动一点就好啦

void ShellSort(int* a, int n)
{int gap = n;   //  引入gapwhile (gap > 1)   // 外层循环   {gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++)   //  这里直接i截止到n-gap  防止取tmp的时候数组越界{int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}elsebreak;}a[end + gap] = tmp;   //  这些操作还是 直接插入排序的代码 直接插入排序就是gap==1的情况  }}
}

可能会有人疑问 虽然外层循环优化了,循环内部的代码和直接插入一样的呀
我来仔细讲解

希尔排序的时间复杂度

希尔排序的时间复杂度估算:
外层循环:
外层循环的时间复杂度可以直接给出为: O(log2(n)) O(log3(n)) 即O(log n)
内层循环:

在这里插入图片描述
在这里插入图片描述
可以画出曲线图:
在这里插入图片描述

因此,希尔排序在最初和最后的排序的次数都为n,即前一阶段排序次数是逐渐上升的状态,当到达某一顶点时,排序次数逐渐下降至n,而该顶点的计算暂时无法给出具体的计算过程

在这里插入图片描述

所以希尔排序的时间复杂度为 :O(nlog n) ~ O(n^2) 最好的情况为O(n ^1.3) 最坏的情况为O(n ^2)

插入排序就到这里啦


三、选择排序

选择排序的基本思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

3.1 直接选择排序

  1. 在元素集合array[i]–array[n-1] 中选择关键码最大(小)的数据元素
  2. 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
  3. 在剩余的array[i]–array[n-2](array[i+1]–array[n-1]) 集合中,重复上述步骤,直到集合剩余1 个元素

通俗一点来讲,先遍历一遍,找一个最小的数据,放第一个,然后遍历第二个到最后一个数据,找最小放在第二个
这样一直遍历,放置,遍历,放置,数据就排好了。

代码实现起来也是很简单的

void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;   while (begin < end){int mini = begin, maxi = begin;for (int i = begin; i < end; i++)   //  每一躺找出最大和最小值  {if (a[i] < a[mini]){mini = i;}if (a[i] > a[maxi]){maxi = i;}}if (maxi == begin)   //  注意有最大值在当前区域的第一个位置  如果先把最小的换过去,最大的位置就变了{maxi = mini;   //   所以这里要处理一下  }swap(a[mini], a[begin]);swap(a[maxi], a[end]);   //  把最小换到前面,把最大换到后面begin++;   //  数组需要遍历的元素减少两个  每次都排一个当前区域的最大值和最小值end--;}
}

直接选择排序的特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2) 注定是上不了台面
  3. 空间复杂度: O(1)

3.2 堆排序

博客链接:堆排序

堆排序在之前的二叉树第一幕已经讲过啦
这里就不多赘述了,主要是深刻理解向下调整算法和向上调整算法
实现一下代码吧

void AdjustDown(int* a, int parent, int n)
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child] > a[child + 1]){child++;}if (a[child] < a[parent]){swap(a[child], a[parent]);parent = child;child = parent * 2 + 1;}elsebreak;}
}
void HeapSort(int *a, int n)
{for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, i, n);}int end = n - 1;while (end > 0){swap(a[0], a[end]);AdjustDown(a, 0, end);end--;}
}

堆排序就到这里啦,以上代码供复习使用,详情去 ----博客链接:堆排序

总结

今天就学习了插入排序和选择排序,总体来说就是希尔排序有一些难处理堆排序在前面的博客已经讲过啦
其他的都挺简单的,平常也很容易想到
下一篇,小编可是要放大招了嗷,听说快速排序用起来可是非常爽的,不要走开,小编持续更新中~~~

欲望以提升热忱,毅力以磨平高山。又是充实的一天

在这里插入图片描述


http://www.ppmy.cn/ops/136794.html

相关文章

docker部署redis,并设置密码

获取官方镜像 redis:7.4.1 准备配置文件 文件 redis.conf requirepass xxx密码请自行设置 准备启动脚本 start_redis.sh 注意&#xff1a; 要把这个脚本和上面的redis.conf放到同一个目录下。 workdir$(cd $(dirname $0); pwd) redis_id"$(docker ps -a| grep red…

C# Winform 五子棋小游戏源码

文章目录 1.设计来源五子棋小游戏讲解1.1 主界面1.2 对弈棋盘界面1.3 对弈结束界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/…

图像拟合算法全解析:从基础原理到前沿实践

摘要&#xff1a; 本文全面且深入地阐述了多种图像拟合算法&#xff0c;涵盖线性回归、多项式拟合、最小二乘法拟合、高斯拟合以及基于深度学习的图像拟合方法等。针对每种算法&#xff0c;详细剖析其原理、数学模型、具体实现步骤&#xff0c;并细致探讨它们的优缺点与适用场景…

redis的map底层数据结构 分别什么时候使用哈希表(Hash Table)和压缩列表(ZipList)

在Redis中&#xff0c;Hash数据类型的底层数据结构可以是压缩列表&#xff08;ZipList&#xff09;或者哈希表&#xff08;HashTable&#xff09;。这两种结构的使用取决于特定的条件&#xff1a; 1. **使用ZipList的条件**&#xff1a; - 当Hash中的数据项&#xff08;即f…

健身房小程序服务渠道开展

健身不单单是锻炼身体、保持身材&#xff0c;也是一种社交方式&#xff0c;城市里门店不少&#xff0c;每家都有一定流量和老客&#xff0c;但仅靠传统线下拉客/自然流量前往和线上朋友圈、短视频发硬广等方式还不够。 商家需要找到更多潜在目标客户&#xff0c;而消费者也对门…

【MySQL】视图

1.表格形式展示视图的作用 作用详细解释示例场景简化复杂查询将复杂的SQL查询封装成视图&#xff0c;用户通过简单的查询访问结果&#xff0c;减少重复书写复杂SQL。一个复杂的销售报表统计查询&#xff0c;可以创建为视图&#xff0c;用户只需查询视图名称即可获得结果。提高…

GitLab 使用过程中常见问题及解决方案

开发人员常见问题及解决方案 合并请求被拒绝 原因&#xff1a;代码质量问题、安全漏洞或流水线失败。解决方案&#xff1a; 使用 Code Quality 工具检查代码质量。查看流水线日志&#xff0c;修复单元测试、编译错误或扫描问题。优化静态分析&#xff08;SAST&#xff09;结果&…

认识网络安全

一 网络攻击链 踩点-工具准备-载荷投递-漏洞利用-释放载荷-建立通道-目标达成 简化下&#xff1a; 目标侦察&#xff1a;准确识别目标&#xff0c;收集目标详细信息&#xff0c;比如 网络、 邮箱、员工、社会关系、对外提供服务、漏洞 信息等&#xff0c;为 后续攻击做准备。…