数据结构-排序算法之插入排序(直接插入,二分插入,希尔,表插入)
排序算法: 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减排列起来的操作。
插入排序:排序过程的每一步将一个待排序的记录按其排序码值的大小插到前面已排好序的部分中的适当位置,知道全部记录插完为止。
- 直接插入排序:使用顺序查找寻找下一个待排元素的位置。
- 二分插入排序:使用二分查找寻找下一个待排元素的位置。
- 希尔排序:又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。该方法实质上是一种分组插入方法。
- 表插入排序:不移动表中的任何记录,使用指针表示排序结果
直接插入排序算法
算法思想:
把n个待排序的元素看成为一个有序表和一个无序表。开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。
时间复杂度:假设被排序的数列中有N个数。遍历一趟的时间复杂度是O(N),直接插入排序需要N-1次遍历,故算法的时间复杂度为O(N2)。
算法的稳定性:
- 算法稳定性的定义:假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!
- 直接插入排序满足此定义,故直接插入排序是一种稳定的排序算法。
算法实现 算法的c++实现,实现代码来自王治和教授数据结构课堂的总结
void straight_insertion_sort(int entry[],int count){
int first_unsorted;
int position;
int current;
for(first_unsorted=1;first_unsorted<count;first_unsorted++){
if(entry[first_unsorted]<entry[first_unsorted-1]){//按照升序排列
position=first_unsorted;
current=entry[first_unsorted];//未排序之处的元素暂存
do{
entry[position]=entry[position-1];
position--;
}while(position>0&&entry[position-1]>current);
entry[position]=current;//插入暂存的元素
}
}
}
二分插入排序算法
算法思想:
把n个待排序的元素看成为一个有序表和一个无序表。开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。插入位置的寻找过程是通过二分查找实现的。
时间复杂度:二分插入排序是稳定的与二分查找的复杂度相同;最好的情况是当插入的位置刚好是二分位置 所用时间为O(log₂n);最坏的情况是当插入的位置不在二分位置 所需比较次数为O(n),无限逼近线性查找的复杂度。平均时间复杂度为O(n^2)
算法的稳定性:二分插入排序算法是一种稳定的排序算法。
算法的C++实现
void dichotomize_insertion_sort(int entry[],int count){
int current;
for(int i=1;i<count;i++){
if(entry[i]<entry[i-1]){
int l=0,r=i-1;
while(1<=r){
int mid=(1+r)/2;
if(entry[i]<entry[mid])
r=mid-1;
else
l=mid+1;
}
current=entry[i];
for(int j=i-1;j>=l;j--)
entry[j+1]=entry[j];
entry[l]=current;
}
}
}
希尔插入排序算法
算法思想:
先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
时间复杂度:希尔排序最好时间复杂度和平均时间复杂度都是O(NLogN),最坏时间复杂度为O(N2)。
算法的稳定性:由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
算法的C++实现
void sort_interval(int entry[],int count,int start, int increment)
{
int current;
int j;
for(int i=start+ increment; i<count; i+= increment ){
if (entry[i]<entry[i- increment]){
current=entry[i];
j=i- increment;
}
while((current <entry[j])&&j>=start){
entry[j+ increment]=entry[j];
j-= increment;
}
entry[j+ increment]=current;
}
}
void shell_insertion_sort(int entry[],int count){
int increment=count/2;
while (increment>=1){
for(int start=0; start<increment; start++)
sort_interval(entry,count,start, increment);
increment/= 2;
}
}
表插入排序算法
算法思想:
- 不移动表中的任何记录,使用指针表示排序结果。
算法的C++实现 算法的c++实现,实现代码来自苏叔叔csdn的博客
const int MAX = 1000; //这里的最大值,应设置好
typedef struct rec //Record 静态链表的节点类型
{
int data;
int next; //静态链表的链域可以这样写,大家应该见过很多次
}Rec;
void InsertSort(int a[], int n) //表插入
{
Rec *rec = new Rec[n+1];
for (int i = 0; i < n; i++) //把数据赋给链表
{
rec[i + 1].data = a[i];
rec[i + 1].next = 0;
}
rec[0].data = MAX;
rec[0].next = 1; //头节点和第一个节点构成了循环链表
int p, q;
for (int i = 2; i < n + 1; i++) //修改next域,使其按指针指向有序
{
q = 0;
p = rec[q].next; //p指向当前待比较的节点,q指向前一个
while (rec[i].data >= rec[p].data) // >= 保证了排序的稳定性
{
q = p;
p = rec[q].next;
}
rec[i].next = p;
rec[q].next = i;
}
p = rec[0].next;
int i=0;
while(p!=0) //顺着静态链表的指针指向,回写数据到原数组
{
a[i++]=rec[p].data;
p=rec[p].next;
}
delete[]rec; //释放空间
}