说明:下述内容除了少许图片选取自他人网站,其余均为本人独创。
0、排序算法导论
排序的概念
数据结构中的一个重点概念就是内部排序,内部排序是指待排序列完全存放在内存中所进行的排序过程,适合不太大的元素序列。其功能是对一个数据元素集合或序列重新排列成一个按数据元素某个相知有序(递增,递减)的序列。
排序的分类
- 插入类排序:直接插入排序、希尔排序。
- 交换类排序:冒泡排序、快速排序。
- 选择排序:简单选择排序、堆排序。
- 归并排序。
- 基数排序。
- 桶排序。
- 计数排序。
术语说明
- 稳定的排序: 排序过程中有前后两个元素相同的点,待排序结束后,这两个相同的点的相对前后位置不发生变化。
- 不稳定的排序: 排序过程中有前后两个元素相同的点,待排序结束后,这两个相同的点的相对前后位置发生了变化。
- 时间复杂度: 一个算法执行所耗费的时间。
- 空间复杂度:执行完一个程序所需内存的大小。
算法种类、时间复杂度、空间复杂度、是否稳定
out-place:占用额外内存。
k:桶的个数。
算法分类
注意:本博客涉及到的所有排序算法均是升序排序!
1、直接插入排序
算法描述
直接插入排序的过程是最开始固定头一个元素,然后在第二个元素开始,从前面已经排好序的序列中,选择一个合适的位置,将待插入元素插入到前面排好序的序列中。方法是从后向前的扫描序列,每扫描一次元素向后移动一个位置。
代码实现
class Solution {
public:
vector<int> sortArray(vector<int>& nums) {
int i = 1, j = 0;
for(; i < nums.size(); i++){
int tmp = nums[i];
for(j = i; j > 0; j--){
if(tmp < nums[j - 1])
nums[j] = nums[j - 1];
else
break;
}
nums[j] = tmp; // 该赋值语句要写在for循环的外面,否则当待排序元素需要排在第0个位置时,无法正确处理。
}
return nums;
}
};
运行截图
2、希尔排序
算法描述
希尔排序也称为缩小增量排序,是直接插入类排序的改进方法,时间复杂度在 o ( n log n ) \text o(\text n\log{\text n}) o(nlogn)。算法思想是每次选一定的增量为一个分组,在该分组中进行直接插入排序。下一轮减小增量,再使用相同的方法, 直到增量为1为止,对全体元素执行整体的直接插入排序算法。
严蔚敏的数据结构课本中的希尔排序法的增量为:5, 3, 1,这也是一般的计算机考研中数据结构题目中的希尔排序增量。
下面的代码中的增量是数组长的 1 2 \frac{1}{2} 21,随后依次2倍递减。
代码实现
class Solution {
public:
vector<int> sortArray(vector<int>& nums) {
int len = nums.size(), tmp = 0;
int gap = len / 2;
for(; gap; gap /= 2){
for(int i = gap; i < len; i++){
tmp = nums[i];
int preIndex = i - gap;
while(preIndex >= 0 and nums[preIndex] > tmp){
nums[preIndex + gap] = nums[preIndex];
preIndex -= gap;
}
nums[preIndex + gap] = tmp;
}
}
return nums;
}
};
运行截图
3、冒泡排序
算法描述
冒泡排序的过程是从左至右依次比较两个相邻的元素,若前面的元素大于后面的元素,则交换两个元素,否则不执行操作。待指针从左至右遍历结束后,数组中的最大值便被交换到了最右边,依稀类推,直到在某一轮遍历的过程中没有元素发生交换为止。
由于整个过程就像冒泡一样,所以名为冒泡排序。
代码实现
class Solution {
public:
vector<int> sortArray(vector<int>& nums) {
int i = nums.size() - 1, j = 0;
for(; i > 0; i--){
bool flag = true; // 设置flag是冒泡排序的优化算法
for(j = 0; j < i; j++){
if(nums[j] > nums[j + 1]){
swap(nums[j], nums[j + 1]);
flag = false;
}
}
if(flag)
break;
}
return nums;
}
};
运行截图
4、快速排序
算法描述
快速排序也是交换类排序的一种。思想是通过设置一个点为枢轴(一般为数组中的第一个元素),通过一趟排序之后,枢轴位于序列的中部位置,要求枢轴的左边元素均小于枢轴,枢轴的右边元素均大于枢轴。再分别递归对枢轴左边的元素和枢轴右边的元素执行快速排序直到数列整体有序为止。
具体实现:设置指针指向序列的最右边元素,依次从右向左遍历数组,找到第一个不大于枢轴的元素,将枢轴的值与该值交换,然后再从左向右依次遍历数组找到第一个不小于枢轴的值,将枢轴的值与该值交换,指针再指向右边没有遍历的位置开始遍历,方法与上述相同,以此类推,直到枢轴处于数组的中部无可交换的元素为止。第一轮快速排序结束,再分