几种重要的排序算法——插入排序
插入排序
1.插入排序
插入排序分为直接插入排序、折半插入排序、希尔排序(shell sort),后两种是在直接插入排序的改进上而来。
1.直接插入排序
排序思路:假设待排序的元素存放在数组A[1..n]A[1..n]A[1..n]中,在排序过程的某一时刻,A被划分为两个子区间A[1..mid]A[1..mid]A[1..mid]和A[mid+1..n]A[mid+1..n]A[mid+1..n],其中前一个子区间为排好序的有序区,而后一个子区间为未排序的子区间,暂称作无序区。该排序算法顾名思义,取出无序区的第一个元素A[mid+1]A[mid+1]A[mid+1]插入到有序区A[1..mid]A[1..mid]A[1..mid]中适当的位置,A[1..mid+1]A[1..mid+1]A[1..mid+1]变成新的有序区(假设按照升序排序)。动画演示如下:
对于第i趟排序,要将无序区的第一个元素A[mid+1]A[mid+1]A[mid+1](设A[mid+1]的值为aA[mid+1]的值为aA[mid+1]的值为a)插入到有序区的适当位置,此时便将aaa不断的与有序区的元素进行比较(即为动画中红色条形与绿色条形的部分),当找到第一个比aaa小的元素时(假设是A[m](1≤m<mid+1)A[m](1\leq m<mid+1)A[m](1≤m<mid+1)),在该元素的后面将aaa插入,当a是有序区中最小的元素时,将a放在有序区的第一个位置,此时数组中下标m+1m+1m+1到midmidmid的所有元素均要往后移动一格,就像是腾出一个位置来让给aaa,然后把aaa放到数组中mmm的位置。
c/c++代码描述:
void InsertSort(int A[], int n)
{int i, j;for (int i = 2; i <= n; i++) // 依次将A[2]~A[n]插入到前面已经有序区{if (A[i] < A[i - 1]) // A[i]小于其前面的元素,将A[i]放入有序区{A[0] = A[i]; // A[0]不存放元素,专用于来存放当前需要插入的值for (j = i - 1; A[j] > A[0]; j--) // 从后往前寻找插入位置,直至找到第一个比A[i]小的元素时停止A[j + 1] = A[j]; // 向后挪一个位置,腾出地方让给A[i]}A[j + 1] = A[0]; // A[i]归位,放到有序区合适的位置}
}
python代码描述:
def insert_sort(a):length = len(a)for i in range(1,length):waitting_insert_ele = a[i]j = i - 1while j > -1 and a[j] > waitting_insert_ele: # 与前面有序区进行比较,如果比前面有序区第一个大,则停止,将该元素插入此位置a[j + 1] = a[j] # 后移操作,给待插入元素“腾位置”j -= 1a[j+1] = waitting_insert_ele # 当不比前面的数值小时,便在当前位置“赖着不走”print(a)
a = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]
insert_sort(a)
运行结果如下:
算法分析:
空间复杂度:因为直接插入排序并没有开第二个数组,它仅仅只是就着他自己的原来的数组进行排序,利用的存储空间是常数个空间,因而其空间复杂度为O(1)O(1)O(1),即原地工作。
时间复杂度:在排序过程中,向有序区逐个的插入元素进行了n−1n-1n−1趟,每趟都分为两步:
- 和前一元素进行比较
- 将前一元素进行往后挪
其中比较的次数与待排序数组的初始状态有关,在最优情况下,表中元素已有序,此时每遍历一次,都不用移动元素,且只需比较一次,时间复杂度为O(n)O(n)O(n)。在最坏情况下,数组中元素与要排序顺序相反(在本博客中以初始时降序排列为反序),总的比较次数达到最大均为O(n2)O(n^2)O(n2)。平均情况下总的比较次数和移动次数约为n24\frac{n^2} {4}4n2,所以时间复杂度为O(n2)O(n^2)O(n2)。
稳定性:每次插入元素时,总是从后向前比较再移动,所以相同元素的相对位置不会发生变化,直接插入排序是一个稳定的排序算法。
适用性:直接插入排序适用于顺序存储和链式存储的线性表。
2.折半插入排序
排序思路: 思路同直接插入排序一致,但在进行元素比较时采用的是折半查找,在查找待插入位置时时间复杂度为O(nlog2n)O(nlog_2n)O(nlog2n)(折半查找的时间复杂度)。
c/c++代码描述:
void InsertSort(int A[], int n)
{int i, j, low, high, mid;for (int i = 2; i <= n; i++) // 依次将A[2]~A[n]插入到前面已经有序区{A[0] = A[i]; // A[0]不存放元素,专用于来存放当前需要插入的值low = 1;high = i - 1;while (low < high){mid = (low + high) / 2; //取中间节点if (A[mid] > A[0])high = mid - 1; // 插入位置应在左半子表,即有序区的前半部分elselow = mid + 1; // 插入位置应在右半子表,即有序区的后半部分}for (j = i - 1; j > high + 1; j--) // 从后往前寻找插入位置,直至找到第一个比A[i]小的元素时停止A[j + 1] = A[j]; // 向后挪一个位置,腾出地方让给A[i]A[high + 1] = A[0]; // A[i]归位,放到合适的地方}
}
python代码描述:
def insert_sort(a):length = len(a)print(a)for i in range(1, length):waitting_insert_key = a[i]low, high = 0, i -1while low <= high: # 折半查找mid = (low + high) // 2 # “//”表示向下取整,相当于c语言里边的单斜杆if a[mid] > waitting_insert_key:high = mid - 1else:low = mid + 1# 上面的循环体返回第一个比 waitting_insert_key 大的元素的位置,# 当有序区中不存在比它小的元素时(即它自己和有序区中的元素相比是最小的)# 返回 -1j = i - 1while j >= high + 1: # 元素后移,给waitting_sort_key “腾位置”a[j + 1] = a[j]j -= 1a[high + 1] = waitting_insert_keyprint(a)
a = [0,1,6,3,2,5,4]
b = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]
insert_sort(a)
insert_sort(b)
运行结果如下:
算法分析: 折半插入排序仅仅只是减少了比较的时间而已,而对于腾出空间的挪动操作并未有改变,挪动次数取决于元素在存储结构中的初始位置,若初始表中数据整体有序的较多,则移动次数较少,因此折半插入排序的时间复杂度仍然是O(n2)O(n^2)O(n2)。折半插入排序是一种稳定的排序算法。
3.希尔排序(shell sort)
排序思路: 先将待排序的元素分组,从初始表中每相隔距离d取一个元素,形如L[i,i+d,i+2d,i+3d,...,i+kd]L[i,i+d,i+2d,i+3d,...,i+kd]L[i,i+d,i+2d,i+3d,...,i+kd],首次可将n个元素划分成ddd个组(即从第1个元素到第d个元素直接的所有元素是每组的第一个元素,“头儿”),每次分别对这些组进行直接插入排序,当整个表中的元素已基本有序时,再对全体记录进行一次直接插入排序。希尔排序每趟并不产生有序区,在最后一趟排序结束前,所有元素并不一定归位了,但是在希尔排序每趟完成后数据越来越接近有序。动画效果如下(演示动画仅进行了一次分组,第二趟排序便直接对整个序列进行排序了):
动画中演示的是先将分组中值较大的调整至后面,再将值较小的调整至前。
c/c++代码描述:
void shellSort(int A[], int n)
{int d, i, j;for (d = n / 2; d >= 1; d / 2) //每次分组的间隔长度,步长变化for (i = d + 1; i <= n; i++)if (A[i] < A[i - d]) //需将A[i]插入有序区{A[0] = A[i];for (j = i - d; j > 0 && A[j] > A[0]; j -= d)A[j + d] = A[j]; // 给小组中后面值较小的元素挪位置,让较小的元素尽量往前边靠A[j + d] = A[0]; // 最终归位}
}
python代码描述:
def insert_sort(a):length = len(a)d = length >> 1 # 除半操作,即划分分组,刚开始以两个元素为一组,共d组# 此处采用的是位运算,和前面的折半操作稍有不同# 位运算的速度要稍快于普通的乘除while d >= 1:for i in range(d, length, 1): # 从每组的第二个元素开始遍历if a[i] < a[i - d]: # 如果当前元素值比前边同组的“有序区”部分的元素小,则把它“调”到有序区waitting_insert_key = a[i]j = i - dwhile j > -1 and a[j] > waitting_insert_key: # “腾位置”操作a[j + d] = a[j]j -= da[j + d] = waitting_insert_key # “果宝机甲”归位d = d >> 1 # 分组数减少一半,分区增大一倍,即原来第一次排序# 前一个分区至多2个元素,合并后至第二轮便有4个……print(a)b = [0,1,6,3,2,5,4]
a = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]insert_sort(a)
insert_sort(b)
运行结果如下:
算法分析:
空间复杂度:因为希尔排序并没有使用第二个数组,它仅仅只是就着他自己的原来的数组进行排序,使用存储空间为常数个空间,因而其空间复杂度为O(1)O(1)O(1)。
时间复杂度:由于希尔排序的时间复杂度依赖于增量序列的函数,这涉及数学上未解决的难题,所以其时间复杂度分析比较困难。当n在某个特定范围时,希尔排序的时间复杂度约为O(n1.3)O(n^{1.3})O(n1.3)。在最坏情况下希尔排序的时间复杂度为O(n2)O(n^2)O(n2)。
稳定性:希尔排序是一种不稳定的排序算法。
适用性:希尔排序仅适用于线性表为顺序存储的情况。
后记: 文章中的python代码的运行示例的pycharm风格不同是因为文章是断断续续写的,前两张的运行截图是借同学的电脑运行的截图,后一张是自己吃完晚饭在自己电脑上截的图。平常要忙着考研复习,只有中午、晚上吃完饭回寝室才有时间写一下,文章连着写了好几天才写完。后面也会挤时间陆续把其他的几个基础算法补齐的。欢迎各位大佬指正文中不足的部分,弟弟以后定加改正!
几种重要的排序算法——插入排序相关推荐
- 七种常见的排序算法总结
目录 引言 1.什么是排序? 2.排序算法的目的是什么? 3.常见的排序算法有哪些? 一,插入排序 1.基本思想 2.代码实现 3.性能分析 4.测试 二,希尔排序(缩小增量排序) 1.基本思想 2. ...
- access两字段同时升序排序_7 天时间,我整理并实现了这 9 种常见的排序算法
排序算法 回顾 我们前面已经介绍了 3 种最常见的排序算法: java 实现冒泡排序讲解 QuickSort 快速排序到底快在哪里? SelectionSort 选择排序算法详解(java 实现) 然 ...
- 视觉直观感受 7 种常用的排序算法
2019独角兽企业重金招聘Python工程师标准>>> 1. 快速排序 介绍: 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较 ...
- 7 种常用的排序算法直观感受
1. 快速排序 介绍: 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见.事实上,快速排序 ...
- 视觉直观感受7种常用的排序算法
1 快速排序 介绍: 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见.事实上,快速排序通 ...
- 几种常用的排序算法之JavaScript实现
文章目录 插入排序 二分插入排序 选择排序 选择排序 冒泡排序 快速排序 堆排序 归并排序 桶排序 计数排序 插入排序 <html> <script> /* 1)算法简介插入排 ...
- 通过视觉直观感受7种常用的排序算法
1 快速排序 介绍: 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见.事实上,快速排序通 ...
- 算法学习--排序算法--插入排序
算法学习--排序算法--插入排序 插入排序算法 代码实现 插入排序算法 插入排序(Insertion sort)是一种简单直观且稳定的排序算法.如果有一个已经有序的数据序列,要求在这个已经排好的数据序 ...
- Java排序算法——插入排序(Insertion Sort)
之前总结了交换排序的冒泡排序与选择排序的简单选择排序,这次我们来看看插入排序的简单插入排序~ 往期传送门: 冒泡排序: Java排序算法--冒泡排序(Bubble Sort)https://blog. ...
最新文章
- vi/vim: 使用taglist插件
- 多伦多大学和清华大学创新创业论坛,数据科学研究院建言献策
- c语言四则运算实验报告,c语言四则运算实验报告.doc
- Segment Routing — SRv6 — SRv6 协议解析
- Android 设计模式 - 观察者模式
- 一文带你看懂PaddleHub
- 索尔维会议记录软件测试,索尔维会议
- 关于NSKeyedArchiver的编码格式
- 使用Google Guava Cache进行本地缓存
- JMeter4.0以上 分布式测试报错 server failed start Listen failed on port
- 【python基础】ValueError: only 2 non-keyword arguments accepte
- i2c传输距离_使用 ToF 传感器进行距离测量和手势识别的基本原理
- Java经典编程题50道之三十一
- 谁说前端程序员不了解业务?站出来!
- LVS (Linux虚拟服务器)模型及算法
- 抱米花-豆丁文档下载器 20100529
- 机器学习——联合概率分布及其意义
- 辩证的看待IDE工具(Java与Python学习通法)
- WebSphere Application Server V7、V8 和 V8.5 中的高级安全性加强,第 1 部分: 安全性加强的概述和方法...
- 面试官:说一下什么是熔断?什么是服务降级?
热门文章
- python challenge
- (libgdx学习)ScrollPane(使用ScrollPane来创建一个横向滚动条)
- 【Android】WebView安全漏洞问题
- python可以设计网页吗_python能做网页吗?
- mapreduce 编程
- 跨站脚本攻击(XSS)分类介绍及解决办法
- 通达信接口官网-TcApi的工作机制
- file_zilla 通过key连接远程服务器
- php怎么获取图片拍摄时间,【技巧】如何查看图片的拍摄地点和日期时间等信息...
- 校园卡私人区块链平台毕设