DSA之十大排序算法第三种:Straight Insertion Sort

直接插入排序

相比较于简单粗暴的冒泡和直接选择排序,直接插入排序就略显复杂了。不过直接插入排序相较于 班级学生按身高从低到高进行排序的方式一样,第一个学生先进行站队,第二个学生若是比他低,则站前面否则站后面。无论如何,此时他们二人已经有序,此刻第三个学生来了,同理 找到自己的位置 插进站队里面。一直到最后一名学生也找到自己的位置,整个排队过程结束。道理上是一模一样的。注:我们这里实现的所有排序算法,统一由小到大 C++实现。

排序步骤就是:

  1. 将整个无序序列的第一个元素看做是一个有序序列;把从第二个元素开始到最后一个元素结束看做是无序序列。
  2. 从头到尾依次 扫描无序序列,将扫描到的每个元素 挨个 插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

在这里插入图片描述

分析具体实现:

如上图所示(从菜鸟网站“拿”来的):面对上面的一组数据:

1,50,38,5,47,15,36,26,27,2,46,4,19,44,48

在这里我们之间跳跃一下:此刻到第三个数据38 找插入点了

1,50       38,5,47,15,36,26,27,2,46,4,19,44,48

当然38的插入点在50前面,也就是说 有序序列从最后一个元素开始依次向后移动一位,直到找出在有序序列里面 小于或者等于新元素38的位置(也就是插入点),然后将38 插入即可。根据 减而治之 的策略,可以证明其正确性和有穷性。

分析具体算法:
  1. 根据上面动图所示:依次将无序序列里面的第一个元素取出来,然后在前面已然有序的区间里面找 小于或者等于新元素的位置(也就是插入点),然后将新元素插入即可。我们这里只是以 升序 为例子进行分析。
  2. 当然最好的情况就是已然有序,需要进行n-1次操作。取出来一个,然后它的插入点本来就是它原来的位置,然后插入。当然在这种情况下:最优时间复杂度O(n)
  3. 最坏情况:完全逆序。每一个无序序列里面的第一个元素取出来,然后在前面已然有序的区间里面找 小于或者等于新元素的位置(也就是插入点),然后将新元素插入的过程中。它前面的每一个有序序列的元素 都要 向后面移动一位,这样就牵扯到了巨大的数据移动。 进行数据比较 和 数据移动的次数都是n(n-1)/2次,这个就非常不好了。此时的时间复杂度O(n²)
  4. 直接插入排序是在一个已经有序的区间上,一次只插入一个元素。在最开始的时候,这个有序的区间只有第一个元素。数据的比较是从有序序列(已排序好的)的末尾开始,也就是将这个新元素和已经有序的最大值开始比起,直到找到一个已排序的元素小于或者等于新元素的位置(插入点),将新元素插入到该位置后面。 如果碰见一个和待插入元素(新元素)相等的,那么就把待插入元素(新元素)放在此刻相等元素的后面。所以相等元素的前后顺序没有改变,从原无序序列最初的顺序就是排好序后的顺序,所以对于直接插入排序而言,它是稳定的。
算法初始版本:
void Straight_Insertion_Sort1(vector<int>& src)//使用的是数组后移,然后插入
{
	int size = src.size();
	int count = 0;//计作移动的次数
	for (int i = 0; i < size - 1; ++i)
	{
		int j = i + 1;//取出无序序列的第一个元素
		int k = i;
		int tempval = src[j];
		while (k >= 0 && tempval < src[k])//这个k的比较 必须放前面
		{
			count++;
			src[j] = src[j - 1];
			k--;
			j--;
		}
		src[j] = tempval;//插入队中
		cout << "第" << setw(2) << i + 1 << "次排序后,结果为:";
		for (int k = 0; k <= size - 1; ++k)
		{
			cout << setw(2) << src[k] << " ";
		}
		cout << endl;
	}
	cout << "整个排序过程共进行了" << count << "次的移动" << endl;
}

上面的是通过 数组数据的向右移动来 空出插入位置。

算法优化版本:
void Straight_Insertion_Sort2(vector<int>& src)//使用的是两两交换前进
{
	int size = src.size();
	int count = 0;//计作交换的次数
	for (int i = 0; i < size - 1; ++i)
	{
		int j = i + 1;//取出无序序列的第一个元素
		int k = i;
		while (k >= 0 && src[j] < src[k])//这个k的比较 必须放前面
		{
			count++;
			swap(src[k], src[j]);
			k--;
			j--;
		}
		cout << "第" << setw(2) << i  + 1 << "次排序后,结果为:";
		for (int k = 0; k <= size - 1; ++k)
		{
			cout << setw(2) << src[k] << " ";
		}
		cout << endl;
	}	
	cout << "整个排序过程共进行了" << count << "次的交换" << endl;
}

优化版本 其实谈不上优化,数据的两两交换反而不如数组数据移动,移动的次数 和 交换的次数一样多。

算法测试打印:
#include <iostream>
#include <vector>
#include <iomanip>
#include <algorithm>
using namespace std;

void Straight_Insertion_Sort2(vector<int>& src)//使用的是两两交换前进
{
	int size = src.size();
	int count = 0;//计作交换的次数
	for (int i = 0; i < size - 1; ++i)
	{
		int j = i + 1;//取出无序序列的第一个元素
		int k = i;
		while (k >= 0 && src[j] < src[k])//这个k的比较 必须放前面
		{
			count++;
			swap(src[k], src[j]);
			k--;
			j--;
		}
		cout << "第" << setw(2) << i  + 1 << "次排序后,结果为:";
		for (int k = 0; k <= size - 1; ++k)
		{
			cout << setw(2) << src[k] << " ";
		}
		cout << endl;
	}	
	cout << "整个排序过程共进行了" << count << "次的交换" << endl;
}

void Straight_Insertion_Sort1(vector<int>& src)//使用的是数组后移,然后插入
{
	int size = src.size();
	int count = 0;//计作移动的次数
	for (int i = 0; i < size - 1; ++i)
	{
		int j = i + 1;//取出无序序列的第一个元素
		int k = i;
		int tempval = src[j];
		while (k >= 0 && tempval < src[k])//这个k的比较 必须放前面
		{
			count++;
			src[j] = src[j - 1];
			k--;
			j--;
		}
		src[j] = tempval;//插入队中
		cout << "第" << setw(2) << i + 1 << "次排序后,结果为:";
		for (int k = 0; k <= size - 1; ++k)
		{
			cout << setw(2) << src[k] << " ";
		}
		cout << endl;
	}
	cout << "整个排序过程共进行了" << count << "次的移动" << endl;
}
int main()
{
	int Array[] = { 1,50,38,5,47,15,36,26,27,2,46,4,19,44,48 };
	vector<int>myvec(begin(Array), end(Array));

	Straight_Insertion_Sort1(myvec);
	cout << "排序结束后,最终结果:";
	for (int val : myvec)
	{
		cout << setw(2) << val << " ";
	}
	cout << endl;
	cout << "**********************************" << endl;
	vector<int>myvec2(begin(Array), end(Array));
	Straight_Insertion_Sort2(myvec2);
	cout << "排序结束后,最终结果:";
	for (int val : myvec2)
	{
		cout << setw(2) << val << " ";
	}
	return 0;
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

孤傲小二~阿沐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值