排序——插入排序(直接插入排序、希尔排序、折半插入排序)

心已入冬 提交于 2020-01-04 02:39:55

排序这个文章我本来想写一篇的,但是想了想太长了,最后我还是决定拆开来写吧。第一部分主要是讲插入排序的,包括直接插入排序和希尔排序。

排序算法合辑

  1. 排序——插入排序(直接插入排序、希尔排序、折半插入排序)
  2. 排序——插入排序(交换排序、快速排序)
  3. 排序——选择排序(简单选择排序、堆排序)

直接插入排序(straight insertion sort)

直接插入排序算法简单、容易实现,当序列中的记录基本有序或待排序记录较少时,是最佳的排序方法。基本思想是依次将待排序序列中的每一个记录插入到一个已排好序的序列中,直到全部记录都排好序。可以想一下打扑克的时候,每抓到一张牌就会插入到你手中已经按顺序理好的牌中。
在这里插入图片描述

  1. 将整个待排序的记录划分为有序区和无序区,初始时有序区为待排序记录序列中的第一个记录,无序区包括所有的剩余待排序记录
  2. 讲无序区第一个记录插入到有序区的合适位置中,从而使无序区减少一个记录,有序区增加一个记录
  3. 重复2直到无序区中没有记录为止。

例如待排序序列序列12 15 9 20 6 31 24

初试键值序列 [ 12 ] 15 9 20 6 31 24
第一趟 [ 12 15 ] 9 20 6 31 24
第二趟 [ 9 12 15 ] 20 6 31 24
第三趟 [ 9 12 15 20 ] 6 31 24
第四趟 [ 6 9 12 15 20 ] 31 24
第五趟 [ 6 9 12 15 20 31 ] 24
第六趟 [ 6 9 12 15 20 24 31 ]

代码

a[n]为待排序数组,其中有n个空间,但是注意此时是将数组a[n]的第一个位置,即a[0]作为哨兵,暂存你从无序区选到的数。因此你实际排序的数只有n-1个。

void InsertSort(int r[],int n)
{
	int i,j;
	for(i=2;i<n;i++)				//0号单元作为哨兵
	{
		r[0]=r[i];					//暂存关键码
		for(j=i-1;r[0]<=r[j];j--)	//r[0]<r[j]也可
			r[j+1]=r[j];
		r[j+1]=r[0];
	}
}

算法分析

/ 时间复杂度 空间复杂度
最好情况 O(n)O(n)
最坏情况 O(n2)O(n^2)
平均 O(n2)O(n^2) O(1)O(1 )

实例

待排序序列12,46,75,46,1,35,64 共七个数,因此需要设置一个容量为8的数组。
在这里插入图片描述
在这里插入图片描述

#include<iostream>
using namespace std;
void print1(int r[],int n) //过程输出 
{
	cout<<"r["<<0<<"] = "<<r[0]<<" 哨兵"<<endl; 
	for(int i=1;i<n;i++)
		cout<<"r["<<i<<"] = "<<r[i]<<endl; 
}
void print2(int r[],int n) //常规输出 
{
	for(int i=1;i<n;i++)
		cout<<"r["<<i<<"] = "<<r[i]<<endl; 
}
void InsertSort(int r[],int n)
{
	int i,j;
	for(i=2;i<n;i++)				//0号单元作为哨兵
	{
		r[0]=r[i];					//暂存关键码
		for(j=i-1;r[0]<=r[j];j--)
			r[j+1]=r[j];
		r[j+1]=r[0];
		cout<<"第"<<i-1<<"趟结果"<<endl;
		print1(r,8);
		cout<<endl;
	}
}

int main() 
{
	int r[8]={0,12,46,75,46,1,35,64};
	cout<<"待排序数组:"<<endl; 
	print2(r,8);
	cout<<endl;
	InsertSort(r,8);
	cout<<"排序后数组:"<<endl; 
	print2(r,8);
    return 0;
}

折半插入排序

这个就不多说了,就是把直接插入排序中在有序区进行顺序查找替换为二分查找。

代码

void BInsertSort(int a[],int n)
{
	int i,j,low,high,mid;
	for (i=2;i<n;i++)
	{
		a[0]=a[i];
		low=1;
		high=i-1;
		while(low<=high)
		{
			mid=(low+high)/2;
			if (a[mid]>a[0])
				high=mid-1;
			else
				low=mid+1;
		}
		for(j=i-1;j>=high+1;j--)
			a[j+1]=a[j];
		a[high+1]=a[0]; //a[low]=a[0];
	}
}

希尔排序(shell sort)

希尔排序又称缩小增量排序,是对直接插入排序的一种改进。基本思想是将待排序序列分成若干子序列,在子序列内进行插入排序,待整个序列基本有序时,对全体记录进行一次插入排序。

排序过程

假设待排序的记录为n个,先取整数dnd<n,将所有相距为d的记录构成一组,从而将整个待排序记录序列分割成为d个子序列,对每个子序列分别进行直接插入排序,然后再缩小间隔d。重复上述分割,再对每个子序列分别进行直接插入排序,直到最后取d=1,即将所有记录放在一组进行一次直接插入排序,最终将所有记录重新排列成按关键码有序的序列。d的取值可以自己决定,一般来说,按照希尔最早提出的方法d1=n/2d_1=\lfloor n/2 \rfloordi+1=di/2d_{i+1}=\lfloor d_i/2 \rfloor,最后增量为1。
例如:待排序徐序列59,20,17,36,98,23,14,13,28

shell sort d 序列
第一趟 9/2=49/2=4 28    59    98
20    23  
 14    17 
   13    36
结果:28 20 14 13 59 23 17 36 98
第二趟 4/2=24/2=2 14 17 28 59 98
 19 20 23 36 
结果:14 19 17 20 28 23 59 36 98
第三趟 2/2=12/2=1 (即直接插入排序)
结果:14 17 19 20 23 28 36 59 98

代码

希尔排序中a[n]为待排序数组,其中有n个空间,注意此时是将数组a[n]的第一个位置,即a[0]作为暂存单元,不是哨兵。因此你实际排序的数只有n-1个。

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++)
		{
				a[0]=a[i];
				for (j=i-d;j>0&&a[0]<a[j];j=j-d)
					a[j+d]=a[j];
				a[j+d]=a[0];
		}
	}
}

算法分析

/ 时间复杂度 空间复杂度
最好情况 O(nlog2n)O(nlog_2n)
最坏情况 O(n2)O(n^2)
平均 O(n1.3)O(n^{1.3}) O(1)O(1 )

实例

待排序序列59,20,17,36,98,23,14,13,28 共9个数,因此需要设置一个容量为10的数组。
在这里插入图片描述

#include<iostream>
using namespace std;
void print(int r[],int n) //常规输出 
{
	for(int i=1;i<n;i++)
		cout<<"r["<<i<<"] = "<<r[i]<<endl; 
	cout<<endl; 
}

void ShellSort(int a[],int n)
{
	int d,i,j;
	int count=1; //用于过程输出,可删除 
	for(d=n/2;d>=1;d/=2)
	{
		for (i=d+1;i<n;i++)
		{
				a[0]=a[i];
				for (j=i-d;j>0&&a[0]<a[j];j=j-d)
					a[j+d]=a[j];
				a[j+d]=a[0];
		}
		/*-----过程输出可删除--------*/ 
		cout<<"第"<<count<<"趟"<<endl;
		cout<<"d = "<<d<<endl;
		count++;
		print(a,n);
		/*-----过程输出--------*/ 
	}
}
int main()
{

	int a[10]={0,59,20,17,36,98,23,14,13,28};
	cout<<"待排序数组"<<endl;
	print(a,10);
	ShellSort(a,10);
	cout<<"排序结果"<<endl;
	print(a,10);
	return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!