总体概述
一:复杂度与稳定性
1、稳定排序
稳定:如果
a
原本在
b
前面,而
a=b
,排序之后
a
仍然在
b
的前面。
不稳定:如果
a
原本在
b
的前面,而
a=b
,排序之后
a
可能会出现在
b
的后面。
冒泡排序、归并排序
、插入排序、记数排序、桶排序、基数排序
是稳定排序;
快速排序
、希尔排序、选择排序、堆排序
是不稳定排序。
2、最坏情况下时间复杂度最小的排序算法?
① 归并排序
② 堆排序
最坏时间复杂度都为
O(nlogn)
3、XXXX与初始序列无关的排序有哪些?
(1
)
移动次数
与初始序列无关的是:归并排序、基数排序
分析:因为归并和基数都是非原地的,都得移动。
选择排序(有关)——特例:全部排好,无需移动
插入排序(有关)——就像抓牌,如果全部排好,每次放最右边就行;如果没有排好,每次
要移动一些牌再插入。注:折半插入(即采用二分法查找插入位置)可以减少比较次数
。
(2
)
比较次数
与初始序列无关是:选择排序、基数排序
分析:
选择排序(无关)——哪怕全部排好,也要比较(n-1)!次,选出最值并(交换后)固定
基数排序(无关)——没有相互比较的过程,无关
堆排序(有关)——比较次数和下沉深度相关。虽然一个元素是从底部拉上来的,但不代表
这个元素一定会接着沉到底部,如果沉到中间就停止下沉的话,比较次数就少了。
(3
)
时间复杂度
与初始序列无关的是:选择排序、基数排序、归并排序、堆排序
分析:平均、最好、最坏时间复杂度相同 -> 与初始序列无关
(4
)
排序趟数
与初始序列无关的是:插入排序、选择排序、基数排序
分析:除了两个例外:快排、优泡
① 快速排序中,排序趟数(即递归深度)与关键字选择(即初始状态)有关;
② 优化后的冒泡排序中,排序趟数与初始状态有关;
二、手撕
1、冒泡排序
/*
* 冒泡排序
* 比较交换相邻元素
* 思路简单,仅学习用
*/
class Bubble
{
public:
void mysort(vector<int> &nums)
{
int n = nums.size();
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (nums[j] > nums[j + 1])
{
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}
};
int main()
{
vector<int> nums = {9, 7, 5, 3, 1, 0, 8, 4, 6, 2};
Bubble bubble;
bubble.mysort(nums);
for (int num : nums)
{
cout << num << " ";
}
return 0;
}