1、堆结构
优先队列结构就是堆结构
将一个数组看作一个完全二叉树
一个节点的下标为i
,则它的左子树为2 * i + 1
,右子树为2 * i + 2
,父节点为(i - 1)/ 2
2、堆排序
主要靠两个函数:
heapInsert
(自下而上)heapify
(自上而下)
#include<stdio.h>
int arr[5] = {4, 5, 2, 3, 1};
void swap(int arr[], int a, int b){
if(a == b){
return;
}
arr[a] = arr[a] ^ arr[b];
arr[b] = arr[a] ^ arr[b];
arr[a] = arr[a] ^ arr[b];
}
void heapInsert(int arr[], int index){
//这里(index-1)/2 不能使用 index - 1 >> 2
// -1 >> 2 = -1
while(arr[index] > arr[(index - 1) / 2]){
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
void heapify(int arr[], int index, int heapsize){
int left = index * 2 + 1;
while(left < heapsize){
int largest = arr[left] > arr[index] ? left : index;
largest = left + 1 < heapsize && arr[left + 1] > arr[largest] ? left + 1 : largest;
if(largest == index){
break;
}
swap(arr, index, largest);
left = largest * 2 + 1;
index = largest;
}
}
int main(){
int heapsize = 5;
for(int i = 0; i < 5; i++){
heapInsert(arr, i);
}
//swap(arr, 0, --heapsize);
while(heapsize > 0){
heapify(arr, 0, heapsize);
swap(arr, 0, --heapsize);
}
for(int i = 0; i < 5; i++){
printf("%d", arr[i]);
}
return 0;
}
3、推排序扩展
1、已知一个几乎有序的数组,几乎有序是指:如果把数组排好序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。
heapsize
维持在k个长度的堆排序。
前k个一定有一个最小值或最大值。
2、数据流的中位数
class Solution {
public:
priority_queue<int,vector<int>,less<int>> p;//最大值优先
priority_queue<int,vector<int>,greater<int>> q;//最小值优先
void Insert(int num) {
q.push(num);
p.push(q.top());
q.pop();
int num2 = p.size() - q.size();
if(num2 > 1){
int num3 = p.top();
p.pop();
q.push(num3);
}else if(num2 < -1){
int num3 = q.top();
q.pop();
p.push(num3);
}
}
double GetMedian() {
if(p.size() > q.size()){
return (double)p.top();
}else if(p.size() == q.size()){
return ((double)p.top() + (double)q.top()) / 2;
}else{
return (double)q.top();
}
}
};
4、比较器
返回负数的时候,第一个参数排在前面
返回正数的时候,第二个参数排在前面
返回0的时候,谁排在前面无所谓
实质:重载比较运算符
5、不基于比较的排序算法
6、桶排序
#include<bits/stdc++.h>
using namespace std;
int getMax(int arr[]){
int m = 0;
for(int i = 0; i < 5; i++){
m = max(m, arr[i]);
}
int res = 0;
while(m){
m = m / 10;
res++;
}
return res;
}
//arr:arr数组中的某个数,d:第几轮入桶
int getSite(int arr, int d){
if(d == 1){
return (int)arr % 10;
}else{
return (arr / (int)pow(10, d - 1)) % 10;
}
}
int main(){
int arr[5] = {44, 45, 22, 51, 100};
int digit = getMax(arr);
printf("%d\n", getSite(55,3));
for(int i = 1; i <= digit; i++){
int count[10] = {0};
for(int j = 0; j < 5; j++){
count[getSite(arr[j], i)]++;
}
//前缀和
for(int j = 1; j <= 5; j++){
count[j] = count[j] + count[j - 1];
}
int b[5];
for(int j = 4; j >=0; j--){
int k = getSite(arr[j], i);
b[count[k] - 1] = arr[j];
count[k]--;
}
for(int j = 0; j < 5; j++){
arr[j] = b[j];
}
}
for(int i = 0; i < 5; i++){
printf("%d ", arr[i]);
}
return 0;
}
7、稳定性
数组中多个相同的数字,在排序之后,它们之间的相对位置不变
2 1(第一个) 1(第二个) 3 4
1(第一个)1(第二个)2 3 4
8、多种排序的异同点总结
时间复杂度 | 空间复杂度 | 稳定性 | |
---|---|---|---|
选择 | O(N2) | O(1) | ❌ |
冒泡 | O(N2) | O(1) | ✔ |
插入 | O(N2) | O(1) | ✔ |
归并 | O(N log N) | O(N) | ✔ |
快排 | O(N log N) | O(log N) | ❌ |
堆 | O(N log N) | O(1) | ❌ |