菜鸟笔记之数据结构(6)


声明:以下都是学的尚硅谷网课所记的笔记。可能会有一些错误,发现了会修改。

冒泡排序

介绍: 对待排序序列从前往后(从下标较小的元素开始),依次比较相邻元素的值,如果逆序则交换,使得值较大的元素逐渐从前往后移,就像水泡一样逐渐往上冒。

规则:

  • 一共需要进行数组的大小减1次循环;
  • 每一趟需要排序的次数逐渐减小;
  • 优化: 如果发现某趟排序中,没有发生一次交换,则可以提前结束冒泡排序。
//只列出关键代码
int temp = 0;
boolean flag = false;
for(int i = 0; i < arr.length-1; i++){
	for(int j = 0; j < arr.length-1-i; j++){
		if(arr[j] > arr[i]){
			flag == true;
			temp = arr[j];
			arr[j] = arr[j+1];
			arr[j+1] = temp;
		}
	}
	if(flag == false){ //一次也没有交换过,则可以提前结束
		break;
	} else{
		flag = false; //重置标识为了进行下次判断
	}
}

平均时间复杂度为O(n2),没优化前,80000个随机数据排序大概20s左右。

选择排序

介绍:从待排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。 例如从数据中选出最小的值arr[0],然后选出次最小的放在arr[1],依次排序直到完成排序。

规则:

  • 共有数组大小减1轮排序。
  • 先假定当前这个为最小数,然后和后面每个进行比较,有更小的则交换。
public static void selectSort(int[] arr){
	for(int i = 0; i < arr.length-1; i++){
		int minIndex = i;
		int min = arr[i];
		for(int j = i + 1; j < arr.length; j++){
			if(min > [j]){
				min = arr[j];
				minIndex = j;
			}
		}
		if(minIndex != i){
			arr[minIndex] = arr[i];
			arr[i] = min;
		}
	}
}

平均时间复杂度为O(n2),80000个随机数据排序用了大概3s左右。比冒泡快,因为冒泡排序需要交换很多次,费时。

插入排序

介绍:对待排序的元素以插入的方式找到该元素的适当位置,达到排序的目的。 用一个有序表和一个无序表,开始有序表中只有一个元素,无序表中有n-1个元素,每次从无序表中取第一个元素,将其值依次与有序表的值进行比较,将其插入到有序表中适当位置,完成排序。

代码参考这篇文章 插入排序–直接插入排序

平均时间复杂度为O(n2),80000个随机数据排序大概5s左右。

希尔排序

介绍: 也是一种插入排序,是一种改进版本,也称为缩小增量排序。把记录按下标的一定增量分组,对每组使用直接插入排序算法,随着增量逐渐减小,每组包含的数据越来越多,当增量减少至1时,整个数据集被分成一组,算法便终止。

原理参考这篇文章 希尔排序

步骤:

  1. 先把数据分为gap = length / 2组,gap为增量,然后对这n组数据分别进行插入排序;
  2. 再将数据分为gap1 = gap / 2组,再进行直接插入排序,直到最后只剩一组,进行最后一次插入排序。
public static void shellSort(int[] arr){
	int temp = 0;
	for(int gap = arr.length/2; gap>0; gap/=2){//gap为步长
		for(int i=gap; i<arr.length; i++){
			for(int j=i-gap; j>=0; j-=gap){
				if(arr[j]>arr[j+gap]){
					//插入时使用交换法
					temp = arr[j];
					arr[j] = arr[j+gap];
					arr[j+gap] = temp;
				}
			}
		}
	}
}

平均时间复杂度为O(nlogn),80000个随机数据排序大概15s左右。可以在交换时用移位法优化。

改进版本:

public static void shellSort2(int[] arr){
	//增量gap,并逐步缩小增量
	for(int gap = arr.length/2; gap > 0; gap/=2){
		//从第gap个元素,逐个对其所在组进行插入排序
		for(int i = gap; i < arr.length; i++){
			int j = i;
			int temp = arr[j];
			if(arr[j] < arr[j-gap]){
				while(j-gap >=0 && temp < arr[j-gap]){
					//移动
					arr[j] = arr[j-gap];
					j -= gap;
				}
				arr[j] = temp;
			}
		}
	}
}

80000个随机数据花了1s左右。

快速排序

**介绍:**对冒泡排序的一种改进,通过一趟排序将要排序的数据分割成两部分,其中一部分所有数据比另一部分的所有数据都要小,然后再对这两部分分别进行快排,整个排序过程可以递归。平均时间复杂度为O(nlogn),80000个数据需要1s不到,优于希尔排序。

具体参考博文 快速排序

归并排序

介绍: 是一种稳定排序。利用归并思想实现的排序方法,采用经典的分治策略(将问题分成一些小问题然后递归求解,而治的阶段则将分的阶段得到的各答案“修补”在一起,分而治之)。归并排序需要额外的空间,以空间换时间,提高了排序效率。平均时间复杂度为O(nlogn),80000个数据需要1s不到,8000000个数据3s左右,跟快排差不多。

具体参考 MergeSort(归并排序)算法Java实现

基数排序

介绍: 分配式排序,又称“桶子法”,通过数值的各个的值,从低位开始将待排序的数按照这一位的值放到相应的编号为0~9的桶中,达到排序的目的。是一种稳定性排序,效率高,是桶排序的扩展,也是以空间换时间。

具体参考 基数排序详解以及java实现

思想: 将所有待比较数值统一为同样位数长度,数位较短的前面补0。然后从最低位开始依次进行一次排序。这样从最低位开始一直到最高位排序完,就完成了排序。

例:arr ={53, 3, 542, 748, 14, 214} (第一轮排序比较个位) -> {542, 53, 3, 14, 214, 748} (第二轮排序比较十位) -> {3, 14, 214, 542, 748, 53} (第三轮排序比较百位) -> {3, 14, 53, 214, 542, 748}.

注:进行多少轮排序取决于最高位数是多少位,上例最高位为千位,即3位,所以进行了3轮排序。如果有负数的数组,则不能用基数排序来做。

平均时间复杂度为O(n×k)。80000个数据1s不到,8000000个数据1s左右,比快排、归并快,且稳定。当运行80000000个数据时,占用内存太大,导致出现异常(OutOfMemoryError),缺点就是数据越多越占用空间。

堆排序

介绍:

  • 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,平均时间复杂度为O(nlogn),是一种不稳定的排序。
  • 堆是具有以下性质的完全二叉树:每个节点值都大于或等于其左右子节点的值,称为大顶堆。其中左右子节点的值的大小关系没有要求。
  • 每个节点值都小于或者等于其子节点值,称为小顶堆。一般升序采用大顶堆,降序采用小顶堆。

大顶堆
特点: arr[i] >= arr[2i+1], arr[i] >= arr[2i+2] ,i从0开始。80000个随机数据排序用了1s不到,8000000个数据大概2s左右。

注: 堆排序实际上没有创建树,而是用数组下标来表示的。

思想:

  1. 将待排序的序列构造成一个大顶堆;
  2. 整个序列的最大值就是堆顶的根节点;
  3. 将其与末尾交换,此时末尾就为最大值;
  4. 然后将剩余n-1个元素重新构造成一个大顶堆,这样就可以得到次大值,如此反复,就可以完成排序。

具体参考 Java实现堆排序和图解

总结:常用排序算法对比

常用排序算法比较
名词解释:

  • 稳定性:a原本在b前面,且a=b,排序后a仍然在b前面,就称该算法具有稳定性。
  • 排序方式分为内排序和外排序。内排序:所有操作在内存中完成。外排序:由于数据过大,把数据放在磁盘中,通过磁盘和内存的数据传输才能进行排序。
  • 时间复杂度:一个算法所耗费的时间。
  • 空间复杂度:运行一个程序所需要的内存大小。
  • n:数据规模。
  • k:桶的个数。
  • In-place:不占额外内存。
  • Out-place:占额外内存。

---------------------------- 个人学习笔记----------------------------

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值