直接插入排序是学习数据结构里经典排序算法之一。
操作与思想
该排序的操作步骤类似平时我们打扑克牌的时候给自己的手牌进行排序的行为。
这个操作思想主要可以概括为:一次取出序列里每一个元素,将其插入一个已经有序的序列之中,一直到整个序列都有序为止。
举个例子:
刚发牌的时候,你的手牌是:【4 3 5 6 A J 4】这六张牌,如果你从左到右将手牌进行升序排序,操作是不是如下:
- 第一步:4和3交换,【3 4 5 6 A J 4】
- 第二步:3 4 5 6已经是一个有序序列了,往下遍历是A(把A视为1最小值看吧),就是把A直接放到3的前面,即【A 3 4 5 6 J 4】
- 第三步:A 3 4 5 6 J 也是有序序列了,但是4还等往前面移动,所以要把4这张牌抽出来直接插入4和5之间,此时手牌顺序为【A 3 4 4 5 6 J】,排序完毕。
通过这个例子,这个排序算法主要是解决这一个问题:
如何确定元素的插入位置以构造出有序序列。
我们从上述例子第二步分析:我们怎么通过A来知道3 4 5 6是有序的呢?挨个往前面对比就可以了。操作如下(*号表示空位):
A和6对比,将6右移,得【3 4 5 * 6 J 4】;
A和5对比,将5右移,得【3 4 * 5 6 J 4】;
以此类推,一直到A前方没有元素或者A的前方元素不大于A就可以结束本轮遍历了(后面4元素的插入就是后者情况)
……
A和3对比,将3右移,得【 * 3 4 5 6 J 4】。此时A前方没有元素了,将A插入 * 的位置,就得出【A 3 4 5 6 J 4】了。
对于4的操作思想也是一致的:
第一步:J右移;【A 3 4 5 6 * J】
第二步:6右移;【A 3 4 5 * 6 J】
第三步:5右移;【A 3 4 * 5 6 J】
第四部:4和4对比,4前方的元素并不大于4,结束本轮遍历,将4插入 * 的位置,就得出【A 3 4 4 5 6 J】,排序完毕。
关于稳定性
因为原序列中两个数的值是相同时,它们在排完序后,位置不发生变化,所以直接插入排序是稳定的。
关于时间复杂度:
最差的情况:序列的顺序和目标顺序是逆序(比如你要得到逆序的序列但是初始的序列是升序的)
第一次插入元素要移动1次,第二次插入元素要移动2次,以此类推第n次插入元素就是要移动n-1次,时间复杂度是此时O(n^2)。
最理想的情况:
序列的顺序和目标顺序一致
此时只需要将全部元素遍历一遍即可,时间复杂度为O(n)。
关于空间复杂度:
只使用一个中间变量操作元素移动,空间复杂度为O(1)。
核心算法函数
void InsertSort(int a[]){
int t;//被选中的元素
int ti;//被选中的元素下标(视为该元素被抽出,该位置也是空位的初始坐标)
for(int i=1;i<10;i++){//大循环,遍历每一个元素
while(ti>0){//向左对比
cout<<"t:"<<t<<" ti:"<<ti<<endl;
if(t<a[ti-1]){//升序
a[ti] = a[ti-1];//将大元素往右移
ti--; //空位下标左移
}else{//左侧元素小于等于被选中的元素,该元素的遍历结束
break;
}
}
a[ti] = t;//将元素插入空位
}
}
排序过程
完整代码
/*
广西师范大学 计算机科学与工程学院
GuangXi Normal University
College of Computer Science and Engineering
Student STZ
*/
#include<iostream>
#include<stdio.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
void initial(int a[]){
int max=10;
srand((unsigned)time(NULL));//时间种子
for(int i=0;i<10;i++){//随机生成10个数
a[i] = rand()%max;
}
}
void showArray(int a[]){
cout<<"Array:";
for(int i=0;i<10;i++){
cout<<a[i]<<" ";
}
cout<<endl;
cout<<"Index:";
for(int i=0;i<10;i++){
cout<<i<<" ";
}
cout<<endl<<endl;
}
void InsertSort(int a[]){
int t;//被选中的元素
int ti;//被选中的元素下标(视为该元素被抽出,该位置也是空位的初始坐标)
for(int i=1;i<10;i++){//大循环,遍历每一个元素
t = a[i];ti=i;//将第i个元素取出
while(ti>0){//向左对比
cout<<"抽出的元素:"<<t<<" 空位下标:"<<ti<<endl;
if(t<a[ti-1]){//升序
a[ti] = a[ti-1];//将大元素往右移
ti--;
}else{
break;
}
}
a[ti] = t;//将元素插入
cout<<"本轮排序结果:\n";
showArray(a);
}
}
int main(){
int a[10];
initial(a);
cout<<"初始状态:\n";
showArray(a);
InsertSort(a);
return 0;
}
敬请批评指正。