算法:选择排序+堆排序
选择排序
基于"选择"的排序
思路
把整个序列分为 有序区 和 无序区。
初始时,有序区为空,无序区是整个数组。
每一趟从 无序区 里选择一个最小元素,放到无序区的第一个位置。
这样,有序区的长度 +1,无序区的长度 -1。
重复这个过程,直到所有元素都进入有序区(只需要执行 n-1 趟)。
举一个例子:
数组:[64, 25, 12, 22, 11]
第1趟:最小数是 11 → 与第1个元素交换 → [11, 25, 12, 22, 64]
第2趟:最小数是 12 → 与第2个元素交换 → [11, 12, 25, 22, 64]
第3趟:最小数是 22 → 与第3个元素交换 → [11, 12, 22, 25, 64]
第4趟:最小数是 25 → 与第4个元素交换 → [11, 12, 22, 25, 64]
排序完成
代码
代码也很好写
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <unordered_map>
#include <limits.h>
#include <queue>
#include <string.h>
#include <stack>
using namespace std;
void select_sort(vector<int>& arr)
{for(int i=0;i<arr.size()-1;i++){int minn=arr[i];int minindex=i;for(int j=i+1;j<arr.size();j++){if(minn>arr[j]){minn=arr[j];minindex=j;}}swap(arr[i],arr[minindex]); }
}
int main()
{//ios::sync_with_stdio(0),cin.tie(0),cout.tie(vector<int> arr = {64, 25, 12, 22, 11};select_sort(arr); for(int i=0;i<arr.size();i++){cout<<arr[i]<<" ";}return 0;}
时间复杂度
O(n^2)
稳定性
选择排序是不稳定的
例如数组 [5(前), 5(后), 3]
第一趟选择最小值 3,与第一个 5 交换 → [3, 5(后), 5(前)]
两个相同元素的相对顺序发生了改变。
原因:交换操作可能会把后面的相等元素提前。
就地性
选择排序:是就地排序,因为只需要少量辅助变量 minindex 和循环下标。
内部排序
因为所有待排序记录可以⼀次载⼊内存时,因此它是内部排序。
优化
我们知道选择排序的时间复杂度为O(n^2)时间复杂度是很大的,那么我们有没有什么办法可以把它降低一些呢?
选择排序中需要跑n-1趟是无法优化的,能优化的就是在找最小值这里,我们原来是通过遍历来找最小值,现在我们可以借助堆来优化使得时间复杂度从O(n)降低到O(logn).
那么什么是堆呢?
这里用一篇文章讲清堆:
数据结构:堆
由此引出堆排序。
堆排序
堆排序我看发现有两种方法,一种是用小顶堆输出堆顶,再重复删除堆顶,直到堆为空,但这样改变了原本数组的元素位置,虽然输出的是堆排序,但原数组并不是按堆排序存放,这是一种缺陷
它的时间复杂度为建立小顶堆的O(n)*删除堆顶元素的O(logn)
总共为O(nlogn)
另一种是建立大顶堆,再交换堆顶到末尾,然后缩小堆,调整堆,这种方法保留排序的过程,并且是原地排序,排序后的数组也是有序的,感觉这种方法是最合适的。它的时间复杂度同样是O(nlogn)
因此我们这里就采用大顶堆+交换堆顶的方法来实现堆排序。
完整代码如下:
#include <iostream>
#include <vector>
using namespace std;// 下沉操作(维护大顶堆)
void heapify(vector<int>& arr, int n, int i) {int largest = i; // 假设父节点最大int left = 2 * i + 1; // 左孩子int right = 2 * i + 2; // 右孩子if (left < n && arr[left] > arr[largest])largest = left;if (right < n && arr[right] > arr[largest])largest = right;if (largest != i) {swap(arr[i], arr[largest]);heapify(arr, n, largest); // 递归下沉}
}// 堆排序
void heapSort(vector<int>& arr) {int n = arr.size();// 1. 建立大顶堆for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}// 2. 交换堆顶与末尾元素,然后调整堆for (int i = n - 1; i >= 1; i--) {swap(arr[0], arr[i]); // 堆顶最大值放到末尾heapify(arr, i, 0); // 重新调整堆,范围缩小}
}int main() {vector<int> arr = {12, 11, 13, 5, 6, 7};cout << "排序前: ";for (int x : arr) cout << x << " ";cout << endl;heapSort(arr);cout << "升序排序后: ";for (int x : arr) cout << x << " ";cout << endl;return 0;
}
堆排序相对交换排序的时间复杂度下降到O(nlogn)
是一种进步,它是就地排序,因为它没有借助其他数组,它不稳定因为它在排序的过程中会改变数组的相对位置。
举例:
数组:[4a, 5, 3, 4b, 1](4a 和 4b 值相同,但是不同元素)
建大顶堆 → 堆顶 5
交换堆顶 5 和末尾 1 → [1,4a,3,4b,5]
堆化 → [4a,1,3,4b,5]
继续交换堆顶 4a 和末尾 4b → [4b,1,3,4a,5]
发现 4a 和 4b 的相对顺序被改变了 → 不稳定
总结:堆排序
时间复杂度:O(n log n)
空间复杂度:O(1)(原地)
稳定性:不稳定