一、在N个元素中选出前M个元素(N远大于M时)
在1,000,000个元素中选出前100名。
思路一:对N的元素进行排序,然后选出前M个元素,时间复杂度为O(NlogN)(采用快排或归并排序等高级排序算法)
思路二:使用优先队列,可以将时间复杂度降低为O(NlogM)。具体实现:使用优先队列维护M个元素,即每次向优先队列中加入一个元素时,需要将当前队列中的最小元素替换出去,保证队列中的M个元素是当前遍历过的元素中的最大的前M个。
使用最小堆
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
import heapq
from collections import Counter
class Solution:
def topKFrequent(self, nums, k):
res = []
dic = Counter(nums)
max_heap = [(-val, key) for key, val in dic.items()]
heapq.heapify(max_heap)
for i in range(k):
# 把Heap里面出现最大的几个数pop出来就好,记得这里需要pop出来的是key, 不是val
res.append(heapq.heappop(max_heap)[1])
return res
# 利用Counter + PriorityQueue来做
# python skill
class Solution(object):
def topKFrequent(self, nums, k):
return [item[0] for item in collections.Counter(nums).most_common(k)]
class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
//将数组放入一个记录频次的Map中
TreeMap<Integer, Integer> map = new TreeMap<>();
for(int num : nums){
if(map.containsKey(num)){
map.put(num, map.get(num)+1);
}else{
map.put(num, 1);
}
}
//通过一个最多可容纳K个元素的优先队列来选出出现频率前k高的k个元素
//优先队列在构造时,通过传入的外部比较器Comparator 可以定义优先队列里元素类型的比较方法
//这里,优先队列里存放Integer, 传入的外部比较器重新定义了比较两个整型类型的方法,即比较它们出现的频次
//Java内置优先队列的底层是最小堆,所以出队的是根据比较器定义的最小元素
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>(){
@Override
public int compare(Integer a, Integer b){ //使用匿名内部类实现Comparator接口,重新定义了比较两个整型类型的方法,即比较它们出现的频次
return map.get(a)-map.get(b);
}
});
for(int num : map.keySet()){
if(pq.size()<k){
pq.add(num);
}else{
if(map.get(num)>map.get(pq.peek())){ //优先队列内只保持k个元素,入队时需要和优先级最高的元素((频次)最小的元素)作比较
pq.remove();
pq.add(num);
}
}
}
//根据要求,返回一个List
ArrayList<Integer> list = new ArrayList<>();
while(!pq.isEmpty()){
list.add(pq.remove());
}
return list;
}
}
二、POJ2431
用优先队列模拟贪心
题意:
驾驶卡车行驶L单位距离,卡车初始P单位汽油,行驶1单位距离消耗1单位汽油。图中有一些加油站,可以在加油站中给汽车加油,知道加油站离终点的距离也知道每个加油站最多给汽车加多少油,现在要求最少停车几次,能够使得汽车到达终点。若不能到达,输出-1。
思路:
题目中加油站数量N非常大,所以不能暴力。然后可以这样想,路过加油站的时候,先不加油,但是你没油的时候你要知道你之前是可以加油的。也就是到达加油站的时候,就获得了加油的权利。这样,当汽车没油的时候,认为在之前加过油就行了。
那么为了使得加油次数最少,每次没油的时候就选取经过的加油站中能够加油最多的一个加油站加油即可。那么就用到了优先队列,大根堆。路过时push(),加油时pop()。
解题思路:反正就是一个个站点遍历下去,如果够不到下一个站点,那说明前面需要加油。就把优先队列中加油最大的那个提出来。每扫过一个站点,把它可加的油放入优先队列中维护。
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 10010;
struct Node{
int x;
int y;
}stop[maxn];
bool cmp (Node a, Node b) //按照距离从大到小排序
{
return a.x > b.x;
}
int main()
{
int n, l, p, cnt = 0;;
scanf("%d",&n);
priority_queue<int > q;
for(int i = 0; i < n ;i ++){
scanf("%d%d", &stop[i].x, &stop[i].y);
}
scanf("%d%d", &l, &p);
sort(stop, stop + n, cmp);
int i = 0;
p ++; //防止初始位置就有加油站
l ++;
while(p != 0 && l != 0){
p --;
l --;
//cout<<"p="<<p<<" l="<<l<<endl;
if(l == 0) //到达终点
break;
if(i < n && stop[i].x == l){ //路过加油站
q.push(stop[i++].y);
}
if(p == 0 && q.size() != 0){ //没油了 并且还可以加油
p += q.top();
q.pop();
cnt ++; //停车次数加1
}
}
if(l == 0){
printf("%d\n",cnt);
} else {
printf("-1\n");
}
return 0;
}
参考:
三、小红薯笔试
血量降序排序:2~max,选择一个最合适的值(因为如果大的都不行,那么比这个“大”的小的元素就都不行,根本不用考虑。)
选出这个最合适的值a,分别对每个元素攻击Xi(相减),Xi-a后的值也放入优先队列里,每次都从优先队列里找到最大的元素。
def list2gen(a):
for n in a:
yield n
def ismatch(X):
# 在达到目标时输出X
fali_need = 0
for Hi in list2gen(H):
fali_need += Hi // X
# fali_need = sum([Hi // X for Hi in H])
# 法力不够,用物理攻击弥补
if fali_need >= M:
wuli_need = X * (fali_need - M)
for Hi in list2gen(H):
wuli_need += Hi % X
# wuli_need += sum([Hi % X for Hi in H])
if M + fali_need <= T:
return True
else:
return False
# 法力超出,多出来的这样用
# X=3,Hi=2,这样使用一次
else:
yushu = sorted([Hi % X for Hi in H])
wuli_need = sum(sorted(yushu)[::-1][M - fali_need :])
if M + wuli_need <= T:
return True
else:
return False
def solution(N, T, M, H):
H = sorted(H)[::-1]
# 特殊情况
if M + sum(H[M:]) > T:
return -1
l = (sum(H) - (T - M)) // M
r = H[0]
# print(l, r)
while l < r:
mid = l + ((r - l) >> 1)
# print(f'mid: {mid}')
if ismatch(mid):
r = mid
else:
l = mid + 1
return l
import sys
line = sys.stdin.readline().strip()
N, T, M = list(map(int, line.split()))
line = sys.stdin.readline().strip()
H = list(map(int, line.split()))
print(solution(N, T, M, H))
#include <iostream>
#include <algorithm>
#include <string>
#include <queue>
using namespace std;
int main()
{
int n, t, m;
cin>>n>>t>>m;
int h[100010];
for (int i = 1; i <= n; ++i)
cin>>h[i];
sort(h + 1, h + n + 1);
int l = 0, r = h[n], ans;
int x = 0x3ffffff;
while(l <= r)
{
int mid = (l + r) / 2;
priority_queue<int> que;
ans = 0;
int cnt = m;
for (int i = 1; i <= n; ++i)
{
int tmp = (h[i] / mid < cnt) ? (h[i] / mid) : cnt;
ans += tmp;
cnt -= tmp;
que.push(h[i] - tmp * mid);
}
while(!que.empty())
{
ans += (cnt > 0) ? 1 : que.top();
que.pop();
cnt--;
}
if (ans <= t)
{
x = (mid < x) ? mid : x;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
if (x <= 0x3ffffff)
{
cout<<x<<endl;
}
else
{
cout<<(-1)<<endl;
}
}
有个优先队列题目汇总的https://www.cnblogs.com/xenny/p/9379659.html