复习梗概

文章目录

  • 复习梗概
  • 插入排序算法思想
  • 插入排序时间复杂度与特性(多少,与什么有关?)
  • 插入排序基础版
  • 插入排序2nd优化版(优化了哪里?)
  • !!!插入排序二分搜索优化版(优化了哪里?如何优化?优化后的二分搜索判定条件?)
    • 关键在于理解新的二分搜索,搜索合适的插入位置如何实现
  • 完整代码

插入排序算法思想

想象插入排序就是两只手,一只手里的牌是有序的,一只是无序的,每次把无序的手里的牌的第一张,与有序的牌逐个比较,插入有序牌堆的合适位置


插入排序时间复杂度与特性(多少,与什么有关?)

  1. 插入排序的时间复杂度: 与数组中的逆序对有关
  2. 逆序对:比如想要递增的数组【0,8,9,1】这里【8,1】【9,1】都是逆序对
  3. 最坏时间复杂度:O(n2)(输入数组完全逆序),最好时间复杂度O(n)(输入数组已经有序),平均时间复杂度O(n2),空间复杂度O(1)
  4. 即使应用了二分搜索优化,时间复杂度还是O(n2),因为避不开要挪元素腾位置插入
  5. 属于原地稳定排序算法
  6. 排序算法在逆序对特别少的数组中效率很高,甚至可能比O(nlogn)级别的堆排序和快速排序还快

插入排序基础版

void insertionSort1th(vector<int> &array) //这里以数组左侧作为有序的那一边
{// chaosBeginIndex:未排序序列的起始元素下标// insertEIndex:拿去插入有序序列的那个元素的下标,就是未排序序列的起始元素,但随着交换位置,下标会改变for (int chaosBeginIndex = 1; chaosBeginIndex < array.size(); chaosBeginIndex++) //外循环控制从第二个元素开始插入到有序序列里,直到最后一个元素{for (int insertEIndex = chaosBeginIndex; insertEIndex > 0; insertEIndex--)//内循环控制从未排序序列的首元素开始,逐渐与有序序列元素比较和交换位置,找到有序序列中的合适位置{if (array[insertEIndex] < array[insertEIndex - 1]){int temp = array[insertEIndex];array[insertEIndex] = array[insertEIndex - 1];array[insertEIndex - 1] = temp;}else{break;}}vectorPrint(array);}
}void insertionSort1thRe(vector<int> &array) //这里以数组右侧作为有序的那一边
{for (int chaosBeginIndex = array.size() - 1 - 1; chaosBeginIndex >= 0; chaosBeginIndex--){for (int insertEIndex = chaosBeginIndex; insertEIndex < array.size() - 1; insertEIndex++){if (array[insertEIndex] > array[insertEIndex + 1]){int temp = array[insertEIndex];array[insertEIndex] = array[insertEIndex + 1];array[insertEIndex + 1] = temp;}else{break;}}vectorPrint(array);}
}
输入数组:
6 9 3 1 2 0 8 29 15 11 10
插入排序基础版
6 9 3 1 2 0 8 29 15 11 10
3 6 9 1 2 0 8 29 15 11 10
1 3 6 9 2 0 8 29 15 11 10
1 2 3 6 9 0 8 29 15 11 10
0 1 2 3 6 9 8 29 15 11 10
0 1 2 3 6 8 9 29 15 11 10
0 1 2 3 6 8 9 29 15 11 10
0 1 2 3 6 8 9 15 29 11 10
0 1 2 3 6 8 9 11 15 29 10
0 1 2 3 6 8 9 10 11 15 29
算法用时:(微秒)
[AlgoTime: 10002 us]
输入数组:
6 9 3 1 2 0 8 29 15 11 10
插入排序基础版(换一个方向)
6 9 3 1 2 0 8 29 15 10 11
6 9 3 1 2 0 8 29 10 11 15
6 9 3 1 2 0 8 10 11 15 29
6 9 3 1 2 0 8 10 11 15 29
6 9 3 1 2 0 8 10 11 15 29
6 9 3 1 0 2 8 10 11 15 29
6 9 3 0 1 2 8 10 11 15 29
6 9 0 1 2 3 8 10 11 15 29
6 0 1 2 3 8 9 10 11 15 29
0 1 2 3 6 8 9 10 11 15 29
算法用时:(微秒)
[AlgoTime: 10003 us]

插入排序2nd优化版(优化了哪里?)

  1. 思路是将交换改为了挪动
  2. 就是把左边比我大的元素都要跟我交换,换成了左边比我大的元素都要往右移
  3. 流程图来自腾讯课堂-恋上数据结构与算法第二季-李明杰老师
//插入排序的第一次优化,就是把不断的交换位置,改成了找到位置后插入数组,数组元素后移覆盖
//就是把左边比我大的元素都要跟我交换,换成了左边比我大的元素都要往右移
void insertionSort2nd(vector<int> &array) //这里以数组左侧作为有序的那一边
{for (int chaosBeginIndex = 1; chaosBeginIndex < array.size(); chaosBeginIndex++){int insertENum = array[chaosBeginIndex]; //记录插入元素的元素内容,因为后面原本位置会被覆盖int rightEIndex = chaosBeginIndex;       //记录正确的插入位置,初始值就是被插入元素的原本位置(未排序序列的第一个元素位置)while (rightEIndex > 0)   //不断比较插入元素与有序队列元素,找到合适插入位置,用rightEIndex记录{if (insertENum< array[rightEIndex-1]){array[rightEIndex] = array[rightEIndex - 1];//妙啊,找合适位置的同时就顺便把元素后移了rightEIndex = rightEIndex - 1;}else{break;}}array[rightEIndex] = insertENum; //插入到空出来的合适位置中vectorPrint(array);}
}
输入数组:
6 9 3 1 2 0 8 29 15 11 10
插入排序第一次优化版
6 9 3 1 2 0 8 29 15 11 10
3 6 9 1 2 0 8 29 15 11 10
1 3 6 9 2 0 8 29 15 11 10
1 2 3 6 9 0 8 29 15 11 10
0 1 2 3 6 9 8 29 15 11 10
0 1 2 3 6 8 9 29 15 11 10
0 1 2 3 6 8 9 29 15 11 10
0 1 2 3 6 8 9 15 29 11 10
0 1 2 3 6 8 9 11 15 29 10
0 1 2 3 6 8 9 10 11 15 29
算法用时:(微秒)
[AlgoTime: 11002 us]

!!!插入排序二分搜索优化版(优化了哪里?如何优化?优化后的二分搜索判定条件?)

关键在于理解新的二分搜索,搜索合适的插入位置如何实现

流程图来自腾讯网课恋上数据结构与算法第二季,李明杰老师

//整体思想是利用二分搜索的框架,修改结束条件,从找到或找不到某元素,变为寻找数组中第一个大于插入元素的元素
//具体修改: 插入元素小于mid,更新搜寻范围向左,插入元素大于等于mid,更新搜寻范围向右,直到begin=end说明找到插入位置
int binarySearchInsertionIndex(vector<int> array, int chaosBeginIndex)
{int begin = 0;int end = chaosBeginIndex;//!我们知道二分搜索法end的定义可以有两种,但是如果优化插入排序算法的话,我们这里的end只能定义为搜索数组的末尾元素的后一位while (begin != end){ //写begin<end也可,begin=end时已经找到插入位置int mid = (begin + end) / 2;if (array[chaosBeginIndex] < array[mid]){end = mid;}else if (array[chaosBeginIndex] >= array[mid]){begin = mid + 1;}}return begin;//如果前面end定义是数组末尾元素索引,会导致begin和end错过时才能确定插入位置,而且我们不知道begin和end哪个是正确的插入位
}void insertionSort_BSEdition(vector<int> &array) //这里以数组左侧作为有序的那一边
{// chaosBeginIndex:未排序序列的起始元素下标for (int chaosBeginIndex = 1; chaosBeginIndex < array.size(); chaosBeginIndex++)//外循环控制从第二个元素开始插入到有序序列里,直到最后一个元素{int rightEIndex = binarySearchInsertionIndex(array, chaosBeginIndex); //二分搜索找到正确插入位置O(logn)int insertENum = array[chaosBeginIndex];                              //保留插入元素内容,因为后面要后移覆盖腾出插入的位置for (int i = chaosBeginIndex; i > rightEIndex; i--){array[i] = array[i - 1];}array[rightEIndex] = insertENum;vectorPrint(array);}
}
输入数组:
6 9 3 1 2 0 8 29 15 11 10
插入排序二分搜索优化版
6 9 3 1 2 0 8 29 15 11 10
3 6 9 1 2 0 8 29 15 11 10
1 3 6 9 2 0 8 29 15 11 10
1 2 3 6 9 0 8 29 15 11 10
0 1 2 3 6 9 8 29 15 11 10
0 1 2 3 6 8 9 29 15 11 10
0 1 2 3 6 8 9 29 15 11 10
0 1 2 3 6 8 9 15 29 11 10
0 1 2 3 6 8 9 11 15 29 10
0 1 2 3 6 8 9 10 11 15 29
算法用时:(微秒)
[AlgoTime: 11002 us]

完整代码

#include <iostream>
#include <vector>
#include "MeasureAlgoTime.hpp"
using namespace std;void vectorPrint(vector<int> &array)
{for (int i = 0; i < array.size(); i++){cout << array[i] << ' ';}cout << endl;
}//想象插入排序就是两只手,一只手里的牌是有序的,一只是无序的,每次把无序的手里的牌的第一张,与有序的比较,插入有序牌堆的合适位置
//插入排序的时间复杂度: 与数组中的逆序对有关 ,逆序对:比如想要递增的数组【0,8,9,1】这里【8,1】【9,1】都是逆序对
//最坏时间复杂度:O(n2)(输入数组完全逆序),最好时间复杂度O(n)(输入数组已经有序),平均时间复杂度O(n2),空间复杂度O(1),原地稳定排序算法
//排序算法在逆序对特别少的数组中效率很高,甚至可能比O(nlogn)级别的堆排序和快速排序还快void insertionSort1th(vector<int> &array) //这里以数组左侧作为有序的那一边
{// chaosBeginIndex:未排序序列的起始元素下标// insertEIndex:拿去插入有序序列的那个元素的下标,就是未排序序列的起始元素,但随着交换位置,下标会改变for (int chaosBeginIndex = 1; chaosBeginIndex < array.size(); chaosBeginIndex++)//外循环控制从第二个元素开始插入到有序序列里,直到最后一个元素{for (int insertEIndex = chaosBeginIndex; insertEIndex > 0; insertEIndex--)//内循环控制从未排序序列的首元素开始,逐渐与有序序列元素比较和交换位置,找到有序序列中的合适位置{if (array[insertEIndex] < array[insertEIndex - 1]){int temp = array[insertEIndex];array[insertEIndex] = array[insertEIndex - 1];array[insertEIndex - 1] = temp;}else{break;}}vectorPrint(array);}
}void insertionSort1thRe(vector<int> &array) //这里以数组右侧作为有序的那一边
{for (int chaosBeginIndex = array.size() - 1 - 1; chaosBeginIndex >= 0; chaosBeginIndex--){for (int insertEIndex = chaosBeginIndex; insertEIndex < array.size() - 1; insertEIndex++){if (array[insertEIndex] > array[insertEIndex + 1]){int temp = array[insertEIndex];array[insertEIndex] = array[insertEIndex + 1];array[insertEIndex + 1] = temp;}else{break;}}vectorPrint(array);}
}//插入排序的第一次优化,就是把不断的交换位置,改成了找到位置后插入数组,数组元素后移覆盖
//就是把左边比我大的元素都要跟我交换,换成了左边比我大的元素都要往右移
void insertionSort2nd(vector<int> &array) //这里以数组左侧作为有序的那一边
{for (int chaosBeginIndex = 1; chaosBeginIndex < array.size(); chaosBeginIndex++){int insertENum = array[chaosBeginIndex]; //记录插入元素的元素内容,因为后面原本位置会被覆盖int rightEIndex = chaosBeginIndex;       //记录正确的插入位置,初始值就是被插入元素的原本位置(未排序序列的第一个元素位置)while (rightEIndex > 0) //不断比较插入元素与有序队列元素,找到合适插入位置,用rightEIndex记录{if (insertENum < array[rightEIndex - 1]){array[rightEIndex] = array[rightEIndex - 1]; //妙啊,找合适位置的同时就顺便把元素后移了rightEIndex = rightEIndex - 1;}else{break;}}array[rightEIndex] = insertENum; //插入到空出来的合适位置中vectorPrint(array);}
}//整体思想是利用二分搜索的框架,修改结束条件,从找到或找不到某元素,变为寻找数组中第一个大于插入元素的元素
//具体修改: 插入元素小于mid,更新搜寻范围向左,插入元素大于等于mid,更新搜寻范围向右,直到begin=end说明找到插入位置
int binarySearchInsertionIndex(vector<int> array, int chaosBeginIndex)
{int begin = 0;int end = chaosBeginIndex;//!我们知道二分搜索法end的定义可以有两种,但是如果优化插入排序算法的话,我们这里的end只能定义为搜索数组的末尾元素的后一位while (begin != end){ //写begin<end也可,begin=end时已经找到插入位置int mid = (begin + end) / 2;if (array[chaosBeginIndex] < array[mid]){end = mid;}else if (array[chaosBeginIndex] >= array[mid]){begin = mid + 1;}}return begin;//如果前面end定义是数组末尾元素索引,会导致begin和end错过时才能确定插入位置,而且我们不知道begin和end哪个是正确的插入位
}void insertionSort_BSEdition(vector<int> &array) //这里以数组左侧作为有序的那一边
{// chaosBeginIndex:未排序序列的起始元素下标for (int chaosBeginIndex = 1; chaosBeginIndex < array.size(); chaosBeginIndex++)//外循环控制从第二个元素开始插入到有序序列里,直到最后一个元素{int rightEIndex = binarySearchInsertionIndex(array, chaosBeginIndex); //二分搜索找到正确插入位置O(logn)int insertENum = array[chaosBeginIndex];                              //保留插入元素内容,因为后面要后移覆盖腾出插入的位置for (int i = chaosBeginIndex; i > rightEIndex; i--){array[i] = array[i - 1];}array[rightEIndex] = insertENum;vectorPrint(array);}
}int main()
{Tools::Time::AlgoTimeUs time1;Tools::Time::AlgoTimeUs time2;Tools::Time::AlgoTimeUs time3;Tools::Time::AlgoTimeUs time5;vector<int> array;array = {6, 9, 3, 1, 2, 0, 8, 29, 15, 11, 10};vector<int> array2 = array;vector<int> array3 = array;vector<int> array5 = array;cout << "输入数组:" << endl;vectorPrint(array);time1.start();cout << "插入排序基础版" << endl;insertionSort1th(array);cout << "算法用时:(微秒)";time1.printElapsed();cout << "输入数组:" << endl;vectorPrint(array2);time2.start();cout << "插入排序基础版(换一个方向)" << endl;insertionSort1thRe(array2);cout << "算法用时:(微秒)";time2.printElapsed();cout << "输入数组:" << endl;vectorPrint(array3);time3.start();cout << "插入排序第一次优化版" << endl;insertionSort2nd(array3);cout << "算法用时:(微秒)";time3.printElapsed();cout << "输入数组:" << endl;vectorPrint(array5);time5.start();cout << "插入排序二分搜索优化版" << endl;insertionSort_BSEdition(array5);cout << "算法用时:(微秒)";time5.printElapsed();cout << ' ' << endl;return 0;
}

插入排序算法 及其二分搜索优化版 C++代码实现 恋上数据结构笔记相关推荐

  1. 二分搜索法 C++代码实现 恋上数据结构笔记

    复习梗概 二分搜索法的end有两种定义方式,两种分别是什么含义? 二分搜索法end的两种定义方式分别影响了什么?(结束条件,更新指针) 二分搜索法的结束条件和更新指针两步代码? 二分搜索法的整体流程? ...

  2. 归并排序算法 C++实现与时间复杂度(考过)恋上数据结构笔记

    复习梗概 画图,自己整个数组,看代码写步骤,这个对理解归并排序还是很有必要的 合并两个有序数组的merge函数写法 时间复杂度的分析方法!!! 其实我觉得去b站找个动态的步骤分解视频也是不错的复习方法 ...

  3. 快速排序 C++代码实现及其算法思想及时间复杂度分析及优化 恋上数据结构笔记

    文章目录 复习梗概 算法思想 算法复杂度分析及稳定性 如何优化? 快速排序改进版代码C++ 快速排序个人青春版代码 完整代码 复习梗概 算法思想,别的排序名字直接就能让人联想到它的算法思想,唯独快速排 ...

  4. 堆排序 C++代码实现及思想 排序过程输出 恋上数据结构笔记

    复习梗概 文章目录 复习梗概 什么是堆思想? 堆排序算法怎么来的? 什么是下滤?代码 什么是建堆?代码 堆排序本体 代码及排序过程输出 和时间复杂度 完整代码 什么是堆思想? 最大堆:树形结构,每一个 ...

  5. 基数排序及其思想 C++代码实现及分析 恋上数据结构笔记

    文章目录 复习梗概 算法思想 时间及空间复杂度 基数排序基础版代码 及输出结果 计数排序函数 基数排序函数 可视化输出 另一种思路 完整版代码 复习梗概 思想 如何取数字各个位位数 计数排序保证稳定性 ...

  6. 计数排序及其改进 C++代码实现与分析 恋上数据结构笔记

    文章目录 复习梗概 算法思想 基础思想 改进空间复杂度,改进不能对负数进行排序问题 改进稳定性 计数排序时间空间复杂度 计数排序基础版 代码及输出 计数排序第一次改进版 代码及输出 计数排序终极版 代 ...

  7. 希尔排序(缩小增量排序)(插入排序的优化版) C++代码实现及算法分析 恋上数据结构笔记

    文章目录 复习概要 算法思想 算法流程 算法复杂度分析及稳定性 希尔排序代码正常版 希尔排序与插入排序代码对比 希尔排序个人青春版(别看以免走上歧途) 复习概要 算法思想与流程 分这么多组分别插入排序 ...

  8. 选择排序 C++代码实现及性能分析 恋上数据结构笔记

    文章目录 复习梗概 算法思想及时间复杂度 选择排序的优化 代码及输出 完整代码 复习梗概 选择排序算法图解 选择排序在什么地方进行元素的调换 选择排序在什么地方优化,优化后的算法 时间复杂度分析 算法 ...

  9. 【恋上数据结构】串匹配算法(蛮力匹配、KMP【重点】、Boyer-Moore、Karp-Rabin、Sunday)

    串(Sequence) 串(前缀.后缀) 串匹配算法 蛮力(Brute Force) 蛮力1 – 执行过程 + 实现 蛮力1 – 优化 蛮力2 – 执行过程 + 实现 蛮力 – 性能分析 KMP 蛮力 ...

最新文章

  1. 不是之所以不是,所以不是
  2. linux sed命令替换目标字符串所在行整行为给定值
  3. angularjs中使用swiper时不起作用,最后出现空白位
  4. 论文必备神器,1行代码搞定Latex公式编写,这个4.6M的Python小插件
  5. 【LeetCode】剑指 Offer 50. 第一个只出现一次的字符
  6. 正则表达式 实现计算器
  7. 【GISER Painter】矢量切片(Vector tile)番外一:Proj4js
  8. 树莓派无法安装mariadb_RaspberryPi(树莓派)安装 MariaDB 数据库没有办法远程访问...
  9. JavaScript Promise对象
  10. Oracle 11g RAC 修改IP
  11. dreamweaver 正则表达式为属性值加上双引号_IT兄弟连 HTML5教程 HTML5表单 新增的表单属性3...
  12. mssql 2000 备份计划
  13. LED设备驱动二之重要代码和调试问题分析
  14. 荣誉勋章出现计算机中丢失,荣誉勋章战士无法运行的原因及解决方法
  15. ST-Link的红灯一直异常闪烁
  16. 生命是罐头,胆量是开罐器
  17. project顶行显示项目名称
  18. 毕业设计 树莓派指纹识别打卡系统设计与实现
  19. jquery如何设置占位隐藏_css3隐藏并且不占位怎么设置?
  20. 如何做一个快速粉的小红薯ai绘画号?绘画工具篇

热门文章

  1. ili9341屏幕在断电一段时间后首次上电白屏问题
  2. 机器学习 聚类篇——python实现DBSCAN(基于密度的聚类方法)
  3. c语言八大数据基本类型,C语言中基本的数据类型有哪些
  4. C++ Primer 5th笔记(chap 17 标准库特殊设施)regex_replace
  5. 智能合约重构社会契约 (5)比特犬模型实现智能合约
  6. 知识图谱 (1)基本概念
  7. 区块链BaaS云服务(8)京东 智臻链
  8. redis的安装和运行
  9. python—sql语句参数化
  10. 中断/gic代码导读:在哪里配置安全中断的?