沿街有一排连续的房屋。每间房屋内都藏有一定的现金。现在有一位小偷计划从这些房屋中窃取现金。
由于相邻的房屋装有相互连通的防盗系统,所以小偷 不会窃取相邻的房屋 。
小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额 。
给你一个整数数组 nums
表示每间房屋存放的现金金额。形式上,从左起第 i
间房屋中放有 nums[i]
美元。
另给你一个整数 k
,表示窃贼将会窃取的 最少 房屋数。小偷总能窃取至少 k
间房屋。
返回小偷的 最小 窃取能力。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 109
1 <= k <= (nums.length + 1)/2
【动态规划 + 二分】
int minCapability(int* nums, int numsSize, int k){
int lower=nums[0],upper=nums[0];
for(int i=0;i<numsSize;i++){
lower=fmin(nums[i],lower);
upper=fmax(nums[i],upper);
}
//取得nums里面的最大值和最小值作为二分的边界
while(lower<=upper){
int mid=(lower+upper)/2;
int cnt=0; //计算有多少个符合条件的房子
bool visit=0; //用于判定上一间房是否被访问过
for(int i=0;i<numsSize;i++){
//当前房子小于mid,且邻居没访问
if(nums[i]<=mid && visit==0){
cnt++;
visit=1;
}
//重置邻居
else{
visit=0;
}
}
if(cnt>=k){
upper=mid-1;
}
else{
lower=mid+1;
}
}
return lower;
}
桌上有 n
堆力扣币,每堆的数量保存在数组 coins
中。我们每次可以选择任意一堆,拿走其中的一枚或者两枚,求拿完所有力扣币的最少次数。
限制:
1 <= n <= 4
1 <= coins[i] <= 10
【贪心】每次都先拿2个币,分奇偶讨论
int minCount(int* coins, int coinsSize){
//n stacks, choose 1 stack and take 1 / 2 coins
int cnt=0;
for(int i=0;i<coinsSize;i++){
if(coins[i]%2==0){
cnt+=(coins[i]/2);
}
else{
cnt+=(coins[i]/2)+1;
}
}
return cnt;
}
给你一个 n
个节点的无向无根树,节点编号从 0
到 n - 1
。给你整数 n
和一个长度为 n - 1
的二维整数数组 edges
,其中 edges[i] = [ai, bi]
表示树中节点 ai
和 bi
之间有一条边。再给你一个长度为 n
的数组 coins
,其中 coins[i]
可能为 0
也可能为 1
,1
表示节点 i
处有一个金币。
一开始,你需要选择树中任意一个节点出发。你可以执行下述操作任意次:
2
以内的所有金币,或者你需要收集树中所有的金币,并且回到出发节点,请你返回最少经过的边数。
如果你多次经过一条边,每一次经过都会给答案加一。
提示:
n == coins.length
1 <= n <= 3 * 104
0 <= coins[i] <= 1
edges.length == n - 1
edges[i].length == 2
0 <= ai, bi < n
ai != bi
edges
表示一棵合法的树。class Solution {
public:
int collectTheCoins(vector<int> &coins, vector<vector<int>> &edges) {
int n = coins.size();
vector<vector<int>> g(n);
vector<int> deg(n);
for (auto &e: edges) {
int x = e[0], y = e[1];
g[x].push_back(y);
g[y].push_back(x); // 建图
deg[x]++;
deg[y]++; // 统计每个节点的度数(邻居个数)
}
int left_edges = n - 1; // 剩余边数
// 拓扑排序,去掉没有金币的子树
vector<int> q;
for (int i = 0; i < n; i++)
if (deg[i] == 1 && coins[i] == 0) // 没有金币的叶子
q.push_back(i);
while (!q.empty()) {
left_edges--; // 删除节点 x(到其父节点的边)
int x = q.back(); q.pop_back();
for (int y: g[x])
if (--deg[y] == 1 && coins[y] == 0) // 没有金币的叶子
q.push_back(y);
}
// 再次拓扑排序
for (int i = 0; i < n; i++)
if (deg[i] == 1 && coins[i]) // 有金币的叶子(判断 coins[i] 是避免把没有金币的叶子也算进来)
q.push_back(i);
left_edges -= q.size(); // 删除所有叶子(到其父节点的边)
for (int x: q) // 遍历所有叶子
for (int y: g[x])
if (--deg[y] == 1) // y 现在是叶子了
left_edges--; // 删除 y(到其父节点的边)
return max(left_edges * 2, 0);
}
};
给你一个整数 money
,表示你总共有的钱数(单位为美元)和另一个整数 children
,表示你要将钱分配给多少个儿童。
你需要按照如下规则分配:
1
美元。4
美元。请你按照上述规则分配金钱,并返回 最多 有多少个儿童获得 恰好 8
美元。如果没有任何分配方案,返回 -1
。
提示:
1 <= money <= 200
2 <= children <= 30
【贪心】
int distMoney(int money, int children){
//钱不够分
if(money<children) return -1;
//每个人先分1元
money-=children;
//给尽可能多的人7元
int cnt=fmin(money/7,children);
//剩余的钱和孩子
money-=cnt*7;
children-=cnt;
//如果孩子都有8元,且还剩钱,则把剩下的钱都给一个孩子
//如果孩子还剩1个,且钱剩3元,则这个孩子会是4元,得找一个8元孩子换钱
if((children==0 && money>0)||(children==1 && money==3)){
cnt--;
}
return cnt;
}
给你一棵 n
个节点的树,编号从 0
到 n - 1
,以父节点数组 parent
的形式给出,其中 parent[i]
是第 i
个节点的父节点。树的根节点为 0
号节点,所以 parent[0] = -1
,因为它没有父节点。你想要设计一个数据结构实现树里面对节点的加锁,解锁和升级操作。
数据结构需要支持如下函数:
请你实现 LockingTree
类:
LockingTree(int[] parent)
用父节点数组初始化数据结构。lock(int num, int user)
如果 id 为 user
的用户可以给节点 num
上锁,那么返回 true
,否则返回 false
。如果可以执行此操作,节点 num
会被 id 为 user
的用户 上锁 。unlock(int num, int user)
如果 id 为 user
的用户可以给节点 num
解锁,那么返回 true
,否则返回 false
。如果可以执行此操作,节点 num
变为 未上锁 状态。upgrade(int num, int user)
如果 id 为 user
的用户可以给节点 num
升级,那么返回 true
,否则返回 false
。如果可以执行此操作,节点 num
会被 升级 。提示:
n == parent.length
2 <= n <= 2000
i != 0
,满足 0 <= parent[i] <= n - 1
parent[0] == -1
0 <= num <= n - 1
1 <= user <= 104
parent
表示一棵合法的树。lock
,unlock
和 upgrade
的调用 总共 不超过 2000
次。【DFS】
typedef struct {
int nodeSize;
int *parent;
int *lockNodeUser;
struct ListNode **children;
} LockingTree;
struct ListNode *createListNode(int val){
struct ListNode *obj=(struct ListNode*)malloc(sizeof(struct ListNode));
obj->val=val;
obj->next=NULL;
return obj;
}
LockingTree* lockingTreeCreate(int* parent, int parentSize) {
LockingTree *obj=(LockingTree*)malloc(sizeof(LockingTree));
obj->nodeSize=parentSize;
obj->parent=(int*)malloc(sizeof(int)*parentSize);
obj->lockNodeUser=(int*)malloc(sizeof(int)*parentSize);
obj->children=(struct ListNode**)malloc(sizeof(struct ListNode*)*parentSize);
memcpy(obj->parent,parent,sizeof(int)*parentSize);
for(int i=0;i<parentSize;i++){
obj->lockNodeUser[i]=-1;
obj->children[i]=NULL;
}
for(int i=0;i<parentSize;i++){
int p=parent[i];
if(p!=-1){
struct ListNode *node=createListNode(i);
node->next=obj->children[p];
obj->children[p]=node;
}
}
return obj;
}
//可以用一个数组变量lockNodeUser记录给各个节点上锁的用户
bool lockingTreeLock(LockingTree* obj, int num, int user) {
if(obj->lockNodeUser[num]==-1){
obj->lockNodeUser[num]=user;
return 1;
}
return 0;
}
//通过比较变量lockNodeUser[num]和user是否先等来判断当前节点是否可以解锁,通过赋值来解锁。
bool lockingTreeUnlock(LockingTree* obj, int num, int user) {
if(obj->lockNodeUser[num]==user){
obj->lockNodeUser[num]=-1;
return 1;
}
return 0;
}
bool hasLockedAncestor(LockingTree *obj,int num){
num=obj->parent[num];
while(num!=-1){
if(obj->lockNodeUser[num]!=-1){
return 1;
}
num=obj->parent[num];
}
return 0;
}
bool checkAndUnlockDescendant(LockingTree *obj,int num){
bool res=obj->lockNodeUser[num]!=-1;
obj->lockNodeUser[num]=-1;
for(struct ListNode *node=obj->children[num];node;node=node->next){
res |=checkAndUnlockDescendant(obj,node->val);
}
return res;
}
bool lockingTreeUpgrade(LockingTree* obj, int num, int user) {
bool res=obj->lockNodeUser[num]==-1 \
&& !hasLockedAncestor(obj, num) \
&& checkAndUnlockDescendant(obj, num);
if (res) {
obj->lockNodeUser[num] = user;
}
return res;
}
void freeList(struct ListNode *list) {
while (list) {
struct ListNode *cur = list;
list = list->next;
free(cur);
}
}
void lockingTreeFree(LockingTree* obj) {
free(obj->parent);
free(obj->lockNodeUser);
for (int i = 0; i < obj->nodeSize; i++) {
freeList(obj->children[i]);
}
free(obj);
}
/**
* Your LockingTree struct will be instantiated and called as such:
* LockingTree* obj = lockingTreeCreate(parent, parentSize);
* bool param_1 = lockingTreeLock(obj, num, user);
* bool param_2 = lockingTreeUnlock(obj, num, user);
* bool param_3 = lockingTreeUpgrade(obj, num, user);
* lockingTreeFree(obj);
*/
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache
类:
LRUCache(int capacity)
以 正整数 作为容量 capacity
初始化 LRU 缓存int get(int key)
如果关键字 key
存在于缓存中,则返回关键字的值,否则返回 -1
。void put(int key, int value)
如果关键字 key
已经存在,则变更其数据值 value
;如果不存在,则向缓存中插入该组 key-value
。如果插入操作导致关键字数量超过 capacity
,则应该 逐出 最久未使用的关键字。函数 get
和 put
必须以 O(1)
的平均时间复杂度运行。
提示:
1 <= capacity <= 3000
0 <= key <= 10000
0 <= value <= 105
2 * 105
次 get
和 put
【灵神题解】146. LRU 缓存 - 力扣(LeetCode)
class Node{
public:
int key,value;
Node *prev,*next;
Node(int k=0,int v=0) : key(k), value(v) {}
};
class LRUCache {
int capacity;
Node *dummy;
unordered_map<int,Node*> key_to_node;
void remove(Node *x){
x->prev->next=x->next;
x->next->prev=x->prev;
}
void push_front(Node *x){
x->prev=dummy;
x->next=dummy->next;
x->prev->next=x;
x->next->prev=x;
}
Node *get_node(int key){
auto it=key_to_node.find(key);
if(it==key_to_node.end()){
//no book
return nullptr;
}
auto node=it->second;
//book exists
remove(node);
push_front(node);
return node;
}
public:
LRUCache(int capacity) {
this->capacity=capacity;
this->dummy=new Node;
dummy->prev=dummy;
dummy->next=dummy;
}
int get(int key) {
auto node=get_node(key);
return node ? node->value : -1;
}
void put(int key, int value) {
auto node=get_node(key);
if(node){
//book exists
node->value=value;
return;
}
key_to_node[key]=node=new Node(key,value);
//new book
push_front(node);
if(key_to_node.size() > capacity){
//to many books
auto back_node=dummy->prev;
key_to_node.erase(back_node->key);
remove(back_node);
delete back_node;
}
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。
实现 LFUCache
类:
LFUCache(int capacity)
- 用数据结构的容量 capacity
初始化对象int get(int key)
- 如果键 key
存在于缓存中,则获取键的值,否则返回 -1
。void put(int key, int value)
- 如果键 key
已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 capacity
时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近最久未使用 的键。为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。
当一个键首次插入到缓存中时,它的使用计数器被设置为 1
(由于 put 操作)。对缓存中的键执行 get
或 put
操作,使用计数器的值将会递增。
函数 get
和 put
必须以 O(1)
的平均时间复杂度运行。
提示:
1 <= capacity <= 104
0 <= key <= 105
0 <= value <= 109
2 * 105
次 get
和 put
方法【灵神题解】460. LFU 缓存 - 力扣(LeetCode)
class Node {
public:
int key, value, freq = 1; // 新书只读了一次
Node *prev, *next;
Node(int k = 0, int v = 0) : key(k), value(v) {}
};
class LFUCache {
private:
int min_freq;
int capacity;
unordered_map<int, Node*> key_to_node;
unordered_map<int, Node*> freq_to_dummy;
Node *get_node(int key) {
auto it = key_to_node.find(key);
if (it == key_to_node.end()) { // 没有这本书
return nullptr;
}
auto node = it->second; // 有这本书
remove(node); // 把这本书抽出来
auto dummy = freq_to_dummy[node->freq];
if (dummy->prev == dummy) { // 抽出来后,这摞书是空的
freq_to_dummy.erase(node->freq); // 移除空链表
delete dummy; // 释放内存
if (min_freq == node->freq) {
min_freq++;
}
}
push_front(++node->freq, node); // 放在右边这摞书的最上面
return node;
}
// 创建一个新的双向链表
Node *new_list() {
auto dummy = new Node(); // 哨兵节点
dummy->prev = dummy;
dummy->next = dummy;
return dummy;
}
// 在链表头添加一个节点(把一本书放在最上面)
void push_front(int freq, Node *x) {
auto it = freq_to_dummy.find(freq);
if (it == freq_to_dummy.end()) { // 这摞书是空的
it = freq_to_dummy.emplace(freq, new_list()).first;
}
auto dummy = it->second;
x->prev = dummy;
x->next = dummy->next;
x->prev->next = x;
x->next->prev = x;
}
// 删除一个节点(抽出一本书)
void remove(Node *x) {
x->prev->next = x->next;
x->next->prev = x->prev;
}
public:
LFUCache(int capacity) : capacity(capacity) {}
int get(int key) {
auto node = get_node(key);
return node ? node->value : -1;
}
void put(int key, int value) {
auto node = get_node(key);
if (node) { // 有这本书
node->value = value; // 更新 value
return;
}
if (key_to_node.size() == capacity) { // 书太多了
auto dummy = freq_to_dummy[min_freq];
auto back_node = dummy->prev; // 最左边那摞书的最下面的书
key_to_node.erase(back_node->key);
remove(back_node); // 移除
delete back_node; // 释放内存
if (dummy->prev == dummy) { // 这摞书是空的
freq_to_dummy.erase(min_freq); // 移除空链表
delete dummy; // 释放内存
}
}
key_to_node[key] = node = new Node(key, value); // 新书
push_front(1, node); // 放在「看过 1 次」的最上面
min_freq = 1;
}
};
/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/