形成堆:
首先形成堆,我们有两种处理(“上浮”和”下沉”)
上浮:在原堆尾添加元素时,通过比较添加元素和原堆(二叉树)中父节点关系,决定是否继续上浮
下沉:在原堆首添加元素时(此时添加元素是二叉树的根节点),通过比较父节点与左右节点,找出最大的节点,通过是否交换元素,判断是否需要继续下沉
上浮和下沉最坏情况都是lgn,其实就是和二叉树树高有关 T(n) = O(lgn)
总结:两种方法都能形成堆,可是两种方法都有不同使用场景才能发挥其最大威力,
比如:如果向堆集合中添加元素,对于一个序列我们习惯都是在尾部添加(数组,向量(Vector)都是这样),如果我们执意从序列首部添加元素,那未免不得承担将原序列向后挪的性能损失了(因为这是可以避免的,所以我不得不称它为一种”性能损失了”)
所以向原堆添加元素有两种做法:
1.将添加元素放在原堆尾部,采用上浮的做法重新生成堆
2.将添加元素放在原堆首部,采用下沉的做法重新生成堆(其实我上面说有性能损失其实是有问题的,因为计算机中表示序列的一般只有两种
1.顺序存储(数组之流)
2.链式存储(链表之流)
因此上种说法对于链式存储就不适用了(因为添加首元素只需修改指针就可以),此外我下面写的堆排序中是顺序存储结构采用下沉的做法生成堆,因为这种业务不需要挪,所以当我们把添加元素弄到首元素时没有不必要的性能损失时,就是它大放异彩的时候
知道如何形成堆,那么堆排序和优先队列也就不难了
堆排序:
我们首先将输入序列S,通过对所有父节点下沉操作形成堆(因为顺序存储的序列已经给定了,(下沉和上浮性能都无异了)
然后通过将堆的首元素与尾部元素交换,从首元素到新堆尾部元素(上次的堆尾部元素位置 - 1)产生新堆,以此类推,直到无新堆产生
通过操作最大或最小堆获得非递减或非递增序列
性能1.:对于序列大小为n,形成堆时间为O(n * lgn),这个上界不是很紧,读者可以运用自己的聪明才智(一丝丝数学知识)让其紧到O(n)
2.迭代产生新堆:第一步都没紧,我第二步不紧也是O(n * lgn)
综上可知,堆排序最坏情况下T(n) = O(n * lgn)
优先队列:
序列我采用的是顺序存储,每次Insert操作就是上浮的时间(O(lgn)),其他的就没什么可说的了,有说的我会来更
Priority_Queue.h
#pragma once
#include <vector>
#include <iostream>
template<typename V>
class Priority_Queue {
public:
Priority_Queue() = default;
~Priority_Queue() = default;
Priority_Queue(const std::vector<V>& v);
private:
std::vector<V> vec;
public:
void Insert(const V& v);
V Maximum();
void Print();
//V Extract_Max();
private:
void swim(const int& k) {
int largest = k;
while ((largest - 1) / 2 >= 0) {
int last = largest;
if (vec[largest] > vec[(last - 1) / 2]) {
largest = (last - 1) / 2;
std::swap(vec[largest], vec[last]);
}else {
break;
}
}
}
void sink(const int& lo) {
int largest = lo;
while (largest * 2 + 1 >= vec.size() - 1) {
int last = largest;
if (vec[larget] < vec[last * 2 + 1]) {
largest = last * 2 + 1;
}
if (last * 2 + 2 <= hi) {
if (vec[largest] < vec[last * 2 + 2]) {
largest = last * 2 + 2;
}
}
if (largest != last) {
std::swap(vec[largest], vec[last]);
}
else {
break;
}
}
}
};
template<typename V>
Priority_Queue<V>::Priority_Queue(const std::vector<V>& v) {
for (auto i : v) {
vec.push_back(i);
swim(vec.size() - 1);
}
}
template<typename V>
void Priority_Queue<V>::Insert(const V& v) {
vec.push_back(v);
swim(vec.size() - 1);
}
template<typename V>
V Priority_Queue<V>::Maximum() {
return vec[0];
}
template<typename V>
void Priority_Queue<V>::Print() {
for (auto& v : vec) {
std::cout << v << std::ends;
}
std::cout << std::endl;
}
main.cpp
#include <stdio.h>
#include <vector>
#include "Priority_Queue.h"
using namespace std;
void sink(vector<int>& vec, const int& lo, const int& hi);
//递归版sink
void keep_max_heap(vector<int>& vec, const int& lo, const int& hi) {
if (lo * 2 + 1 > hi) {
return;
}
int largest;
if (vec[lo] < vec[lo * 2 + 1]) {
largest = lo * 2 + 1;
}else {
largest = lo;
}
if (lo * 2 + 2 <= hi) {
if (vec[largest] < vec[lo * 2 + 2]) {
largest = lo * 2 + 2;
}
}
if (largest != lo) {
std::swap(vec[largest], vec[lo]);
keep_max_heap(vec, largest, hi);
}
}
void make_max_heap(vector<int>& vec) {
for (int i = vec.size() / 2 - 1; i >= 0; --i) {
//keep_max_heap(vec, i, vec.size() - 1);
sink(vec, i, vec.size() - 1);
}
}
void sink(vector<int>& vec,const int& lo,const int& hi) {
int largest = lo;
while (largest * 2 + 1 <= hi) {
int last = largest;
if (vec[last] < vec[last * 2 + 1]) {
largest = last * 2 + 1;
}
if (last * 2 + 2 <= hi) {
if (vec[largest] < vec[last * 2 + 2]) {
largest = last * 2 + 2;
}
}
if (largest != last) {
std::swap(vec[largest], vec[last]);
}else {
break;
}
}
}
void heap_sort(vector<int>& vec) {
make_max_heap(vec);
for (int i = vec.size() - 1; i >= 1; --i) {
std::swap(vec[0], vec[i]);
keep_max_heap(vec,0,i - 1);
}
}
void print_vector(const vector<int>& vec) {
for (auto& v : vec) {
printf("%d ", v);
}
printf("\n");
}
int main(int argc,char** argv) {
vector<int> vec{ 0,1,2,3,4,5,6,7,8,9 };
/*make_max_heap(vec);
print_vector(vec);
heap_sort(vec);
print_vector(vec);*/
Priority_Queue<int> pq(vec);
pq.Print();
pq.Insert(16);
pq.Insert(10);
pq.Insert(13);
pq.Print();
system("pause");
return 0;
}