排序(六大排序)

一,插入排序

插入排序根据查找插入位置的方式不同可以分为三类:

按顺序法查找插入位置的——直接插入排序

按折半法也叫二分法查找插入位置的——折半插入排序;

缩小增量多遍插入排序的——希尔排序

1,直接插入排序

算法步骤

  • 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。

  • 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

动画演示aHR0cDovL3d3dy5jeHl4aWFvd3UuY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE5LzEwLzE1NzEwNTgyODAtMjI4NTA5NjY4ZGE5OTM5LmdpZg

 参考代码

//直接插入排序
void insert(Elemtype A[],int n){//插入排序 
	int i,j;
	for(int i=2;i<=n;i++){  //A[1]直接插入,不需要比较,A[0]相当于哨兵,暂时存放要插入的数据 
		if(A[i]<A[i-1]){ //如果要插入的数小于前一位,先将这个数存入A[0] 
			A[0]=A[i];
			for(j=i-1;A[0]<A[j];j--){ 
                 //让j的位置指向要插入数据的前一位,依次向前遍历并且与A[0]比较
				A[j+1]=A[j]; 
             //要插入的数A[0]小于前面的数时,依次将前面的数向后移 
			}
			A[j]=A[0];  //循环结束后留出空位置j恰好为要插入的地方,再将A[0]赋值给A[j] 
		}
	}
} 

直接插入排序算法的性能分析:

  • 空间效率:仅使用了常数个辅助单元,因而空间复杂度为O(1).
  • 时间效率:每趟操作都分为比较关键字和移动元素,而比较次数和移动次数取决于待排序表的初始状态。在最好的情况下,表中元素已经有序,此时每插入一个元素,都只需要比较一次而不用移动元素,因而时间复杂度为O(n)。在最坏的情况下,表中元素顺序刚好与排序结果中的元素顺序相反,总的比较次数和移动次数达到最大,总的时间复杂度为O(n^2)。
  • 时间复杂度:总的比较次数与总的移动次数均为n^2/4,因此直接插入排序法时间复杂度为O(n^2)。
  • 稳定性:直接插入排序是稳定的排序算法
  • 适用性:直接插入排序法适用于顺序存储和链式存储的线性表。为链式存储时,可以从前往后查找指定元素的位置。

2,折半插入排序

算法步骤

  借助二分查找的思想,先查找插入位置,再移动数据,最后插入到正确的位置。
  我们都知道直接插入排序是一个边比较边移动的过程,而折半插入排序是先确定插入的位置,再来进行移动。
  插入排序的效率是由比较的次数和移动的次数共同决定的,而折半插入排序就是通过降低比较的次数来提高排序的效率。

参考代码

//折半插入排序
void BinaryInsertSort(ElementType array[], int size)
{
	int high;//高地址位置
	int low;//低地址位置
	int mid;//中端位置
	int i;//指示无序组中的元素位置
	int j;//指示有序组中的元素位置
	ElementType temp;//临时变量,用于拷贝数据
	for (i = 1; i < size; i++)//第一个位置默认有序
	{
		temp = array[i];//拷贝数据
		high = i - 1;//指向有序组中最后一个有效数据
		low = 0;//指向有序组中第一个数据位置
		while (low<=high)//采用二分查找,在有序组中找待插入元素的位置
		{
			mid = (low + high) / 2;//中间位置为low于high和的一半
			if (array[mid] < temp)//说明插入位置在mid的右边
			{
				low = mid + 1;//指向比较位置的后一个位置
			}
			else//说明插入位置在mid的左边
			{
				high = mid - 1;//指向比较位置的前一个位置
			}
		}
		for (j = i; j >=low; j--)//将查找的位置元素及其后面的元素整体移动一个位置
		{
			array[j] = array[j - 1];
		}
		array[low] = temp;//插入到正确位置
	}
}

 算法分析

从上述算法中,折半插入仅减少了比较元素的次数,约为O(nlog2n),该比较次数与待排序表的初始状态无关,仅取决于表中从元素个数n,而元素的移动次数并未改变,它依赖于待排序表的初始状态,因此折半插入的时间复杂度仍为O(n^2),折半插入排序是一种稳定的排序方法。

3,希尔排序

步骤:
1.先选定一个小于N的整数gap作为第一增量,然后将所有距离为gap的元素分在同一组,并对每一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量,重复上述操作…
2.当增量的大小减到1时,就相当于整个序列被分到一组,进行一次直接插入排序,排序完成。
动图如下:
20210509190237603.gif#pic_center
思路:
希尔排序,先将待排序列进行预排序,使待排序列接近有序,然后再对该序列进行一次插入排序,此时插入排序的时间复杂度为O(N),

 参考代码

//希尔排序
void ShellSort(int* arr, int n)
{
	int gap = n;
	while (gap>1)
	{
		//每次对gap折半操作
		gap = gap / 2;
		//单趟排序
		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tem = arr[end + gap];
			while (end >= 0)
			{
				if (tem < arr[end])
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = tem;
		}
	}
}

 

  •  空间复杂度为O(1)
  • 希尔排序是一种不稳定的排序方法
  • 希尔排序算法仅适用于线性表为顺序存储的情况

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bl‥ck

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

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

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

打赏作者

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

抵扣说明:

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

余额充值