【排序】插入排序,希尔排序,选择排序,冒泡排序,堆排序,归并排序优化及稳定性分析

InsertSort:(以升序为例)

插入排序的基本思想是:

    每次将一个待排的记录,按照其关键字的大小,插入到前面已经排好序的有序区中适当的位置,直到全部记录插入完毕为止。

    假设待排序的记录存放在数组R[0..n]中,初始时R[0]是一个有序区,R[1..n]是无序区,从i=1开始,依次将R[i]插入到有序区R[0..i-1]中,生成一个包含n个记录的有序区

void InsertSort(int *arr, int len)
{
	int i = 0;
	int j = 0;
	int tmp = 0;
	for (i = 1; i < len; i++)
	{
		tmp = arr[i];
		for (j = i; j>0 && arr[j - 1]>tmp; j--)       //在有序区找到一个正确的位置
		{
			arr[j] = arr[j - 1];                  //将当前记录后移
		}
		arr[j] = tmp;                             //将tmp插入到找到的位置
	}
}

优化:折半插入

既然有序区已经有序,那么我们在寻找合适位置的时候,可以使用折半查找的思想在有序区寻找这个位置。

稳定性

  直接插入排序是稳定排序,即相同的数据元素在排序前后不会改变二者之间的相对顺序。

希尔排序

  希尔排序又叫缩小增量排序,其实希尔排序就是插入排序的一种,只不过对插入排序做了优化。 
  在插入排序中,我们每次相当于每隔一个的再插入,希尔排序的思想是:

设待排序元素有n个,首先取一个整数gap<n作为间隔,将全部元素分为gap组,所有距离为gap的元素都在同一个组里,然后对每一个组都进行插入排序。然后缩小增量,直到gap==1的时候整个序列都会变成有序的。

希尔排序与插入排序的比较:

       插入排序在什么情况下效率最高呢???

扫描二维码关注公众号,回复: 2768732 查看本文章

       如果当要排序的序列大致上接近于有序的时候,插入排序的效率最高,基本上接近O(N)。因为当数据基本上接近于有序的时候,元素向有序区插入的时候比较的次数会很少,同样有序区元素向后移动的次数也是很少的。

如果当一组数据接近的有序的话,我们优先使用插入排序,效率高。如果当一组数据接近于逆序的话,我们使用希尔排序会更优。插入情况最好的情况就是希尔排序最坏的情况,因为这时候希尔排序的预排序起不到作用。插入排序最坏的情况则是希尔排序最高的情况,因为这时候预排序的效果是非常明显的。

稳定性

  希尔排序是不稳定的排序,因为我们将原序列分组了,不能保证两个相同的数据在同一组,所以导致其是不稳定排序。

选择排序

选择排序的基本思想:初始时,有序区为0,每一趟在无序区中选出一个关键字最小的元素,然后与无序区第一个元素交换,然后无序区减1,有序区加1,直到记录全部排完

优化

  既然每一次我们都会遍历待排序序列,那何尝不同时将最大值,最小值都找出来,分别放入待排序序列的头和尾呢?

稳定性

   选择排序是不稳定的排序方法。

冒泡排序

         它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。 

冒泡排序的优化

           我们已经知道当走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成,所以我们可以设置一个标志位来判断是否已经排序完毕。

void BubbleSort(int *arr, int len)
{
	assert(arr);
	int i = 0;
	int j = 0;
	int flag = 0;
	int tmp = 0;
	for (i = 0; i < len - 1; i++)
	{
		flag = 1;                              //flag初始时为1
		for (j = 0; j < len - i - 1; j++)       //每排序一趟,则必然后面有一个已经有序,可以缩小排序的范围
		{
			if (arr[j]>arr[j + 1])             //只要要交换数据,则flag就会被修改
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;                    //只要这组数还未完全有序,则一定会修改flag为0
			}
		}
		if (flag)                             //如果排序一趟,发现已经有序,则不进入if,flag没被修改
		{
			break;
		}
	}
}

稳定性

  冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

堆排序

        是用数组模拟树的形式进行的排序。但是堆数据结构只保证父节点的值大于或者小于子节点的值,并不保证整体的顺序。但是我们可以利用根节点的值永远是最大,或者最小的这个特性,利用根节点找出最值,再与尾位置互换,即可排序成功。 

稳定性

  因为每次根节点的值都是不确定的,所以我们也不能保证相同元素的父节点是相同的,所以堆排序是不稳定的

归并排序

    是一种基于分治法的一种排序方法。它将要排序的序列分成两个长度相等的子序列,为每一个子序列进行排序,然后再将子序列合并成一个有序的序列。

优化:

  当划分的子区间的元素小于13个元素左右时,再使用归并的话会效率不会很高,相当于多增加了二叉树的后面几层的结点。这时候我们改用直接插入排序来进行优化

稳定性

归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序),然后把各个有序的段序列合并成一个有 序的长序列,是稳定排序。

猜你喜欢

转载自blog.csdn.net/qq_38936579/article/details/81635662