【C++】priority_queue的理解及实战解决k路归并问题

一、想必刷leetcode各位已经知道了priority_queue的重要性!!!赶快把细节了解清楚

  在C++中的priority_queue底层是堆(heap)来实现的,priority_queue又称为优先队列。在优先队列中,队首元素一定是当前队列中优先级最高的那一个。

  当然,可以在任何时候往优先队列里面加入(push)元素,而优先队列底层的数据结构堆(逻辑实现是完全二叉树,具体的构建方法自行查阅)会随时调整结构,使得每次的队首元素都是优先级最大的。

二、用法:

0x00 定义为:priority_queue < typename > name;

有大根堆和小根堆两种优先级排序的方法;

和队列不一样的是,优先队列没有front()和back()函数,而只能通过top()函数来访问队首元素。

(1)push():插入元素到队尾,并排序
(2)top():可以获得队首元素(即堆顶元素)
(3)pop():队首元素(即堆顶元素)出队
(4)empty():和queue一样
(5)size():
(6)emplace () :原地构造一个元素并插入队列

#include<iostream>
#include<queue>
using namespace std;
int main () {
	priority_queue<int> q;
	q.push(1);
	q.push(4);
	q.push(3);
	cout<<q.top()<<endl; // 4 --因此优先队列默认为**大根堆!**
	q.pop();
	return 0;
}

0x01 priority_queue优先级的设置:

  1. 基本数据类型的优先级设置
    priority_queue<int, vector<int>, less<int> > q;
    注:

    1. less表示数字大的优先级越大,即大根堆,而greater表示数字小的优先级越大,即小根堆;
    2. vector是用来承载底层数据结构堆(heap)的容器;
  2. 结构体的优先级设置

struct Status {
	int val;
	ListNode *ptr;
	// 此处小于号的重载于排序函数sort中的cmp函数有些类似。
	bool operator < (const Status &f2) const {
		return val > f2.val; // 这样定义的是小根堆,反之为大根堆
	}
};
priority_queue <Status> q;

在排序中,如果是“return f1.val > f2.val”,那么则是按值从高到低排序,但是在优先队列中却是把值小的放到队首。原因在于,优先队列本身默认的规则就是优先级高的放在队首,因此把小于号重载为大于号的功能时只是把这个规则反向了一下。如果读者无法理解,那么不妨记住,优先队列的这个函数与sort中的cmp函数的效果是相反的。

  1. 将cmp函数写在原先结构体外边:
// 定义链表
struct ListNode {
	int val;
	ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};
struct cmp {
    // 注意将 < 变成 ()  
    bool operator()(ListNode* a, ListNode* b) {
        return a->val > b->val; //小根堆
    }
};
// 优先队列的定义,中间是定义存在变量的容器:
priority_queue<ListNode*, vector<ListNode*>, cmp> q;
// 1.如果直接默认初始化,这个时候是 pair的比较,先比较第一个元素,第一个相等比较第二个。
priority_queue<pair<int, int>> pq; //大根堆,第一个元素大的在顶堆
// 添加新元素的方法
pq.push(pair<int, int> (3, 0));
//or  pq.emplace(3,0);

//2. 自定义初始化
struct cmp1{
    bool operator()(const pair<int, int> &a, const pair<int, int> &b){
            return a.first + a.second < b.first + b.second; //定义排序方法,这里是两者和大的优先,做大根堆排序;
    }
};
priority_queue<pair<int, int>, vector<pair<int, int>>, cmp1> mp;
//这样在执行mp.push(make_pair(nums[i], nums1[j]));时就会自动的就行排序.
//使用优先队列来模拟堆的运行在c++中是很方便的.
//堆的函数
/*
     ma.pop();
     ma.top();
     ma.empty();
     ma.size();
     ma.emplace();
     ma.push();
*/
//3. 针对于priority_queue中存放pair对象时,自定义排序方法
struct cmp2
{
    template <typename T, typename U>
    bool operator()(T const &left, U const &right)
    {
    // 以first比较。输出结果为First较大的在前First相同时,先进入队列的元素在前。
        if (left.first < right.first)
            return true;
        return false;
    }
};

priority_queue<pair<int, int>, vector<pair<int, int>>, cmp2> pq; //这是大根堆

三、实战(k路归并):

题目一:合并k个有序数组

#include<iostream>
#include<queue>
using namespace std;

class Node{
public:
	int row;
	int col;
	int val;
	Node (int r, int c, int v) {
		row = r;
		col = c;
		val = v;
	}
	bool operater < (const Node &f) const {
		return val > f.val;  // 这里重载的目的是建立小根堆
	}
};
vector<int> mergekSortedArrays(vector<vector<int>> &arrays) {
	vector<int> res;
	if (arrays.empty()) return res;
	priority_queue<Node> q; // 创建一个小根堆
	for (int row=0; row<arrays.size(); row++) {
		if (arrays[row].empty()) continue;
		// 将每一个数组的第一个数放到优先队列(小根堆)中
		q.push(Node(row, 0, arrays[row][0]));
	}
	while (!q.empty()) {
		Node cur = q.top();
		q.pop();
		res.push_back(cur.val);
		int row = cur.row;
		// 找出弹出最小值所在数组后边一位数,并放入到优先队列中
		int nextCol = cur.col + 1;
		if (nextCol<arrays[row].size()) 
			q.push(Node(row, nextCol, array[row][nextCol]));
	}
	return res;
}

题目二:合并k个有序链表

#include<iostream>
#include<vector>
using namespace std;

class ListNode {
	int val;
	ListNode *next;
	ListNode() : val(0), next(nullptr) {}
	ListNode(int x) : val(x), next(nullptr) {}
	ListNode(int x, ListNode *next) : val(x), next(next) {}
};

/*
方法一:用分治的思想合并
*/
// 递归的方法合并两个链表或者也可以使用归并排序的思路完成
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
	if (l1==nullptr) return l2;
	if (l2==nullptr) return l1;
	if (l1->val < l2->val) {
		l1->next = mergeTwoLists(l1->next, l2);
		return l1;
	}
	else {
		l2->next = mergeTwoLists(l1, l2->next);
		return l2;
	}
}
// 分治处理
ListNode* merge(vector<ListNode*>& lists, int l, int r) {
	if (l>r) return nullptr;
	if (l==r) return lists[l];
	int mid = l + (r-l)/2;
	
	ListNode* l1 = merge(lists, l, mid);
	ListNode* l2 = merge(lists, mid+1, r);
	return mergeTwoLists(l1, l2);
}
// 合并k个有序链表
ListNode* mergekSortedLists(vector<ListNode*>& lists) {
	int listsLength = lists.size();
	return merge(lists, 0, listsLength-1);
}

/*
方法二:用小根堆的方法来求解
*/
struct cmp {
	bool operater()(ListNode*l1, ListNode*l2) {
		return l1->val>l2->val; // 同样是为了初始化小根堆
	}
}
ListNode* mergekSortedLists(vector<ListNode*>& lists) {
	
	priority_queue<ListNode*, vector<ListNode*>, cmp> q;
	// 这里将每一个链表的头节点放入到优先队列中
	for (auto node: lists) {
		if (node!=nullptr) q.push(node);
	}
	ListNode* head = new ListNode(1);
	ListNode* tail = head;
	while (!q.empty()) {
		// 取出值最小的节点
		ListNode* cur = q.top();
		q.pop();
		// 将新建的头节点与最小的节点连接起来
		tail->next = cur;
		tail = tail->next;
		// 当所取出链表中的next节点不为空的话就添加到优先队列中
		if (cur->next) q.push(cur->next);
	}
	return head->next;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郝同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值