排序--插入排序&&希尔排序

一.插入排序

1.直接插入排序

简单说明:是一种稳定的排序算法(稳定就是说两个数的大小一样的话不会交换位置),时间复杂度为O(N^2)

插入排序的时间复杂度分析。在最坏情况下,数组完全逆序,插入第2个元素时要考察前1个元素,插入第3个元素时,要考虑前2个元素,……,插入第N个元素,要考虑前 N - 1 个元素。因此,最坏情况下的比较次数是 1 + 2 + 3 + ... + (N - 1),等差数列求和,结果为 N^2 / 2,所以最坏情况下的复杂度为 O(N^2)

最好情况下,数组已经是有序的,每插入一个元素,只需要考查前一个元素,因此最好情况下,插入排序的时间复杂度为O(N)

算法很简单:就是取两个变量end和key,end指向排序好的序列的最后一个元素的下标,排序好的序列最后一个元素的后面那个元素的值即没有排序的那个值,然后比较两个值,如果要排序升序的话,则key小于array[end]的话,则将array[end]往后移一位,即移动到key的位置,然后end--向前指,用key继续和其比较,如果key还是小于array[end]的话继续将这个array[end]这个元素向后移动一位,如果key不小于array[end]的值的话,则将其插入到之前刚移动的元素的位置,即array[end+1]=key.


代码如下:

void InsertSort(int *array,size_t size)//插入排序,不需要借助新空间
{
	for (size_t i = 1; i < size; ++i) 
	{
		int key = array[i];
		int end = i - 1;
		//找当前元素需要插入的位置
		while (end>=0&&key < array[end])//end>0避免end越界
		{
			array[end + 1] = array[end];//往后搬移
			--end;
		}
		array[end + 1] = key;
	}
}
2.二分插入排序
因为从直接插入排序的过程中我们可以看出来,key之前的序列都是已经排序好的序列,所以对于排序好的序列我们都可以使用二分查找法,即使用二分查找法,通过比较在前面 已经排好的序列中找出要把key所插入的位置就可以了。这样的话就不需要我们一个一个去end--比较了,所以效率比较高一点


代码如下:

void InsertSort_OP(int *array, size_t size)
{
	for (size_t i = 1; i < size; i++)
	{
		int left = 0;
		int right = i - 1;
		int mid = 0;
		int key = array[i];
		while (left <= right)
		{
			mid = (right - left) / 2 + left;
			if (key < array[mid])
			{
				right = mid - 1;
			}
			else
			{
				left = mid + 1; 
			}
		}
		//上述算法找到要出入的位置以后下面开始搬移元素
		int end = i - 1;
		while (end>=left)//这里end>left或者right都可以,=left意思需要将left当前位置的元素搬走就可以了
		{
			array[end + 1] = array[end];
			--end;
		}
		//插入元素
		array[left] = key;
	}
}

二分查找的时间复杂度也是O(N^2),因为他只是 减少了查找的插入点的 次数,移动的次数和遍历的次数是不变的。

二.希尔排序(缩小增量排序)
希尔排序是 插入排序 的一种又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法

时间复





上述是整个希尔排序的流程图,下面给出代码,参考代码理解

void ShellSort(int *array, size_t size)
{
	int gap = size;
	while (gap>1 )
	{
		gap = gap / 3 + 1;
		for (size_t i = gap; i < size; i++)
	  {
		int key = array[i];
		int end = i- gap;
		//找当前元素需要插入的位置
		while (end >= 0 && key < array[end])//end>0避免end越界
		{
			array[end + gap] = array[end];//往后搬移
			end -= gap;
		 }
		array[end + gap] = key;
	  }
		/*gap--;*/
	}
}

下面 在给出完整的代码 和测试程序

#include<iostream>
using namespace std;

//直接插入
void InsertSort(int *array,size_t size)//插入排序,不需要借助新空间
{
	for (size_t i = 1; i < size; ++i) 
	{
		int key = array[i];
		int end = i - 1;
		//找当前元素需要插入的位置
		while (end>=0&&key < array[end])//end>0避免end越界
		{
			array[end + 1] = array[end];//往后搬移
			--end;
		}
		array[end + 1] = key;
	}
}
//时间复杂度
//空间复杂度
//稳定性,给成等号的话就不稳定,不加等号得到话是稳定的
//上述方法效率太低,既然前面已经排好序了就可以使用二分查找

//二分查找的插入排序,效率高
void InsertSort_OP(int *array, size_t size)
{
	for (size_t i = 1; i < size; i++)
	{
		int left = 0;
		int right = i - 1;
		int mid = 0;
		int key = array[i];
		while (left <= right)
		{
			mid = (right - left) / 2 + left;
			if (key < array[mid])
			{
				right = mid - 1;
			}
			else
			{
				left = mid + 1; 
			}
		}
		//上述算法找到要出入的位置以后下面开始搬移元素
		int end = i - 1;
		while (end>=left)//这里end>left或者right都可以,=left意思需要将left当前位置的元素搬走就可以了
		{
			array[end + 1] = array[end];
			--end;
		}
		//插入元素
		array[left] = key;
	}
}

//数据量大杂乱,希尔排序(缩小增量排序)
void ShellSort(int *array, size_t size)
{
	int gap = size;
	while (gap>1 )
	{
		gap = gap / 3 + 1;
		for (size_t i = gap; i < size; i++)
	  {
		int key = array[i];
		int end = i- gap;
		//找当前元素需要插入的位置
		while (end >= 0 && key < array[end])//end>0避免end越界
		{
			array[end + gap] = array[end];//往后搬移
			end -= gap;
		 }
		array[end + gap] = key;
	  }
		/*gap--;*/
	}
}
void display(int *array, int size)
{
	for (int i = 0; i < size; i++)
		cout << array[i] << " ";
	cout << endl;
}
void TestSort()
{
	int arr[] = { 9,5,5,6,8,2,3,1,7};
	int size = sizeof(arr) / sizeof(*arr);
	cout << "直接插入排序:";
     InsertSort(arr, size);
	 display(arr, size);
	 cout << "二分插入排序:";
	InsertSort_OP(arr, size);
	display(arr, size);
	cout << "希尔插入排序:";
	ShellSort(arr, size);
	display(arr, size);
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为你解答关于排序的问题。排序插入排序的一种改进算法,也被称为“缩小增量排序”。它的基本思想是将待排序的序列按照一定的间隔分成若干个子序列,对每个子序列进行插入排序,然后逐步缩小间隔,直到间隔为1,最后对整个序列进行一次插入排序排序的时间复杂度与间隔序列的选择有关,最坏时间复杂度为$O(n^2)$,平均时间复杂度为$O(n \log n)$。相比于插入排序排序的时间复杂度有了明显的提升,且其在实际应用中也表现得比插入排序好。 下面是排序的示例代码: ```python def shell_sort(arr): n = len(arr) gap = n // 2 while gap > 0: for i in range(gap, n): temp = arr[i] j = i while j >= gap and arr[j - gap] > temp: arr[j] = arr[j - gap] j -= gap arr[j] = temp gap //= 2 return arr ``` 在这段代码中,我们首先定义了一个排序的函数shell_sort,它接收一个列表arr作为参数,并返回排好序的列表。在函数中,我们首先获取列表的长度n和初始的间隔gap,然后开始循环,直到间隔为1为止。在每次循环中,我们将列表按照间隔分成若干个子序列,对每个子序列进行插入排序,将子序列按照升序排列。最后,我们将间隔除以2,继续循环直到间隔为1。 使用排序对列表进行排序非常简单,只需要调用shell_sort函数即可。例如,我们可以这样调用函数: ```python arr = [5, 2, 8, 3, 1, 6] sorted_arr = shell_sort(arr) print(sorted_arr) ``` 这段代码会输出排好序的列表[1, 2, 3, 5, 6, 8]。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值