插入类排序详解及总结

插入排序的基本思想:在一个已排好序的纪录子集的基础上,每一步将下一个待排序的记录有序插入到已排好序的记录子集中,直到将所有待排记录全部插入为止。例如:打扑克牌时抓牌就是插入排序的一个很好的例子,每抓一张牌,插入到合适位置,知道抓完牌为止,即可得到一个有序序列。

直接插入排序

【算法思想】 直接插入排序是一种最基本的插入排序方法,其基本操作是将第i个记录插入到前面i-1个已排好序的记录中。具体过程为:将第i个记录的关键字K,顺次与其前面记录的关键字K(i-1),k(i-2),... ,K1,进行比较,将所有关键字大于Ki的记录依次向后移动一个位置,直到遇见一个关键字小于或者等于Ki的记录Kj,此时Kj后面必为空位置,将第i个记录插入空位置即可。完整的插入排序是从第二个数开始的,也就是说,将第一个记录视为已排好序的单元素子集合,然后将第二个记录插入到单元素子集合中。i从第二个数循环到第n个数,即可实现完整的直接插入排序。

void InsertSort(int *arr, int size)
{
	int j = 0;
	int key = 0;
	for (int i = 1; i < size; ++i)
	{
		key = arr[i];
		j = i - 1;
		while (key<arr[j])
		{
			arr[j + 1] = arr[j];
			j = j - 1;
		}
		arr[j + 1] = key;
	}
}

【算法分析】 首先从空间角度看,它只需要一个辅助空间。

从时间耗费角度看,主要耗时在关键字比较和移动元素上。

最好的情况(顺序):while循环只执行一次,且不移动记录。

最坏的情况(逆序):while循环关键字比较次数和移动记录的次数为i-1;

直接插入排序是一种稳定的排序方法。

时间复杂度为T(n)=O(n^2);  空间复杂度S(n)=O(1);


折半插入排序

【算法思想】对于有序表进行折半查找,其性能优于顺序查找。所以,可以将折半查找思想用于有序记录中确定应插入位置,相应的排序法称为折半插入排序法。

void BinSort(int arr[], int size)
{

	for (size_t idx = 1; idx < size; ++idx)
	{
		int temp = arr[idx];
		int end = idx - 1;
		int left = 0;
		int right = idx - 1;
		int pos = 0;
		int mid = 0;
		while (left <= right)
		{
			mid = left + ((right - left) >> 1);
			if (temp < arr[mid])
			{
				right = mid - 1;
			}
			else
			{
				left = mid + 1;
			}
		}

		for (pos = end; pos >= left; --pos)
			arr[pos + 1] = arr[pos];

		arr[left] = temp;
	}
}

【算法分析】采用折半插入排序法,可将少关键字的比较次数。当数据量较大时,折半插入排序的比较次数比直接插入排序的最差情况要好很多,但比其最好情况要差。

虽然折半插入排序法与直接插入排序法比较,改善了算法中比较次数的数量级,达到O(nlog2n),但并未改变移动元素的时间耗费,所以折半插入排序的总的时间复杂度仍是O(n^2);


希尔排序

【算法改进要点】直接插入排序,在待排序的关键字序列基本有序切关键字个数n较少时,其算法性能最佳。希尔排序又称缩小增量排序法,是一种基于插入思想的排序方法,他利用了直接插入排序的最佳性质,首先,将待排序列的关键字序列分成若干个较小的子序列,对子序列进行直接插入排序,是整个待排序序列排好序。在时间耗费上,较直接插入排序法的性能大有改进。然后,在进行直接插入排序时,若待排记录序列已经有序,直接插入排序的时间复杂度可以提高到O(n)。若待排序记录序列基本有序,即序列中具有下列特性的记录较小时:arr[i]<Max{arr[j]},(1<=j<i),直接插入排序的效率会大大提高。希尔排序正是基于以上两点对直接插入排序进行了改进。

【算法思想】先将待排序咧记录序列分割成若干个“较稀疏的”子序列,分别进行直接插入排序。经过上述粗略调整,整个序列中的记录已经基本有序,最后在对全部记录进行一次直接插入排序。

1.首先选定记录间的距离为d(i)(i=1),在整个待排序列记录序列中将所有见各位d(1)的记录分成一组,进行组内直接插入排序。

2.然后去i=i+1,记录间的距离为d(i)(d(i)<d(i-1)),在整个待排序记录序列中,将所有间隔为d(i)的序列分成一组,进行组内直接插入排序。

重复步骤2多次,直至纪录间的距离d(i)=1,此时整个只有一个子序列,对该序列进行直接插入排序,完成整个排序过程。


void ShellSort(int arr[], size_t size)
{
	int gap = size;
	while (gap>1)
	{
		gap = gap / 3 + 1;
		for (size_t idx = gap; idx < size; ++idx)
		{
			int temp = arr[idx];
			int end = idx - gap;
			while (end >= 0 && temp<arr[end])
			{
				arr[end + gap] = arr[end];
				end -= gap;
			}
			arr[end + gap] = temp;
		}
	}
}
【算法分析】1.增量的取法,最初希尔提出d=[n/2],再取d=[d/2],直到d=1为止。该思路的缺点是,奇数位置的元素在最后一步才会与偶数位置的元素进行比较,使得希尔排序效率降低。因此后来Knuth提出d=[d/3+1]。此外还有其他多种取法,但均没有给出最优性证明。

因为增量取法不同,一般认为他的时间复杂度为O(n^1.5),比直接插入要好。

希尔排序对于中等规模(n<=1000)的序列具有较高的效率,且希尔排序算法简单,容易执行。因此很多排序应用程序都选用了希尔排序。

希尔排序是不稳定的排序方法。


总结:



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值