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

选择排序

算法描述

选择排序
图片来源: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的相对位置有所改变。

冒泡排序

算法描述

在这里插入图片描述
图片来源: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)。
  • 稳定性:
    稳定。

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

算法描述
在这里插入图片描述
图片来源:https://blog.csdn.net/qq_41855420/article/details/93653175

在这里插入图片描述
图片来源: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)。
  • 稳定性
    不稳定。
    由于分组的缘故,等值的元素在排序前后相对位置可能发生改变。

归并排序

算法描述

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

快速排序

算法描述

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

堆排序

算法描述

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

桶排序

算法描述

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

基数排序

算法描述

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

计数排序

算法描述

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值