排序算法总结(选择、冒泡、插入、希尔、归并、快排、堆排序、桶排序、基数排序、计数排序)

排序算法总结(选择、冒泡、插入、希尔、归并、快排、堆排序、桶排序、基数排序、计数排序)

  • 选择排序
  • 冒泡排序
  • 插入排序
    • 直接插入排序
    • 希尔排序(改进的插入排序)
  • 归并排序
  • 快速排序
  • 堆排序
  • 桶排序
  • 基数排序
  • 计数排序
  • 算法性能比较(时间复杂度、空间复杂度及稳定性)

选择排序

算法描述

排序算法总结(选择、冒泡、插入、希尔、归并、快排、堆排序、桶排序、基数排序、计数排序)_第1张图片
图片来源:https://github.com/damonare/Sorts

  1. 在一个长度为N的无需数组中,将第1个数的下标设为min。遍历后面N-1个数与min比较,若比min小将其下标重新设为min。遍历结束后将下标为min的数与第一个数交换。
  2. 将第2个数的下标设为min。遍历后面N-2个数。遍历结束后将下标为min的数与第二个数交换。
  3. 重复以上操作直至遍历N-1次,将下标为min的数与第N-1个数交换。排序完成。

代码实现

class Solution {
public:
	vector<int> selectSort(vector<int>& nums) {
		for(int i = 0; i < nums.size() - 1; i++) {
			int min = i;
			for(int j = i + 1; i < nums.size(); j++) {
				if(nums[j] < nums[min]) {
					min = j;
				}
			}
			if(min != i) {
				swap(nums[i], nums[min]);
			}
		}
		return nums;
	}
};

时间复杂度、空间复杂度及稳定性

  • 时间复杂度:
    在任何情况下,选择排序算法都需要遍历 N-1 次,每次遍历需比较的次数分别是 N,N-1,N-2,……,1。在最好的情况下,即数组完全有序时,不需要做任何交换,需要进行 N(N + 1)/2 次操作。在最坏的情况下,即数组逆序时,需要交换次数为N-1次,需要进行 N(N + 1)/2+ N - 1 次操作。
    综上所述,无论是最好的情况还是最差的情况,选择排序的时间复杂度都为O(n2)。
  • 空间复杂度:
    O(1)。
  • 稳定性:
    不稳定。
    举例:对数组[5,9,5,1,3]进行选择排序,第一次遍历交换1与第一个5的位置,则排序完成后两个5的相对位置有所改变。

冒泡排序

算法描述

排序算法总结(选择、冒泡、插入、希尔、归并、快排、堆排序、桶排序、基数排序、计数排序)_第2张图片
图片来源:https://github.com/damonare/Sorts

  1. 对N个数两两进行比较,顺序相反则交换位置,一次遍历过后最小或最大的数将移至数组末尾。
  2. 对前N-1个数重复以上操作。
  3. 重复以上操作,N-1次遍历后排序完成。

代码实现

class Solution {
public:
	vector<int> bubbleSort(vector<int>& nums) {
		for(int i = 0; i < nums.size() - 1; i++) {
			bool flag = true;
			for(int j = 0; j < nums.size() - 1 - i; j++) {
				if(nums[j] > nums[j + 1] {
					swap(nums[j], nums[j + 1]);
					flag = false;
				}
			}
			if(flag) {
				break;
			}
		}
		return nums;
	}
};

时间复杂度、空间复杂度及稳定性

  • 时间复杂度:
    最好的情况下,即数组完全有序时,需进行一次遍历,比较 N-1 次,不进行交换。最坏的情况下,即数组逆序时,需进行 N-1 次遍历,每次遍历分别进行 N-1,N-2,……,1次交换,需要进行 N(N - 1)/2 次操作。
    综上所述,冒泡排序最好的情况下时间复杂度为O(n),最坏的情况下时间复杂度为O(n2),平均时间复杂度为O(n)与O(n2)取平均值,仍然是O(n2)。
  • 空间复杂度:
    O(1)。
  • 稳定性:
    稳定。

插入排序

直接插入排序

算法描述

图片来源:https://github.com/damonare/Sorts

  1. 假设前 i-1 个数已经排好顺序,将第 i 个数插入到前 i-1 个有序数列中,使得前 i 个数成为有序数列。
  2. 重复以上操作,总共遍历 n-1 次,排序完成。

代码实现

class Solution {
public:
	vector<int> insertionSort(vecotr<int>& nums) {
		for(int i = 1; i < nums.size(); i++) {
			int j = i;
			while(j > 0 && nums[j] < nums[j - 1]) {
				swap(nums[j], nums[j - 1]);
				j--;
			}
		}
		return nums;
	}
};

时间复杂度、空间复杂度及稳定性

  • 时间复杂度:
    最好的情况下,即数组完全有序时,需进行 N-1 次比较,不进行交换。最坏的情况下,即数组逆序,需要进行 N-1 次遍历,每次需要比较的次数分别为 1,2,……,N-1 次,每次比较都需要进行交换。
    综上所述,插入排序最好的情况下时间复杂度为O(n),最坏的情况下时间复杂度为O(n2),平均时间复杂度为O(n2)。
    在数组元素随机排列的情况下,插入排序要稍优于上面两种排序。
  • 空间复杂度:
    O(1)。
  • 稳定性:
    稳定。

希尔排序(改进的插入排序)

算法描述
排序算法总结(选择、冒泡、插入、希尔、归并、快排、堆排序、桶排序、基数排序、计数排序)_第3张图片
图片来源:https://blog.csdn.net/qq_41855420/article/details/93653175

排序算法总结(选择、冒泡、插入、希尔、归并、快排、堆排序、桶排序、基数排序、计数排序)_第4张图片
图片来源:https://www.runoob.com/w3cnote/sort-algorithm-summary.html

  1. 取一个小于n的整数作为增量(increment),通常第一个增量取 n / 2,按增量对数组元素进行分组,间隔为整数个增量的元素为一组。对每组进行直接插入排序。
  2. 取一个更小的增量,通常是前一个的 1/2,重复以上操作。
  3. 重复以上操作,直至增量减小至0。

代码实现

class Solution {
public:
	vector<int> shellSort(vector<int>& nums) {
		for(int increment = nums.size() / 2; increment > 0; increment /= 2) {
			for(int i = increment; i < nums.size(); i++) {
				int j = i;
				while(j >= increment && nums[j] < nums[j - increment]) {
					swap(nums[j], nums[j - increment]);
					j -= increment;
				}
			}
		}
		return nums;
	}
};

时间复杂度、空间复杂度及稳定性

  • 时间复杂度
    希尔排序的时间复杂度与增量的选取有关,以上代码中使用的增量(n / 2,n / 4,……)最好情况下时间复杂度为 O(n),最差情况下时间复杂度为 O(n2)。即便是这样,相对于直接插入排序,希尔排序的效率仍然是增加了。因为引入了按增量分组的策略,在增量较大时,可以使一个元素向最终位置移动一大步,随着增量减小,数组接近有序,当增量减为1时对数组进行微调即可得到有序数组,减少了数组中元素的移动总数。
    优化增量序列可以减少希尔排序的最坏时间复杂度,比如Hibbard提出的质数增量序列经证明可使得最坏时间复杂度减少为O(n2/3),Sedgewick也提出了一种增量序列可使得最坏时间复杂度减少至O(n1.3)。
  • 空间复杂度
    O(1)。
  • 稳定性
    不稳定。
    由于分组的缘故,等值的元素在排序前后相对位置可能发生改变。

归并排序

算法描述

代码实现
时间复杂度、空间复杂度及稳定性

快速排序

算法描述

代码实现
时间复杂度、空间复杂度及稳定性

堆排序

算法描述

代码实现
时间复杂度、空间复杂度及稳定性

桶排序

算法描述

代码实现
时间复杂度、空间复杂度及稳定性

基数排序

算法描述

代码实现
时间复杂度、空间复杂度及稳定性

计数排序

算法描述

代码实现
时间复杂度、空间复杂度及稳定性

算法性能比较(时间复杂度、空间复杂度及稳定性)

你可能感兴趣的:(排序算法,排序算法,算法)