408-数据结构-排序-插入排序&希尔排序&冒泡排序&快速排序

排序算法主要评判优劣程度角度:

  1. 时间空间复杂度
  2. 比较次数,交换次数
  3. 是否稳定:值相同能否在排序前后仍然保持相对位置

排序还可以分为内部排序与外部排序。数据正常存储在外存上,如果数据量太大,只能一部分掉入内存先排序。
内部排序主要针对数据全部在内存上,主要关注点使得时间空间复杂度最小就行。
外部排序还需要额外考虑外存数据换入换出的时间开销。

插入排序

算法思想:在已经有序的序列中,插入一个新的值。对于一个未被排序的序列,选定一个点,认为前面已经有序,这个点为需要插入的新的点。序列从前往后按照这种思想遍历一遍就可以有序了。
在有序序列中,插入新的值需要做的事情:
以数组a[i]为插入值,a[0]-a[i-1]已经有序,以递增为例。那么只需要从a[i-1]从后往前找,如果a[i-1]>a[i]就将a[i-1]往后移一个位置,当然a[i]需要存放在一个tem变量里面。直到找到<=或者越界为止。那么这个点的后一个结点就是需要将a[i]放的位置。
当然,寻找插入点由于前面已经有序,可以采取二分查找来进行优化。但其实没差,因为二分查找只能优化寻找位置,而寻找完之后,还是需要完成后移操作。所以时间复杂度在量级看来依旧不变。

空间复杂度:O(1)。
时间复杂度:

  • 最好的情况,也就序列已经有序的情况,对于每一个点只需要往前看一下就可以跳出第二层循环。所以最好时间复杂度为O(n).
  • 最差的情况,也就是序列刚好逆序,对于每一个点都需要遍历到头,当中遇到每一个点都需要将其往后移一个位置。也就是O(n2).
  • 平均时间复杂度O(n2)
    稳定性:在往前找的时候,可以设定直到找到<=停下来,所以可以保证稳定。
    可以针对链表使用。
void insertSort(int a[], int length){
	for (int i = 1; i < length ; i ++){
		int tem = a[i];
		int j;
		for (j = i-1 ; j > -1 && a[j] > tem ; j --){
			a[j+1] = a[j];
		}	
		a[j+1] = tem;
	}

	return;
}

希尔排序

可以认为是插入排序的优化。因为插入排序在序列已经大部分有序的情况下表现还是较为良好。所以希尔排序主要目标是先让序列整体大部分有序,然后最后再插入排序,从而实现优化。

算法过程:设定一个gap值,然后元素下标差值为gap的被认为是同一组,每一组完成插入排序。gap每次/2直到gap为0就停止。所以在最后一次排序gap为1时候,实际上也是插入排序,能够保证这个序列已经大部分有序。
一般来说gap初始设定为排序长度/2.

空间复杂度O(1)
时间复杂度没法判断,只能说上界应该是插入排序O(n2)。经过统计最好情况下能够达到O(n1.3)
稳定性:不稳定。因为可能两个值相同被分到不同的组。后面的就被换到前面。
不可以针对链表使用

void shellSort(int a[], int length){
	
	int gap = length << 1;
	for ( ; gap >= 1 ; gap <<= 1){
		for (int i = gap ; i < length ; i ++){
			int tem = a[i];
			int j;
			for (j = i - gap ; j > 0 && a[j] > tem ; j -= gap){
				a[j + gap] = a[j];
			}
			a[j+gap] = tem;
		}	
	}

	return;
}

冒泡排序

算法思想:我习惯从前比到后,然后最大的冒到最后一个。每一次内部迭代两两比较大的放后面,经过序列长度-1就能够保证最大的一定在最后面。每一次从n序列找最大,n-1序列找最大,…所以显然O(n2)

空间复杂度:O(1)
时间复杂度:如果不进行优化的话,显然时间复杂度稳定O(n2)这个量级。
如果进行优化,也就在内部循环开一个bool判断,如果在这一次循环没有发生交换操作,说明整个序列已经有序,就不需要进行n-1序列比较,算法停止在这就行。进行优化后的最好情况就是序列已经有序,此时只要第一寻找长度n序列最大值发现没有交换就会结束算法,所以最好时间复杂度为O(n)。最差情况也就是根上面一样了O(n2)
稳定性:稳定。
可以使用在链表上。

这个算法应该是排序算法最捞的一个,因为判断次数基本拉满,交换次数应该也是所有算法里面最多的,而且是两两交换,还不是插入排序中的替换。除了能够实现排序这个算法一无是处。


void bubbleSort(int a[], int length){
	for (int i = 0; i < length ; i ++){
		for (int j = length-1 ; j > i ; j ++){
			if (a[j-1] > a[j]){
				int tem = a[j];
				a[j] = a[j+1];
				a[j+1] = tem;
			}
		}
	}
	return;
}

快速排序

快速排序如其名,从平均看来算法效率是所有排序算法最高的。同时算法可递归,更近一步能够使用并行优化。

算法思想:与冒泡找最大不同,快排思想是随便来一个数字,确定他在最终排序完成序列的位置。只需要找到一个位置,让他左边的数字都比他小,右边的数字都比他大,那么他在最终排序结果肯定也是这个位置。那么找到这个最终位置,左边根右边还是无序的,用这种思想对于左边右边再进行相同操作,一直递归下去进行就可以。

算法过程:选取最做边的数字作为需要排序的数字,然后进行一次排序,能够获得左右两个未被排序的序列,然后对左右序列一直递归排序即可。递归跳出的条件就是排序区间需要存在。

一次排序的算法思想:
现将需要排序的数存在变量var里
控制两个指针变量初始化为一个左端点low一个右端点high。high先从右边往左走,一旦发现第一个比var小的就停止,然后将其的值赋值给low所在的结点。然后low开始向右走,一旦发现比var大的就停下,然后将其值赋值给high所在的结点。两个交替循环直到low,high指向同一个位置,这个位置就是var的最终位置。

总体排序过程可以使用递归,每一次排序可以视为新开了两个左右子树,然后一直下去。
空间复杂度:取决于递归树的高度。
时间复杂度:最好的情况就是树的高度最矮,也就是需要排序的树每一次都被放在的序列的中点,此时时间复杂度为O(logn)。最差的情况就是序列已经有序,时间复杂度在O(n2). 由于已经有序情况较少,所以总体平均快排时间复杂度在O(nlogn)。同时也是算法最好的一个。
稳定性:不稳定

时间跟空间复杂度都要看树的高度,递归可以看作二叉树,二叉树高度范围在logn以及n2的范围上。所以时间空间复杂度也在这两个量级上。

int sortUnit(int a[], int low, int high){
	int tem;
	while (low<high){
		tem = a[low];
		while (low < high && a[high]>= tem)
			high--;
		a[low] = a[high];
		while (low < high && a[low] <= tem)
			low++;
		a[high] = a[low];
	}
	a[low] = tem;
	return low;
}

void quickSort(int a[], int low, int high){
	if(low >= high) return;
	int pos = sortUnit(a, 0, length-1);
	quickSort(a, 0, pos-1);
	quickSort(a, pos+1, length-1);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值