1.堆数据结构
堆排序模板
void heapInsert(vector<int>& arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
//这里实际上考虑了多种情况了
swap(arr[index], arr[(index - 1) / 2]);
index = (index - 1) / 2;
}
}
/*
heapify的作用是向下调整
@size:代表最后堆中的最后一个元素索引
*/
void heapify(vector<int>& arr, int index, int size) {
int left = index * 2 + 1;
while(left < size) {
//判断左右两边的数哪一个比较大
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr[largest], arr[index]);
index = largest;
left = index * 2 + 1;
}
}
void HeapSort(vector<int>& arr) {
if (arr.size()<2) {
return;
}
for (int i = 0; i < arr.size(); i++) {
heapInsert(arr, i);//先建成大根堆,然后弹出一个节点
}
int size = arr.size();
swap(arr[0],arr[--size]);//因为我们从序号0开始存放数据
while (size > 0) {
heapify(arr, 0, size);
swap(arr[0], arr[--size]);
}
}
上面的模板仅仅适用于直接给出一组数据的情况,不能处理当一个新的数据进来时候怎么操作,我们手动实现了一个类似大根堆的操作,当然这个模板存在一定的问题,但无大碍了....HeapSort的接口我们实际上没有删除元素
//注意堆排序的模板与优先级队列的实现还是有些区别的
//优先级队列需要考虑删除和增加元素,而堆排序元素的个数一开始就是确定的了
//插入元素,删除元素,取极值以及将某一整组数据配成一个heap
//我们的模板是从0位置开始存储数据的
class Heap {
public:
Heap() { size = 0; }
Heap(const vector<int>& arr) {
m_arr = arr;
size = arr.size();
HeapSort(m_arr);
}
bool isEmpty() { return size==0; }
void heapInsert(vector<int>& arr, int index);
void heapify(vector<int>& arr, int index, int size);
void HeapSort(vector<int>& arr);
void insert(int element);
int delMax();
int top();
int length(){ return size; }
void print();
private:
int size ;
vector<int> m_arr;
};
void Heap::print() {
for (auto it : m_arr) {
cout << it << " ";
}
}
void Heap::insert(int element) {
size++; //数据量+1
m_arr.push_back(element);
heapInsert(m_arr,size-1);//需要调整的节点
}
int Heap::delMax() {
swap(m_arr[0], m_arr[--size]);
heapify(m_arr, 0, size);
int res = m_arr[size];
m_arr.pop_back();
return res;
}
int Heap::top() {
if (!isEmpty()) {
return m_arr[0];
}
}
/*
@index : 需要调整位置的节点index
*/
void Heap::heapInsert(vector<int>& arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
//这里实际上考虑了多种情况了
swap(arr[index], arr[(index - 1) / 2]);
index = (index - 1) / 2;
}
}
/*
heapify的作用是向下调整
@index:需要调整位置的节点Index
@size:代表堆中的最后一个元素索引
*/
void Heap::heapify(vector<int>& arr, int index, int size) {
int left = index * 2 + 1;
while(left < size) {
//判断左右两边的数哪一个比较大
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr[largest], arr[index]);
index = largest;
left = index * 2 + 1;
}
}
void Heap::HeapSort(vector<int>& arr) {
if (arr.size()<2) {
return;
}
for (int i = 0; i < arr.size(); i++) {
heapInsert(arr, i);//先建成大根堆,然后弹出一个节点
}
int size = arr.size();
swap(arr[0],arr[--size]);
while (size > 0) {
heapify(arr, 0, size);
swap(arr[0], arr[--size]);
}
}
STL中优先级队列中的定义 https://blog.csdn.net/weixin_36888577/article/details/79937886
priority_queue <class T,Sequence = vector<T>, class Compare = less<typename Sequemcr::value_type>>,默认是大根堆。可以看到优先队列完全以底层容器为依据,再加上heap的处理规则。
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
- top 访问队头元素
- empty 队列是否为空
- size 返回队列内元素个数
- push 插入元素到队尾 (并排序)
- emplace 原地构造一个元素并插入队列
- pop 弹出队头元素
- swap 交换内容
struct tmp2 //重写仿函数
{
bool operator() (tmp1 a, tmp1 b)
{
return a.x < b.x; //大顶堆
}
};
2.堆的题目
最小的K个数用大根对,最大的K个数用小根堆
面试题40. 最小的k个数 https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
//求最小的k个数应该建一个大根堆
priority_queue<int> m_queue;
for(int i = 0; i < arr.size(); i++){
m_queue.push(arr[i]);
if(m_queue.size() > k){
m_queue.pop();
}
}
vector<int> res;
while(!m_queue.empty()){
int tmp = m_queue.top();
res.push_back(tmp);
m_queue.pop();
}
return res;
}
};
347. 前 K 个高频元素 https://leetcode-cn.com/problems/top-k-frequent-elements/
struct tmp2 //重写仿函数
{
bool operator() (pair<int,int>& p1,pair<int,int>& p2)
{
return p1.second > p2.second;
}
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
map<int,int> Table;
for(int i = 0 ; i < nums.size(); i++){
Table[nums[i]]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,tmp2> m_queue;
for(auto item : Table){
m_queue.push(item);
if(m_queue.size() > k){
m_queue.pop();
}
}
vector<int> res;
while(!m_queue.empty()){
pair<int,int> tmp = m_queue.top();
res.push_back(tmp.first);
m_queue.pop();
}
return res;
}
};
692. 前K个高频单词 https://leetcode-cn.com/problems/top-k-frequent-words/
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
//实际上就是大小为k小根堆,然后弹出顶部就可以了
priority_queue<int,vector<int>,greater<int>> m_queue;
for(int i = 0 ; i < nums.size(); i++){
m_queue.push(nums[i]);
if(m_queue.size() > k){
m_queue.pop();
}
}
return m_queue.top();
}
};
合并K个链表 https://leetcode-cn.com/problems/merge-k-sorted-lists/
没想到答案与这个老哥如出一辙https://leetcode-cn.com/problems/merge-k-sorted-lists/
struct cmp{
bool operator()(const ListNode* p1, const ListNode* p2){
return p1->val > p2->val;
}
};
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.size() == 0) return nullptr;
priority_queue<ListNode*,vector<ListNode*>,cmp> m_queue;
for(int i = 0 ; i < lists.size(); i++){
if(lists[i] == nullptr) continue; //非常关键
m_queue.push(lists[i]);
}
ListNode* dummy = new ListNode(-1);
ListNode* curNode = dummy;
while(!m_queue.empty()){
ListNode* nextNode = m_queue.top();
curNode->next = nextNode;
curNode = nextNode;
m_queue.pop();
if(nextNode->next!=NULL){
m_queue.push(nextNode->next);
}
}
return dummy->next;
}
};