[第六章] 排序

注:此专栏内容主要参考极客时间-数据结构与算法之美

1. 常用排序法

  • 冒泡排序、插入排序、选择排序、归并排序、快速排序、计数排序、基数排序、桶排序
  • 基于比较的排序算法的执行过程,会涉及两种操作,一种是元素比较大小,另一种是元素交换或移动
  • 原地排序(Sorted in place):原地排序算法,就是特指空间复杂度是 O(1) 的排序算法。
  • 稳定性:如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变。通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。如果Ai = Aj,Ai原来在位置前,排序后Ai还是要在Aj位置前。
  • 稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用;
    [第六章] 排序_第1张图片

2.冒泡排序

  • 空间复杂度为O(1),是一个原地排序算法。
  • 相同大小的数据在排序前后不会改变顺序,所以冒泡排序是稳定的排序算法。
  • 最好情况时间复杂度:O(n)、最坏情况时间复杂度:O(n2)
 // 冒泡排序,a 表示数组,n 表示数组大小
 public void bubbleSort(int[] a, int n) {
 	if (n <= 1) return;
 	for (int i = 0; i < n; ++i) {
  		// 提前退出冒泡循环的标志位
	 	boolean flag = false;
	 	for (int j = 0; j < n - i - 1; ++j) {
	 		if (a[j] > a[j+1]) { // 交换
	 		int tmp = a[j];
	 		a[j] = a[j+1];
	 		a[j+1] = tmp;
	 		flag = true; // 表示有数据交换
	 	}
 	}
 		if (!flag) break; // 没有数据交换,提前退出
 	}
 }

[第六章] 排序_第2张图片

  • 冒泡过程还可以优化。当某次冒泡操作已经没有数据交换时,说明已经达到
    完全有序,不用再继续执行后续的冒泡操作;
    [第六章] 排序_第3张图片

3.插入排序

  • 将数组中的数据分为两个区间,已排序区间和未排序区间;
  • 初始,已排序区间只有一个元素,就是数组的第一个元素。
  • 插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。
  • 插入排序是原地排序法,插入排序是稳定排序算法;
  • 最好时间复杂度O(n)、最坏时间复杂度O(n2)、平均复杂度O(n2)
  • 从代码实现上来看,冒泡排序的数据交换要比插入排序的数据移动要复杂,冒泡排序
    需要 3 个赋值操作,而插入排序只需要 1 个
// 插入排序,a 表示数组,n 表示数组大小
public void insertionSort(int[] a, int n) {
	if (n <= 1) return;
	for (int i = 1; i < n; ++i) {
		int value = a[i];
		int j = i - 1;
		// 查找插入的位置
		for (; j >= 0; --j) {
				if (a[j] > value) {
				a[j+1] = a[j]; // 数据移动
 			} else {
 				break;
 		}
 	}
 		a[j+1] = value; // 插入数据
 	}
 }

[第六章] 排序_第4张图片

4.选择排序

  • 选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间,但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。
  • 选择排序空间复杂度为 O(1),是一种原地排序算法。
  • 选择排序是一种不稳定的排序算法。举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。因此,相对于冒泡排序和插入排序,选择排序就稍微逊色了。
  • 选择排序的最好情况时间复杂度、最坏情况和平均情况时间复杂度都为 O(n2)。
    [第六章] 排序_第5张图片

5.归并排序

  • 如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起;
    [第六章] 排序_第6张图片

你可能感兴趣的:([第六章] 排序)