插入排序算法

1、直接插入排序

******、核心思想

插入排序通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入 ,如此重复,直至完成序列排序。

******、算法步骤

1、从序列第一个元素开始,该元素可以认为已经被排序
2、取出下一个元素,设为待插入元素,在已经排序的元素序列中从后向前扫描,如果该元素(已排   序)大于待插入元素,将该元素移到下一位置。   
3、重复步骤2,直到找到已排序的元素小于或者等于待排序元素的位置,此时待排序元素大于当前元素不满足条件,退出循序,插入元素
4、重复2,3步骤,完成排序。

******、算法实现

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#define endl '\n'
using namespace std; 

int * InsertSort(int *p,int n){
	int i,j;
	for(i = 2;i < n; i++){                                 //从下标2开始,位置0为空,用于存放当前比较的值,位置1为一个数,没有可比性所以从2开始 
		if(p[i] < p[i-1]){
		    p[0] = p[i];                                   //位置0做比较位置 
		    for(j = i - 1;p[0] < p[j] ; j-- )              //记录后移 
		        p[j+1] = p[j];
		    p[j+1] = p[0];                                 //插入到空缺出来的位置 
		}
	}
	return p; 
} 
main(){
	cout<<"\n请随便输入要执行插入排序的树:";
	int elem; 
	cin>>elem; 
	int i = 1;
	int sort[20];
	while(elem != -1){
		sort[i] = elem;
		i++;
		cin>>elem; 
	}
	int *address = InsertSort(sort,i);
	int n = 1;
	cout<<"\n直接插入排序结果如下:";
	while(n < i){
		cout<<address[n]<<" "; 
		n ++; 
	}
	
}



在这里插入图片描述

******、算法分析

上述算法是将线性表中的第一个位置,也就是下标为0的地方设置成为了哨兵,用于存放待排序元素,这样就不用检查是否 j >= 0了,比较方便!当p【0】< p【0】不满足条件的时候就出退出移动循环语句!

时间复杂度:
1、顺序递增排列时,只需比较(n-1)次,插入排序时间复杂度为O(n);
2、逆序排序时,总的关键字比较次数大约是  n*n/2 次,以及记录移动的次数大约是n*n/2 次;
   若待排序序列中出现各种可能排列的概率相同,则可以直接取上述最好情况和最坏情况的平均情况!
   所以在平均情况下,直接插入排序关键字的比较次数和记录移动的次数均约是  n*n /4;
   所以该算法的时间复杂度是 O(n*n)
   所以该算法的最坏时间复杂度和平均时间复杂度也都是 o(n*n)
   但是该算法的最好时间复杂度是 o (n),元素递增排列的情况!
空间复杂度:
   需要p[0]来保存待排序元素,所以空间复杂度是 O(1)

******、算法特点

稳定排序
算法简单,而且容易实现
适用于链式存储结构,只是在单链表上无需移动记录,只需要修改相应的指针
更适合初始记录基本有序(正序)的情况,当初始记录无序,n较大的时候,此算法的时间复杂度较高,不宜采用

2、折半插入排序

******、算法目的

在直接插入排序中,在已经排好序的序列中采用的是顺序查找找到待排序元素的插入位置,所以折半插入排序就是在已经排好序列中采用折半查找直接确定元素的插入位置,然后将待插入位置的当前元素全部后移一位,直接将待排序元素插入到该位置即可!

******、算法实现

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#define endl '\n'
using namespace std; 

int * InsertSort(int *p,int n){
	int i,j,temp;
	for(i = 1;i < n; i++){
		if(p[i] < p[i-1]){
		    temp = p[i];                                   //保存当前的较小值
		    int low = 0, high = i - 1,mid;                 //在[0,i-1]的有序区间内使用折半查找 
		    while(low <= high){                            //当low = high的时候,mid和low以及high指向同一位置,不论哪种情况都有 low > high,所以插入点必是 high + 1 
		    	mid = (low + high)/2;
		    	if (p[mid] > temp)        high = mid - 1;  //插入点在前一子表, 
				else                      low = mid + 1;   //插入点在后一子表,当p[mid] = temp,为了算法的稳定性,应该在后一子表中查找        
			} 
		    for(j = i - 1;j >= high + 1 ; j-- )            //记录后移 
		        p[j+1] = p[j];
		    p[high+1] = temp;                              //插入到空缺出来的位置 high + 1 
		}
	}
	return p; 
} 
main(){
	cout<<"\n请随便输入要执行插入排序的树:";
	int elem; 
	cin>>elem; 
	int i = 0;
	int sort[20];
	while(elem != -1){
		sort[i] = elem;
		i++;
		cin>>elem; 
	}
	int *address = InsertSort(sort,i);
	int n = 0;
	cout<<"\n折半插入排序结果打印如下:";
	while(n < i){
		cout<<address[n]<<" "; 
		n ++; 
	}
	
}

在这里插入图片描述

******、算法分析

时间复杂度:
    从时间上来看,折半查找比顺序查找要快,所以就平均性能来看,折半查找要优于直接插入排序!
    在平均情况下,折半插入排序仅减少了关键字的比较次数,而记录移动的次数保持不变!
    因此折半插入排序的时间复杂度仍是 O(n*n)
空间复杂度:
    折半插入排序和直接插入排序相同,只需要一个辅助变量 temp来记录当前待排序的元素,所以该算法的时间复杂度仍是 O(1)
    所以该算法的最坏和平均时间复杂度都是 o(n*n)
    但是该算法的最好时间复杂度是o(n*log2n)(n个数,每个数字进行至少log2N次比较),所以总共需要进行 n * log2N

******、算法特点

稳定排序!
因为要进行折半查找,所以要具有随机存取的特性,所以只能使用顺序结构,而不能使用链式结构!
适合初始记录无序,n较大的时候,因为采用的是折半查找,所以能减少关键字的比较次数!

3、希尔排序

******、算法简介

希尔排序,又叫“缩小增量排序”,也是插入排序的一种,确切的说叫“分组插入排序”。直接插入排序,当待排序的记录个数较少且待排序序列的关键字基本有序时,效率较高。希尔排序基于以上两点,从“减少记录个数”“序列基本有序”两个方面队直接插入排序进行改进!

******、算法步骤

希尔排序分组不是简单地“逐段分割”,而是将相隔某个“增量” 的记录分成一组!
(1)、第一趟取增量d1(d1 < n),所有间隔为d1的分在同一组,在各个组中进行直接插入排序!
(2)、第二趟取增量d2(d2<d1),重复上面的分组排序
(3)、依次类推,直到所取的增量di = 1(di<di-1<…<d1),所有的记录都在同一组,进行直接插入排序为止

******、算法实现

希尔排序的算法实现可以直接在直接插入算法上修改,直接插入排序可以看成一趟增量是1的希尔排序,将直接插入排序中增量 i 改写成 i = dk 或者 i = di[k],而不是 i = 1

int * ShellInsertSort(int *p,int dk,int n){//对数组p做一趟增量是dk的直接插入排序 
	int i,j,temp;
	for(i =dk;i < n; i++){             //dk指向第m个子表的第二个元素 
		if(p[i] < p[i-dk]){            //该子表内进行插入排序 
		    temp = p[i];               //保存当前的较小值
		    for(j = i - dk;j >= 0 &&  temp < p[j] ; j -= dk )   //依次将temp(保存的是当前下标 i 中的值)中的值与下标 i之前的值进行比较,p[j] > temp 的元素依次后移一位,直到 j < 0 或者 p[j] < temp 退出循环 
		        p[j+dk] = p[j];
		    p[j+dk] = temp;
		}
	}
	return p; 
} 

为了方便实现希尔排序算法,直接将多次增量排序与一趟增量排序写到一个函数中,方便函数调用直接打印结果!

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#define endl '\n'
using namespace std; 

int * ShellInsertSort(int *p,int di[],int n){  //对数组p做一趟增量是dk的直接插入排序 
    for(int k = 0; k < 3; k++){                //一趟增量为di[k]的希尔排序 
    	int i,j,temp;
	    for(i = di[k];i < n; i++){             //dk指向第m个子表的第二个元素 
			if(p[i] < p[i-di[k]]){             //该子表内进行插入排序 
			    temp = p[i];                   //保存当前的较小值
			    for(j = i - di[k];j >= 0 &&  temp < p[j] ; j -= di[k] )   //依次将temp(保存的是当前下标 i 中的值)中的值与下标 i之前的值进行比较,p[j] > temp 的元素依次后移一位,直到 j < 0 或者 p[j] < temp 退出循环 
			        p[j+di[k]] = p[j];
			    p[j+di[k]] = temp;
			}
	   }
    }
    return p; 
} 

main(){
	cout<<"\n请随便输入要执行希尔排序的数:";
	int elem; 
	cin>>elem; 
	int i = 0;
	int sort[20];
	while(elem != -1){
		sort[i] = elem;
		i++;
		cin>>elem; 
	}
	int t[3] = {5,3,1},*address;
	address = ShellInsertSort(sort,t,i);
	cout<<"\n----------希尔排序结果打印如下----------!\n";
    int n = 0;
	cout<<"\n"; 
	while(n < i){
		cout<<address[n]<<" "; 
		n ++; 
	}
}

在这里插入图片描述

******、算法分析

时间复杂度难以确定,与递增序列di有关!
空间复杂度:需要一个辅助变量 temp来记录当前待排序的元素,所以该算法的时间复杂度仍是 O(1)
时间复杂度是o (n 的1.3次方)

******、算法特点

(1)、记录跳跃式地移动,导致算法是不稳定的的!
(2)、只能用于顺序结构,而不能用于链式存储(因为需要指定位置进行排序)
(3)、增量序列有多种取法,但是应该使增量序列中的值di没有公因子,并且最后一个增量值必须是1
(4)、记录比价的总次数和移动次数都要比直接插入排序树要少,n越大,这种效果越明显。所以适合 n 越大并且初始记录无序的情况!

******、算法实例

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q渡劫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值