优先队列(priority queue)

目录

一,优先队列

二,STL优先队列

三,二叉堆实现优先队列

四,二项堆实现优先队列

五,线段树实现优先队列

六,OJ实战

力扣 295. 数据流的中位数

力扣 LCP 24. 数字游戏

力扣 LCP 30. 魔塔游戏

力扣 2462. 雇佣 K 位工人的总代价

力扣 632. 最小区间

力扣 857. 雇佣 K 名工人的最低成本

其他应用


一,优先队列

优先队列可以插入元素、查询最大值、删除最大值。

优先队列其实就是最大堆或最小堆,最大堆可以用来实现最大优先队列,最小堆可以用来实现最小优先队列。

用各种堆都可以实现优先队列。

二,STL优先队列

优先队列如果要自定义排序函数,只能用仿函数,不能用普通函数的指针。

示例:

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

class cmp
{
public:
	bool operator()(int a, int b)
	{
		return a < b;
	}
};

bool cmp2(int a, int b)
{
	return a < b;
}

template <typename A>
void display()
{
	A q;
	q.push(1);
	q.push(3);
	q.push(4);
	q.push(2);
	while (!q.empty())
	{
		cout << q.top();
		q.pop();
	}
	cout << endl;
}

int main()
{
	display < priority_queue<int, vector<int>> >();  //默认大顶堆	
	display < priority_queue <int, vector<int>, greater<int>> >();//小顶堆
	display < priority_queue <int, vector<int>, less<int>> >();//大顶堆
	display < priority_queue<int, vector<int>, cmp>>();//大顶堆
	//display < priority_queue<int, vector<int>, cmp2>>();  错误
	return 0;
}

输出:

4321
1234
4321
4321

附上priority_queue的实现代码:

		// TEMPLATE CLASS priority_queue
template<class _Ty,
	class _Container = vector<_Ty>,
	class _Pr = less<typename _Container::value_type> >
	class priority_queue
	{	// priority queue implemented with a _Container
public:
	......
	一堆函数
	......
protected:
	_Container c;	// the underlying container
	_Pr comp;	// the comparator functor
	};

可以看出模板参数_Pr的默认值是less,也就是说默认排序函数是less函数。

priority_queue中包含一个排序的序列,堆顶是这个序列的最后一个元素,所以从小到大排序是大顶堆,从大到小排序是小顶堆。

三,二叉堆实现优先队列

二叉堆

最大优先队列:

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

template<typename T>
bool cmp(T a, T b)
{
    return a < b; //最大堆
}

template<typename T>
void exchange(T* a, T* b)
{
    T tmp = *a;
    *a = *b;
    *b = tmp;
}

int LeftChild(int id)
{
    return id * 2;
}
int RightChild(int id)
{
    return id * 2 + 1;
}
int Parent(int id)
{
    return id/2;
}

template<typename T>
void AdjustHeap(T* arr, int rootId, int size)
{
    int largest = rootId, left = LeftChild(rootId), right = RightChild(rootId);
    if (left < size && cmp(arr[largest], arr[left]))largest = left;
    if (right < size && cmp(arr[largest], arr[right]))largest = right;
    if (largest == rootId)return;
    exchange(arr + rootId, arr + largest);
    AdjustHeap(arr, largest, size);
}

template<typename T>
void HeapIncrese(T* arr, int size, int id, T newValue)
{
    arr[id] = newValue;
    while (id > 0 && cmp(arr[Parent(id)], arr[id])) {
        exchange(arr + id, arr + Parent(id));
        id = Parent(id);
    }
}

template<typename T>
void HeapInsert(T* arr, int &size,T value)
{
    HeapIncrese(arr,size+1,size,value);
    size++;
}

int g_size;
void Init()
{
    g_size = 0;
}
template<typename T>
T Top(T* arr)
{
    return arr[0];
}
template<typename T>
void Push(T* arr, T value)
{
    HeapInsert(arr,g_size,value);
}
template<typename T>
void Pop(T* arr)
{
    g_size--;
    exchange(arr, arr+g_size);
    AdjustHeap(arr, 0, g_size);
}

int main()
{
    int q[10];
    Init();
    Push(q,4);
    Push(q,2);
    Push(q,6);
    Push(q,3);
    Push(q,4);
    Push(q,5);
    cout<<Top(q)<<" ";
    Pop(q);
    cout<<Top(q)<<" ";
    Pop(q);
    cout<<Top(q)<<" ";
    Pop(q);
    cout<<Top(q)<<" ";
    Pop(q);
    cout<<Top(q)<<" ";
    Pop(q);
    cout<<Top(q)<<" ";
    Pop(q);
    cout<<endl<<g_size;
    return 0;
}

运行结果:

6 5 4 4 3 2
0

四,二项堆实现优先队列

二项堆 里面已经实现了push、pop、top等操作,所以它就是一种优先队列。

五,线段树实现优先队列

代码:

struct anode
{
    int t;
    int d;
};
int n;
int num[10001],maxx[40001];
struct anode node[10001];
 
void build(int key, int low, int high)//id
{
	if (low == high)
	{
		maxx[key] = low;
		return;
	}
	int mid = (low + high) / 2;
	build(key * 2, low, mid);
	build(key * 2 + 1, mid + 1, high);
	maxx[key] = (num[maxx[key * 2]] > num[maxx[key * 2 + 1]]) ? maxx[key * 2] : maxx[key * 2 + 1];
}
 
void update(int key, int low, int high, int uplace)//id
{
	if (low == high)
	{
		maxx[key] = low;
		return;
	}
	int mid = (low + high) / 2;
	if (uplace <= mid)update(key * 2, low, mid, uplace);
	else update(key * 2 + 1, mid + 1, high, uplace);
	maxx[key] = (num[maxx[key * 2]] > num[maxx[key * 2 + 1]]) ? maxx[key * 2] : maxx[key * 2 + 1];
}
 
int query(int key, int low, int high, int x, int y)//id
{
	if (low == x && high == y)return maxx[key];
	int mid = (low + high) / 2;
	if (mid < x)return query(key * 2 + 1, mid + 1, high, x, y);
	if (mid >= y)return query(key * 2, low, mid, x, y);
	int a = query(key * 2, low, mid, x, mid);
	int b = query(key * 2 + 1, mid + 1, high, mid + 1, y);
	return  (num[a]>num[b]) ? a : b;
}
 
void push(int loc)
{
    num[loc]=node[loc].t; ///根据实际情况修改
    update(1,1,n,loc);
}
void pop(int loc)
{
    num[loc]=0; ///根据实际情况修改
    update(1,1,n,loc);
}
int top()//id
{
    return query(1,1,n,1,n);
}

​应用:

力扣 630. 课程表 III

六,OJ实战

力扣 295. 数据流的中位数

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3) 
findMedian() -> 2

思路:

维护1个最大堆和1个最小堆,并动态维持他们的尺寸相同。

class MedianFinder {
public:
    MedianFinder() {
    }
 
    void addNum(int num) {
        if (qmax.empty()||num<=qmax.top())qmax.push(num);
        else qmin.push(num);
    }
 
    double findMedian() {
        while (qmin.size() > qmax.size()) qmax.push(qmin.top()),qmin.pop();
        while (qmax.size() > qmin.size()+1)qmin.push(qmax.top()),qmax.pop();
        if (qmax.size() > qmin.size())return qmax.top();
        return (qmax.top() + qmin.top()) / 2.0;
    }
private:
    priority_queue<int, vector<int>> qmax;
    priority_queue <int, vector<int>, greater<int>> qmin;
};

或者用自己实现的堆:

template<typename T>
class MyHeap {
public:
	T arr[100000];
	MyHeap(int type)//0最小堆1最大堆
	{
		this->type = type;
	}
	T Top()
	{
		return arr[0];
	}
	void Push(T value)
	{
		HeapInsert(arr, value);
	}
	void Pop()
	{
		size--;
		exchange(arr, arr + size);
		AdjustHeap(arr, 0);
	}
	bool empty()
	{
		return size == 0;
	}
	int Size()
	{
		return size;
	}
private:
	void AdjustHeap(T* arr, int rootId)
	{
		int largest = rootId, left = LeftChild(rootId), right = RightChild(rootId);
		if (left < size && cmp(arr[largest], arr[left]))largest = left;
		if (right < size && cmp(arr[largest], arr[right]))largest = right;
		if (largest == rootId)return;
		exchange(arr + rootId, arr + largest);
		AdjustHeap(arr, largest);
	}
	void HeapIncrese(T* arr, int id, T newValue)
	{
		arr[id] = newValue;
		while (id > 0 && cmp(arr[Parent(id)], arr[id])) {
			exchange(arr + id, arr + Parent(id));
			id = Parent(id);
		}
	}
	void HeapDecrese(T* arr, int size, int id, T newValue)
	{
		arr[id] = newValue;
		AdjustHeap(arr, id, size);
	}
	void HeapChange(T* arr, int id, T newValue)
	{
		if (cmp(arr[id], newValue))HeapIncrese(arr, id, newValue);
		else HeapDecrese(arr, id, newValue);
	}
	void HeapInsert(T* arr, T value)
	{
		HeapIncrese(arr, size++, value);
	}
	bool cmp(T a, T b)
	{
		return type ? (a < b) : (a > b);
	}
	void exchange(T* a, T* b)
	{
		T tmp = *a;
		*a = *b;
		*b = tmp;
	}

	int LeftChild(int id)
	{
		return id * 2 + 1;
	}
	int RightChild(int id)
	{
		return id * 2 + 2;
	}
	int Parent(int id)
	{
		return (id - 1) / 2;
	}
	int type;
	int size = 0;
};



class MedianFinder {
public:
	MedianFinder() {
	}

	void addNum(int num) {
		if (qmax.empty() || num <= qmax.Top())qmax.Push(num);
		else qmin.Push(num);
	}

	double findMedian() {
		while (qmin.Size() > qmax.Size()) qmax.Push(qmin.Top()), qmin.Pop();
		while (qmax.Size() > qmin.Size() + 1)qmin.Push(qmax.Top()), qmax.Pop();
		if (qmax.Size() > qmin.Size())return qmax.Top();
		return (qmax.Top() + qmin.Top()) / 2.0;
	}
private:
	MyHeap<int> qmax{ 1 };
	MyHeap<int> qmin{ 0 };
};

力扣 LCP 24. 数字游戏

小扣在秋日市集入口处发现了一个数字游戏。主办方共有 N 个计数器,计数器编号为 0 ~ N-1。每个计数器上分别显示了一个数字,小扣按计数器编号升序将所显示的数字记于数组 nums。每个计数器上有两个按钮,分别可以实现将显示数字加一或减一。小扣每一次操作可以选择一个计数器,按下加一或减一按钮。

主办方请小扣回答出一个长度为 N 的数组,第 i 个元素(0 <= i < N)表示将 0~i 号计数器 初始 所示数字操作成满足所有条件 nums[a]+1 == nums[a+1],(0 <= a < i) 的最小操作数。回答正确方可进入秋日市集。

由于答案可能很大,请将每个最小操作数对 1,000,000,007 取余。

示例 1:

输入:nums = [3,4,5,1,6,7]

输出:[0,0,0,5,6,7]

解释: i = 0,[3] 无需操作 i = 1,[3,4] 无需操作; i = 2,[3,4,5] 无需操作; i = 3,将 [3,4,5,1] 操作成 [3,4,5,6], 最少 5 次操作; i = 4,将 [3,4,5,1,6] 操作成 [3,4,5,6,7], 最少 6 次操作; i = 5,将 [3,4,5,1,6,7] 操作成 [3,4,5,6,7,8],最少 7 次操作; 返回 [0,0,0,5,6,7]。

示例 2:

输入:nums = [1,2,3,4,5]

输出:[0,0,0,0,0]

解释:对于任意计数器编号 i 都无需操作。

示例 3:

输入:nums = [1,1,1,2,3,4]

输出:[0,1,2,3,3,3]

解释: i = 0,无需操作; i = 1,将 [1,1] 操作成 [1,2] 或 [0,1] 最少 1 次操作; i = 2,将 [1,1,1] 操作成 [1,2,3] 或 [0,1,2],最少 2 次操作; i = 3,将 [1,1,1,2] 操作成 [1,2,3,4] 或 [0,1,2,3],最少 3 次操作; i = 4,将 [1,1,1,2,3] 操作成 [-1,0,1,2,3],最少 3 次操作; i = 5,将 [1,1,1,2,3,4] 操作成 [-1,0,1,2,3,4],最少 3 次操作; 返回 [0,1,2,3,3,3]。

提示:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^3

思路:

把力扣 295. 数据流的中位数中的MedianFinder进行改造,数据流的形式不变,把查询中位数改成查询所有数到中位数的距离总和。实现的思路不变,时间复杂度也不变。

利用改造后的MedianFinder,直接可以得到本题的答案。

class MedianFinder {
public:
	MedianFinder() {
	}

	void addNum(int num) {
		if (qmax.empty() || num <= qmax.top()) {
			if (qmax.size() == qmin.size()) {
				qmax.push(num);
				s += qmax.top() - num;
			}
			else {
				qmin.push(qmax.top()), qmax.pop();
				qmax.push(num);
				s += qmin.top() - num;
			}
		}
		else {
			if (qmax.size() == qmin.size()) {
				qmin.push(num);
				s += num - qmin.top();
				qmax.push(qmin.top()), qmin.pop();
			}
			else {
				qmin.push(num);
				s += num - qmax.top();
			}			
		}
	}	
	long long sumLen() {
		return s;
	}
private:
	long long s = 0;
	priority_queue<int, vector<int>> qmax;//最大堆放小的一半数据
	priority_queue <int, vector<int>, greater<int>> qmin;//最小堆放大的一半数据
};
class Solution {
public:
	vector<int> numsGame(vector<int>& nums) {
		MedianFinder opt;
		vector<int>ans;
		for(int i=0;i<nums.size();i++){
			opt.addNum(nums[i]-i);
			ans.push_back(opt.sumLen() % 1000000007);
		}
		return ans;
	}
};

力扣 LCP 30. 魔塔游戏

小扣当前位于魔塔游戏第一层,共有 N 个房间,编号为 0 ~ N-1。每个房间的补血道具/怪物对于血量影响记于数组 nums,其中正数表示道具补血数值,即血量增加对应数值;负数表示怪物造成伤害值,即血量减少对应数值;0 表示房间对血量无影响。

小扣初始血量为 1,且无上限。假定小扣原计划按房间编号升序访问所有房间补血/打怪,为保证血量始终为正值,小扣需对房间访问顺序进行调整,每次仅能将一个怪物房间(负数的房间)调整至访问顺序末尾。请返回小扣最少需要调整几次,才能顺利访问所有房间。若调整顺序也无法访问完全部房间,请返回 -1。

示例 1:

输入:nums = [100,100,100,-250,-60,-140,-50,-50,100,150]

输出:1

解释:初始血量为 1。至少需要将 nums[3] 调整至访问顺序末尾以满足要求。

示例 2:

输入:nums = [-200,-300,400,0]

输出:-1

解释:调整访问顺序也无法完成全部房间的访问。

提示:

  • 1 <= nums.length <= 10^5
  • -10^5 <= nums[i] <= 10^5
class Solution {
public:
	int magicTower(vector<int>& nums) {
		priority_queue<int, vector<int>>q;
		long long s = 0, s2 = 0, n = 0;
		for (auto x : nums) {
			if (x < 0)q.push(-x);
			s += x;
			if (s < 0) {
				s += q.top();
				s2 += q.top();
				q.pop();
				n++;
			}
		}
		if (s - s2 >= 0)return n;
		return -1;
	}
};

力扣 2462. 雇佣 K 位工人的总代价

给你一个下标从 0 开始的整数数组 costs ,其中 costs[i] 是雇佣第 i 位工人的代价。

同时给你两个整数 k 和 candidates 。我们想根据以下规则恰好雇佣 k 位工人:

  • 总共进行 k 轮雇佣,且每一轮恰好雇佣一位工人。
  • 在每一轮雇佣中,从最前面 candidates 和最后面 candidates 人中选出代价最小的一位工人,如果有多位代价相同且最小的工人,选择下标更小的一位工人。
    • 比方说,costs = [3,2,7,7,1,2] 且 candidates = 2 ,第一轮雇佣中,我们选择第 4 位工人,因为他的代价最小 [3,2,7,7,1,2] 。
    • 第二轮雇佣,我们选择第 1 位工人,因为他们的代价与第 4 位工人一样都是最小代价,而且下标更小,[3,2,7,7,2] 。注意每一轮雇佣后,剩余工人的下标可能会发生变化。
  • 如果剩余员工数目不足 candidates 人,那么下一轮雇佣他们中代价最小的一人,如果有多位代价相同且最小的工人,选择下标更小的一位工人。
  • 一位工人只能被选择一次。

返回雇佣恰好 k 位工人的总代价。

示例 1:

输入:costs = [17,12,10,2,7,2,11,20,8], k = 3, candidates = 4
输出:11
解释:我们总共雇佣 3 位工人。总代价一开始为 0 。
- 第一轮雇佣,我们从 [17,12,10,2,7,2,11,20,8] 中选择。最小代价是 2 ,有两位工人,我们选择下标更小的一位工人,即第 3 位工人。总代价是 0 + 2 = 2 。
- 第二轮雇佣,我们从 [17,12,10,7,2,11,20,8] 中选择。最小代价是 2 ,下标为 4 ,总代价是 2 + 2 = 4 。
- 第三轮雇佣,我们从 [17,12,10,7,11,20,8] 中选择,最小代价是 7 ,下标为 3 ,总代价是 4 + 7 = 11 。注意下标为 3 的工人同时在最前面和最后面 4 位工人中。
总雇佣代价是 11 。

示例 2:

输入:costs = [1,2,4,1], k = 3, candidates = 3
输出:4
解释:我们总共雇佣 3 位工人。总代价一开始为 0 。
- 第一轮雇佣,我们从 [1,2,4,1] 中选择。最小代价为 1 ,有两位工人,我们选择下标更小的一位工人,即第 0 位工人,总代价是 0 + 1 = 1 。注意,下标为 1 和 2 的工人同时在最前面和最后面 3 位工人中。
- 第二轮雇佣,我们从 [2,4,1] 中选择。最小代价为 1 ,下标为 2 ,总代价是 1 + 1 = 2 。
- 第三轮雇佣,少于 3 位工人,我们从剩余工人 [2,4] 中选择。最小代价是 2 ,下标为 0 。总代价为 2 + 2 = 4 。
总雇佣代价是 4 。

提示:

  • 1 <= costs.length <= 105
  • 1 <= costs[i] <= 105
  • 1 <= k, candidates <= costs.length
struct Node
{
    int x, id;
};
class cmp
{
public:
    bool operator()(Node a, Node b)
    {
        if (a.x == b.x)return a.id > b.id;
        return a.x > b.x;
    }
};
class Solution {
public:
    long long totalCost(vector<int>& costs, int k, int candidates) {
        priority_queue<Node, vector<Node>, cmp>q;
        int low = 0, high = costs.size() - 1;
        while (low < candidates && low < costs.size()) {
            q.push(Node{ costs[low],low++ });
        }
        while (high >= costs.size() - candidates && high >= low) {
            q.push(Node{ costs[high],high-- });
        }
        long long ans = 0;
        while (!q.empty() && k--) {
            Node nod = q.top();
            q.pop();
            ans += nod.x;
            if (low <= high) {
                if(nod.id<low)q.push(Node{ costs[low],low++ });
                else q.push(Node{ costs[high],high-- });
            }
        }
        return ans;
    }
};

力扣 632. 最小区间

你有 k 个 非递减排列 的整数列表。找到一个 最小 区间,使得 k 个列表中的每个列表至少有一个数包含在其中。

我们定义如果 b-a < d-c 或者在 b-a == d-c 时 a < c,则区间 [a,b] 比 [c,d] 小。

示例 1:

输入:nums = [[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
输出:[20,24]
解释: 
列表 1:[4, 10, 15, 24, 26],24 在区间 [20,24] 中。
列表 2:[0, 9, 12, 20],20 在区间 [20,24] 中。
列表 3:[5, 18, 22, 30],22 在区间 [20,24] 中。

示例 2:

输入:nums = [[1,2,3],[1,2,3],[1,2,3]]
输出:[1,1]

提示:

  • nums.length == k
  • 1 <= k <= 3500
  • 1 <= nums[i].length <= 50
  • -105 <= nums[i][j] <= 105
  • nums[i] 按非递减顺序排列
struct Node {
	int x, id;
};
class cmp
{
public:
	bool operator()(Node a, Node b)
	{
		return a.x > b.x;
	}
};
class Solution {
public:
	vector<int> smallestRange(vector<vector<int>>& nums) {
		vector<int>ids(nums.size(), 0);
		vector<int>ans{-100000,100000};
		priority_queue<Node, vector<Node>, cmp>q;//最小堆
		int minMax = 100000;
		int maxid = 0;
		for (int i = 0; i < ids.size(); i++) {
			if (nums[i][0] > nums[maxid][0])maxid = i;
			minMax = min(minMax, nums[i][nums[i].size() - 1]);
			q.push(Node{ nums[i][0],i });
		}
		while (true) {
			int t = q.top().x;
			int minid = q.top().id;
			q.pop();
			if (ans[1] - ans[0] > nums[maxid][ids[maxid]] - nums[minid][ids[minid]] ||
				(ans[1] - ans[0] == nums[maxid][ids[maxid]] - nums[minid][ids[minid]] && ans[0] > nums[minid][ids[minid]])) {
				ans = vector<int>{ nums[minid][ids[minid]] ,nums[maxid][ids[maxid]] };
			}
			if (ids[minid] + 1 >= nums[minid].size())return ans;
			ids[minid]++;
			if (nums[minid][ids[minid]] > nums[maxid][ids[maxid]])maxid = minid;
			q.push(Node{ nums[minid][ids[minid]],minid });
		}
		return ans;
	}
};

力扣 857. 雇佣 K 名工人的最低成本

有 n 名工人。 给定两个数组 quality 和 wage ,其中,quality[i] 表示第 i 名工人的工作质量,其最低期望工资为 wage[i] 。

现在我们想雇佣 k 名工人组成一个工资组。在雇佣 一组 k 名工人时,我们必须按照下述规则向他们支付工资:

  1. 对工资组中的每名工人,应当按其工作质量与同组其他工人的工作质量的比例来支付工资。
  2. 工资组中的每名工人至少应当得到他们的最低期望工资。

给定整数 k ,返回 组成满足上述条件的付费群体所需的最小金额 。在实际答案的 10-5 以内的答案将被接受。。

示例 1:

输入: quality = [10,20,5], wage = [70,50,30], k = 2
输出: 105.00000
解释: 我们向 0 号工人支付 70,向 2 号工人支付 35。

示例 2:

输入: quality = [3,1,10,10,1], wage = [4,8,2,2,7], k = 3
输出: 30.66667
解释: 我们向 0 号工人支付 4,向 2 号和 3 号分别支付 13.33333。

提示:

  • n == quality.length == wage.length
  • 1 <= k <= n <= 104
  • 1 <= quality[i], wage[i] <= 104
struct Node
{
    int q, w;
};
bool cmp(Node a, Node b)
{
    return a.w * b.q < b.w * a.q;
}

class cmp2
{
public:
    bool operator()(Node a, Node b)
    {
        return a.q < b.q;
    }
};

class Solution {
public:
    double mincostToHireWorkers(vector<int>& quality, vector<int>& wage, int k) {
        vector<Node>v;
        for (int i = 0; i < quality.size(); i++) {
            v.push_back(Node{ quality[i],wage[i] });
        }
        sort(v.begin(), v.end(), cmp);
        priority_queue<Node, vector<Node>, cmp2>q;
        int s = 0;
        for (int i = 0; i < k; i++) {
            s += v[i].q;
            q.push(v[i]);
        }
        double ans = v[k - 1].w * 1.0 / v[k - 1].q * s;
        for (int i = k; i < v.size(); i++) {
            s+= v[i].q;
            q.push(v[i]);
            auto nod = q.top();
            q.pop();
            s -= nod.q;
            ans = min(ans, v[i].w * 1.0 / v[i].q * s);
        }
        return ans;
    }
};

其他应用

A算法   

CSU 1588 合并果子

CCF-CSP-2017-3-4 地铁修建

力扣 347. 前 K 个高频元素

力扣 373. 查找和最小的K对数字

力扣 378. 有序矩阵中第K小的元素

力扣 1090. 受标签影响的最大值

力扣 1631. 最小体力消耗路径

力扣 451. 根据字符出现频率排序

2020编码大赛(2)初赛

  • 8
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值