插入排序解释

插入排序很好理解,其步骤是 :先将第一个数据元素看作是一个有序序列,后面的 n-1 个数据元素看作是未排序序列。对后面未排序序列中的第一个数据元素在这个有序序列中进行从后往前扫描,找到合适的插入位置并插入到其中,每次有序序列的长度 +1

重复这样的操作,将每个未排序序列中的元素插入到当前有序序列中合适的位置。直到未排序序列长度为 0,最后得到一个完整的有序序列,即为排序的结果。

(若当前插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)


插入排序动态演示

我们以序列 [7, 6, 4, 5, 8, 2, 3] 为例进行动态演示

第一次插入

第二次插入

第三次插入

第四次插入

第五次插入

第六次插入

直接插入排序 时间复杂度

最优时间复杂度

假设每一层循环,当前未排序序列中的第一个元素(待插入元素)应当插入的位置都处于当前有序序列的末尾,只需要执行一次判断就可以了。

最优时间复杂度 为: O(n)

最坏时间复杂度

假设每一层循环,当前待插入元素应当插入的位置都在当前有序序列的首位,(设当前有序序列长度为 i )那么我们每一次线性查找比较的次数为 i , 并且每次将后面元素进行后移的次数也为 i

最坏时间复杂度 为:


直接插入排序 核心代码

//插入排序
void InsertSort(vector<int> &v){
int n = v.size();
for(int i = 1; i < n; i++){
int key = v[i]; //当前需要插入的数
int j = i - 1; //j为已排序序列的末下标
while(j >= 0 && v[j] > key){
v[j + 1] = v[j]; //后移
j--;
}
v[j + 1] = key; //插入到已排序序列中的合适位置
}
}

优化方法 折半插入排序

线性查找每一次待插入元素在当前有序序列中合适的插入位置往往是比较低效的,因此我们自然会想到用 二分查找 来查找待插入元素合适的插入位置。

其实改进方法很简单,运用二分查找,定义一个 pos 来记录当前待插入元素 key 在有序序列中合适的位置,然后对 pos 之后的元素进行后移操作,在 pos 位置插入当前待插入元素就可以啦~

注意哦,每次二分查找需要找到的是在前i个数组成的有序序列中第一个大于 key 的位置 pos (若没有比key大的,插入位置即为下标为i的位置)


折半插入排序 时间复杂度

对于折半插入排序,由于始终要进行元素的后移操作,所以在 最坏情况下 利用二分查找确定插入位置确实提高了效率,但是不可避免的是依旧需要消耗 O(n^2) 的时间进行元素后移。由此可得

最优时间复杂度 依然是 O(n)

最坏时间复杂度


折半插入排序 核心代码

//折半插入排序
void BinaryInsertSort(vector<int> &v){
int n = v.size();
for(int i = 1; i < n; i++){
int key = v[i]; /* 折半查找部分 */
int low = 0, high = i - 1, pos = i;
//二分查找找到在前i个数组成的有序序列中第一个大于key的位置pos
//若没有比key大的,插入位置即为下标为i的位置
while(low <= high){
int mid = (low + high)>>1;
if(v[mid] > key){
pos = mid;
high = mid - 1;
}else{
low = mid + 1;
}
} //插入位置后后移
int j = i - 1;
while(j >= pos){
v[j + 1] = v[j];
j--;
}
v[pos] = key; //插入到合适的pos位置
}
}

完整程序源代码

#include<iostream>
#include<vector>
#include<time.h>
using namespace std; //插入排序
void InsertSort(vector<int> &v){
int n = v.size();
for(int i = 1; i < n; i++){
int key = v[i]; //当前需要插入的数
int j = i - 1; //j为已排序序列的末下标
while(j >= 0 && v[j] > key){
v[j + 1] = v[j]; //后移
j--;
}
v[j + 1] = key; //插入到已排序序列中的合适位置
}
} //折半插入排序
void BinaryInsertSort(vector<int> &v){
int n = v.size();
for(int i = 1; i < n; i++){
int key = v[i]; /* 折半查找部分 */
int low = 0, high = i - 1, pos = i;
//二分查找找到在前i个数组成的有序序列中第一个大于key的位置pos
//若没有比key大的,插入位置即为下标为i的位置
while(low <= high){
int mid = (low + high)>>1;
if(v[mid] > key){
pos = mid;
high = mid - 1;
}else{
low = mid + 1;
}
} //插入位置后后移
int j = i - 1;
while(j >= pos){
v[j + 1] = v[j];
j--;
}
v[pos] = key; //插入到合适的pos位置
}
} void show(vector<int> &v){
for(auto &x : v)
cout<<x<<" ";
cout<<endl;
} main(){
vector<int> v;
srand((int)time(0));
int n = 50;
while(n--)
v.push_back(rand() % 100 + 1);
show(v); //InsertSort(v);
BinaryInsertSort(v); cout<<endl<<endl;
show(v);
}

程序运行结果图

[排序算法] 直接/折半插入排序 (C++)的更多相关文章

  1. Java常见排序算法之折半插入排序

    在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let ...

  2. 排序算法之折半插入排序的思想以及Java实现

    1 基本思想 折半插入排序(binary insertion sort)的基本原理与直接插入排序相同,不同之处在于,确定当前记录在前面有序子数组中的位置时,直接插入排序是采用顺序查找的方法,而折半插入 ...

  3. 我的Java开发学习之旅------>Java经典排序算法之二分插入排序

    一.折半插入排序(二分插入排序) 将直接插入排序中寻找A[i]的插入位置的方法改为采用折半比较,即可得到折半插入排序算法.在处理A[i]时,A[0]--A[i-1]已经按关键码值排好序.所谓折半比较, ...

  4. Java常见排序算法之直接插入排序

    在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let ...

  5. 排序算法之直接插入排序Java实现

    排序算法之直接插入排序 舞蹈演示排序: 冒泡排序: http://t.cn/hrf58M 希尔排序:http://t.cn/hrosvb  选择排序:http://t.cn/hros6e  插入排序: ...

  6. 排序系列 之 折半插入排序算法 —— Java实现

    基本思想: 折半插入算法是对直接插入排序算法的改进,排序原理同直接插入算法: 把n个待排序的元素看成一个有序表和一个无序表,开始时有序表中只有一个元素,无序表中有n-1个元素:排序过程即每次从无序表中 ...

  7. 七内部排序算法汇总(插入排序、Shell排序、冒泡排序、请选择类别、、高速分拣合并排序、堆排序)

    写在前面: 排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素的随意序列,又一次排列成一个按keyword有序的序列.因此排序掌握各种排序算法很重要. 对以下介绍的各个排序,我们假定全部排 ...

  8. 结构-行为-样式-Js排序算法之 直接插入排序

    最新因工作原因需要接触到算法,之前学习C++的时候有接触过算法,Javascript中实现算法其实也是大同小异.下面我讲下第一个实现的排序算法--直接插入排序.基本实现思路:假定一个数组中前n(n&g ...

  9. 基本的排序算法C++实现(插入排序,选择排序,冒泡排序,归并排序,快速排序,最大堆排序,希尔排序)

    博主欢迎转载,但请给出本文链接,我尊重你,你尊重我,谢谢~http://www.cnblogs.com/chenxiwenruo/p/8529525.html特别不喜欢那些随便转载别人的原创文章又不给 ...

  10. 八大排序算法之直接插入排序(InsertionSort)

    常见的排序算法 今天复习[直接插入排序] 核心思想:有序数组中 找位置 -- 给无序数组第一个 找位置 ` public class InsertionSort { // 核心思想:有序数组中 找位置 ...

随机推荐

  1. flutter系列之:UI layout简介

    目录 简介 flutter中layout的分类 常用layout举例 总结 简介 对于一个前端框架来说,除了各个组件之外,最重要的就是将这些组件进行连接的布局了.布局的英文名叫做layout,就是用来 ...

  2. PostgreSQL 时间函数分类与特性

    KingbaseES 时间函数有两大类:返回事务开始时间和返回语句执行时的时间.具体函数看以下例子: 1.返回事务开始时的时间 以下函数返回事务开始的时间(通过 begin .. end 两次调用结果 ...

  3. Spring_事务总结

    Spring 事务总结 rollbackFor 设为 Exception.class场景下 如果在函数内部catch住异常消费掉,没有再抛出的话,不会回滚 如果catch住 然后原封不动抛出,会回滚 ...

  4. Centos7 安装部署Kubernetes(k8s)集群

    目录 一.系统环境 二.前言 三.Kubernetes 3.1 概述 3.2 Kubernetes 组件 3.2.1 控制平面组件 3.2.2 Node组件 四.安装部署Kubernetes集群 4. ...

  5. Spring Boot2配置Swagger2生成API接口文档

    一.Swagger2介绍 前后端分离开发模式中,api文档是最好的沟通方式. Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务. 及时性 (接 ...

  6. 读时加写锁,写时加读锁,Eureka可真的会玩

    大家好,我是三友~~ 在对于读写锁的认识当中,我们都认为读时加读锁,写时加写锁来保证读写和写写互斥,从而达到读写安全的目的.但是就在我翻Eureka源码的时候,发现Eureka在使用读写锁时竟然是在读 ...

  7. [Linux]-screen命令-切换终端

    在训练模型时,经常遇到需要采用多个策略同时跑的情况,直接运行的话比较费时,只要CPU和GPU支持,可以通过Linux的screen命令多终端并行,大大提升效率. 创建: screen -S name ...

  8. flannel跨主网络通信方案(UDP、VXLAN、HOST-GW)详解

  9. linux搭建内网邮件服务器

    一.配置发件服务器 1.1 根据现场IP,配置主机名 vi /etc/hosts 192.168.40.133 mail.test.com 将主机名更改为邮件服务器域名mail.test.com 1. ...

  10. echarts pie饼状图绑定点击事件

    var valueData = [ {value: 33,name: '诊所'}, {value: 29,name: '汽车服务相关'}, {value: 27, name: '洗衣店'}, {val ...