常见内部排序方法的总结
| 排序方法 | 时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 稳定性 |
|---|---|---|---|---|---|
| 插入排序 | O(n2) | O(n2) | O(n) | O(1) | 稳定 |
| 希尔排序 | O(nlog2n) | O(n2) | O(n) | O(1) | 不稳定 |
| 选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 不稳定 |
| 堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | 不稳定 |
| 冒泡排序 | O(n2) | O(n2) | O(n) | O(1) | 稳定 |
| 快速排序 | O(nlog2n) | O(n2) | O(nlog2n) | O(nlog2n) | 不稳定 |
| 归并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 稳定 |
1. 插入排序
基本思想
在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。
代码实现
直接插入排序
寻找插入位置时使用从前往后直接查找
void InsertSort(vector<int>& v) {
int len = v.size(),cur_num = 0;
for (int i = 1; i < len; i++)
{
if (v[i] > v[i - 1])
{
continue;
}
cur_num = v[i];
// 找到插入位置
int j = 0;
while (j < i)
{
if (cur_num <= v[j])
{
break;
}
j++;
}
// 挪动插入位置的元素
for (int k = i; k > j; k--)
{
v[k] = v[k - 1];
}
// 插入元素
v[j] = cur_num;
}
return;
}
折半插入排序
寻找插入位置时使用二分查找
void BinaryInsertSort(vector<int>& v)
{
int len = v.size(), cur_num = 0;
for (int i = 1; i < len; i++)
{
if (v[i] > v[i - 1])
{
continue;
}
cur_num = v[i];
// 找到插入位置(由于前面元素已排好序,二分查找)
int l = 0, r = i - 1, mid;
while (l < r)
{
mid = (r - l) / 2 + l;
if (cur_num < v[mid])
{
r = mid;
}
else if (cur_num > v[mid])
{
l = mid + 1;
}
else
{
r = mid;
break;
}
}
// 挪动插入位置的元素
for (int k = i; k > r; k--)
{
v[k] = v[k - 1];
}
// 插入元素
v[r] = cur_num;
}
return;
}
2. 希尔排序
基本思想
在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序。
然后逐渐将增量减小,并重复上述过程。直至增量为1,此时数据序列基本有序,最后进行插入排序。

代码实现
void ShellSort(vector<int>& v) {
int len = v.size();
for (int dk = len / 2; dk >= 1; dk /= 2) {
// dk为步长(增量)
for (int i = dk; i < len; i++)
{
// 对以dk为步长的组进行插入排序
if (v[i] < v[i - dk])
{
int temp = v[i];
// 寻找插入位置,并后移元素
int j;
for (j = i - dk; j >= 0 && v[j] > temp; j -= dk) {
v[j + dk] = v[j];
}
// 插入元素
v[j + dk] = temp;
}
}
}
return;
}
3. 选择排序
基本思想
在长度为N的无序数组中,第一次遍历n-1个数,找到最小的数值与第一个元素交换;
第二次遍历n-2个数,找到最小的数值与第二个元素交换;
…
第n-1次遍历,找到最小的数值与第n-1个元素交换,排序完成
代码实现
void SelectSort(vector<int>& v) {
int len = v.size();
for (int i = 0; i < len; i++)
{
// min记录目前最小元素的位置
int min = i;
for (int j = i + 1; j < len; j++)
{
if (v[j] < v[min])
{
min = j;
}
}
// 将目前最小元素放在最终的位置
if(i != min)
{
swap(v[i], v[min]);
}
}
return;
}
4. 堆排序
基本思想
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点,将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

堆节点调整:将堆的末端子节点作调整,使得子节点永远小于父节点
创建按最大堆:从最后一个非叶子结点开始(叶结点自然不用调整),从后往前进行调整。
堆排序:移除位在第一个数据的根节点,并做最大堆调整的递归运算

代码实现
void HeapSort(vector<int>& v) {
int len = v.size();
/*
* 使用STL库的大根堆实现
priority_queue<int> q(v.begin(),v.end());
for (int i = len - 1; i >= 0; i--)
{
v[i] = q.top();
q.pop();
}
*/
// 利用自建的堆进行排序
BuildMaxHeap(v);
for (int i = len - 1; i > 0; i--)
{
swap(v[i], v[0]);
AdjustHeap(v, i, 0);
}
return;
}
void BuildMaxHeap(vector<int>& v) {
int len = v.size();
for (int i = len / 2; i >= 0; i--)
{
// 从堆节点最后一个非叶子节点开始调整
AdjustHeap(v, len, i);
}
}
void AdjustHeap(vector<int>& v, int end, int pos) {
// 将元素pos为根的子树进行调整
int temp = v[pos];
for (int i = 2 * pos; i < end; i *= 2) // 沿着key较大的子节点向下筛选
{
// 让i为key较大的子节点下标
if (i < end - 1 && v[i] < v[i + 1])
{
i++;
}
if (temp >= v[i])
{
// 筛选结束
break;
}
else
{
// 将v[i]调整到双亲节点上
v[pos] = v[i];
// 修改pos值继续向下筛选
pos = i;
}
}
// 将元素pos放在最终位置
v[pos] = temp;
return;
}
5. 冒泡排序
基本思想
从后向前比较相邻的两个数据,如果第二个数小,就交换位置,最终最小数被交换到起始的位置,这样第一个最小数的位置就排好了。
重复上述过程即可。
代码实现
void BubbleSort(vector<int>& v) {
int len = v.size();
for (int i = 0; i < len; i++)
{
bool flag = true; // 标志一趟冒泡是否发生交换
for (int j = len - 1; j > i; j--)
{
// 一趟冒泡过程
if (v[j] < v[j-1])
{
swap(v[j], v[j-1]);
flag = false;
}
}
// 未发生交换则表示已经有序
if (flag)
{
return;
}
}
return;
}
6. 快速排序
基本思想
分治:先从数列中取出一个数作为枢轴值,将比这个数小的数全部放在它的左边,大于或等于它的数全部放在它的右边,最终这个数被放在最终位置。对左右两个小数列重复第二步,直至各区间只有1个数。
代码实现
void QuickSort(vector<int>& v, int low, int high) {
if (low < high)
{
// 划分子数组
int pivotpos = Partition(v, low, high);
// 依次递归对子数组进行快排
QuickSort(v, low, pivotpos - 1);
QuickSort(v, pivotpos + 1, high);
}
return;
}
int Partition(vector<int>& v, int low, int high) {
// 以当前第一个元素作为枢轴元素pivot,对数组进行划分
int pivot = v[low];
while (low < high)
{
// 从右往左找到比枢轴元素小的元素
while (low < high && v[high] >= pivot)
{
high--;
}
v[low] = v[high];
// 从左往右找到比枢轴元素大的元素
while (low < high && v[low] <= pivot)
{
low++;
}
v[high] = v[low];
}
// 将枢轴元素pivot放在最终位置
v[low] = pivot;
return low;
}
7. 归并排序
基本思想
分治:将数组分成2组A,B,如果这2组组内的数据都是有序的,那么就可以很方便的将这2组数据进行排序。如何让这2组组内数据有序了?可以将A,B组各自再分成2组。依次类推,当分出来的小组只有1个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的2个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。
代码实现
void MergeSort(vector<int>& v, int start, int end) {
// 先分解数组进行归并排序,再合并数组
if (end - start > 1)
{
int mid = (end - start) / 2 + start;
MergeSort(v, start, mid);
MergeSort(v, mid, end);
Merge(v, start, mid, end);
}
return;
}
void Merge(vector<int>& v, int start, int mid, int end) {
// 先创建辅助数组,将待排序的数组全放进辅助数组中
vector<int> temp;
for (int i = start; i < end; i++)
{
temp.push_back(v[i]);
}
// 合并有序数组
int cur = start, i = 0, j = mid - start;
mid -= start;
end -= start;
while ( i < mid && j < end)
{
if (temp[i] < temp[j]) {
v[cur++] = temp[i++];
}
else {
v[cur++] = temp[j++];
}
}
// 其中一个数组比较完了,将剩下的数组元素全放进v中
while (i < mid)
{
v[cur++] = temp[i++];
}
while (j < end)
{
v[cur++] = temp[j++];
}
return;
}

7184

被折叠的 条评论
为什么被折叠?



