目录
贪心算法
分糖果问题
题目
一群孩子做游戏,现在请你根据游戏得分来发糖果,要求如下:
-
每个孩子不管得分多少,起码分到一个糖果。
-
任意两个相邻的孩子之间,得分较多的孩子必须拿多一些糖果。(若相同则无此限制)
给定一个数组 𝑎𝑟𝑟arr 代表得分数组,请返回最少需要多少糖果。
要求: 时间复杂度为 𝑂(𝑛) 空间复杂度为 𝑂(𝑛)
数据范围: 1≤𝑛≤100000 ,1≤𝑎𝑖≤1000
示例1
输入:[1,1,2]
返回值:4
说明:最优分配方案为1,1,2
示例2
输入:[1,1,1]
返回值:3
说明:最优分配方案是1,1,1
代码
java
java
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param S string字符串
# @param T string字符串
# @return string字符串
#
import collections
class Solution:
def minWindow(self , S: str, T: str) -> str:
# write code here
if(len(T)>len(S)):
return ""
need=len(T)
cnt = collections.Counter(T)
start=0
end=-1
left=0
right=0
minl=len(S)+1
while right<len(S):
ch=S[right]
if ch in cnt:
if cnt[ch]>0:
need-=1
cnt[ch]-=1
while need==0:
if right-left+1<minl:
minl=right-left+1
start=left
end=right
ch1=S[left]
if ch1 in cnt:
if cnt[ch1] >= 0: # 对当前字符ch还有需求,或刚好无需求(其实此时只有=0的情况)
need += 1 # 此时滑出窗口的ch会对need有影响
cnt[ch1] += 1
left += 1
right+=1
return S[start:end+1]
python
python
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# pick candy
# @param arr int整型一维数组 the array
# @return int整型
#
class Solution:
def candy(self , arr: List[int]) -> int:
# write code here
candy=[1]*len(arr)
result=0
for i in range(1,len(arr)):
if arr[i]>arr[i-1]:
candy[i]=candy[i-1]+1
for j in range(len(arr)-2,-1,-1):
if arr[j]>arr[j+1]:
candy[j]=max(candy[j],candy[j+1]+1)
for c in candy:
result+=c
return result
主持人调度(二)
题目
有 n 个活动即将举办,每个活动都有开始时间与活动的结束时间,第 i 个活动的开始时间是 starti ,第 i 个活动的结束时间是 endi ,举办某个活动就需要为该活动准备一个活动主持人。
一位活动主持人在同一时间只能参与一个活动。并且活动主持人需要全程参与活动,换句话说,一个主持人参与了第 i 个活动,那么该主持人在 (starti,endi) 这个时间段不能参与其他任何活动。求为了成功举办这 n 个活动,最少需要多少名主持人。
数据范围: 1≤𝑛≤105, −232≤𝑠𝑡𝑎𝑟𝑡𝑖≤𝑒𝑛𝑑𝑖≤231−1
复杂度要求:时间复杂度 𝑂(𝑛log𝑛),空间复杂度 𝑂(𝑛)
示例1
输入:2,[[1,2],[2,3]]
返回值:1
说明:只需要一个主持人就能成功举办这两个活动
示例2
输入:2,[[1,3],[2,4]]
返回值:2
说明:需要两个主持人才能成功举办这两个活动
备注:1≤𝑛≤1051≤n≤105
𝑠𝑡𝑎𝑟𝑡𝑖,𝑒𝑛𝑑𝑖starti,endi在int范围内
分析
如果当前活动开始时,最早空闲的主持人已经闲了 那么复用他,不然新增加一个主持人
代码
java
java
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 计算成功举办活动需要多少名主持人
* @param n int整型 有n个活动
* @param startEnd int整型二维数组 startEnd[i][0]用于表示第i个活动的开始时间,startEnd[i][1]表示第i个活动的结束时间
* @return int整型
*/
public int minmumNumberOfHost (int n, int[][] startEnd) {
// write code here
Arrays.sort(startEnd,(a,b) ->Integer.compare(a[0],b[0]));
int[] end=new int[n];
for(int i=0;i<n;i++){
end[i]=startEnd[i][1];
}
Arrays.sort(end);
int count=0;
int endIdx=0;
for(int i=0;i<n;i++){
int s = startEnd[i][0];
if (s >= end[endIdx]) {
endIdx++; // 复用主持人
}else{
count++;
}
}
return count;
}
}
python
python
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 计算成功举办活动需要多少名主持人
# @param n int整型 有n个活动
# @param startEnd int整型二维数组 startEnd[i][0]用于表示第i个活动的开始时间,startEnd[i][1]表示第i个活动的结束时间
# @return int整型
#
class Solution:
def minmumNumberOfHost(self , n: int, startEnd: List[List[int]]) -> int:
# write code here
start = []
end = []
for s, e in startEnd:
start.append(s)
end.append(e)
# 分别排序!这是最关键的一步
start.sort()
end.sort()
count = 0 # 需要的主持人数
end_idx = 0 # 指向最早空闲的主持人
for s in start:
# 如果当前活动开始时,最早的主持人已经闲了
if s >= end[end_idx]:
end_idx += 1 # 复用这个主持人
else:
count += 1 # 不够,加人
return count
模拟
旋转数组
题目
一个数组A中存有 n 个整数,在不允许使用另外数组的前提下,将每个整数循环向右移 M( M >=0)个位置,即将A中的数据由(A0 A1 ......AN-1 )变换为(AN-M ...... AN-1 A0 A1 ......AN-M-1 )(最后 M 个数循环移至最前面的 M 个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?
数据范围:0<𝑛≤100,0≤𝑚≤1000
进阶:空间复杂度 𝑂(1),时间复杂度 𝑂(𝑛)
示例1
输入:6,2,[1,2,3,4,5,6]
返回值:[5,6,1,2,3,4]
示例2
输入:4,0,[1,2,3,4]
返回值:[1,2,3,4]
备注:(1<=N<=100,M>=0)
代码
java
java
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 旋转数组
* @param n int整型 数组长度
* @param m int整型 右移距离
* @param a int整型一维数组 给定数组
* @return int整型一维数组
*/
public int[] solve (int n, int m, int[] a) {
// write code here
m = m % n;
reverse(a,0,n-1);
reverse(a,0,m-1);
reverse(a,m,n-1);
return a;
}
public void reverse(int[] a,int start,int end){
while(start<=end){
int temp=a[start];
a[start]=a[end];
a[end]=temp;
start++;
end--;
}
}
}
python
python
from re import A
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 旋转数组
# @param n int整型 数组长度
# @param m int整型 右移距离
# @param a int整型一维数组 给定数组
# @return int整型一维数组
#
class Solution:
def solve(self , n: int, m: int, a: List[int]) -> List[int]:
# write code here
m%=len(a)
self.reverse(a,0,len(a)-1)
self.reverse(a,0,m-1)
self.reverse(a,m,len(a)-1)
return a
def reverse(self,nums:List[int],start:int,end:int) ->None:
while start<=end:
temp=nums[start]
nums[start]=nums[end]
nums[end]=temp
start+=1
end-=1
螺旋矩阵
题目
给定一个m x n大小的矩阵(m行,n列),按螺旋的顺序返回矩阵中的所有元素。
数据范围:0≤𝑛,𝑚≤10,矩阵中任意元素都满足 ∣𝑣𝑎𝑙∣≤100
要求:空间复杂度 𝑂(𝑛𝑚),时间复杂度 𝑂(𝑛𝑚)
示例1
输入:[[1,2,3],[4,5,6],[7,8,9]]
返回值:[1,2,3,6,9,8,7,4,5]
示例2
输入:[]
返回值:[]
代码
java
java
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param matrix int整型二维数组
* @return int整型ArrayList
*/
public ArrayList<Integer> spiralOrder (int[][] matrix) {
// write code here
ArrayList<Integer> res= new ArrayList<>();
if (matrix == null || matrix.length <= 0 || matrix[0].length <= 0) {
return res;
}
int left = 0, right = matrix[0].length - 1, top = 0, bottom = matrix.length - 1;
while (true) {
for (int i = left; i <= right; i++) {
res.add(matrix[top][i]) ;
}
if (++top > bottom) break;
for (int j = top; j <= bottom; j++) {
res.add(matrix[j][right]) ;
}
if (left > --right) break;
for (int i = right; i >= left; i--) {
res.add(matrix[bottom][i]);
}
if (top > --bottom) break;
for (int j = bottom; j >= top; j--) {
res.add(matrix[j][left]) ;
}
if (++left > right) {
break;
}
}
return res;
}
}
python
python
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param matrix int整型二维数组
# @return int整型一维数组
#
class Solution:
def spiralOrder(self , matrix: List[List[int]]) -> List[int]:
# write code here
list=[]
if len(matrix)==0 or len(matrix[0])==0:
return list
m=len(matrix)
n=len(matrix[0])
left=0
right=n-1
top=0
bottom=m-1
while(1):
for i in range(left,right+1):
list.append(matrix[top][i])
top+=1
if top>bottom:
break
for j in range(top,bottom+1):
list.append(matrix[j][right])
right-=1
if left>right:
break
for i in range(right,left-1,-1):
list.append(matrix[bottom][i])
bottom-=1
if top>bottom:
break
for j in range(bottom,top-1,-1):
list.append(matrix[j][left])
left+=1
if left>right:
break
return list
顺时针旋转矩阵
题目
有一个NxN整数矩阵,请编写一个算法,将矩阵顺时针旋转90度。
给定一个NxN的矩阵,和矩阵的阶数N,请返回旋转后的NxN矩阵。
数据范围:0<𝑛<300,矩阵中的值满足 0≤𝑣𝑎𝑙≤1000
要求:空间复杂度 𝑂(𝑁2),时间复杂度 𝑂(𝑁2)
进阶:空间复杂度 𝑂(1),时间复杂度 𝑂(𝑁2)
示例1
输入:[[1,2,3],[4,5,6],[7,8,9]],3
返回值:[[7,4,1],[8,5,2],[9,6,3]]
分析
矩阵转置:沿着主对角线(左上→右下)交换元素
每行反转:将每一行的元素左右对称反转
代码
java
java
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param mat int整型二维数组
* @param n int整型
* @return int整型二维数组
*/
public int[][] rotateMatrix (int[][] mat, int n) {
// write code here
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
int temp=mat[i][j];
mat[i][j]=mat[j][i];
mat[j][i]=temp;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n/2;j++){
int temp=mat[i][j];
mat[i][j]=mat[i][n-1-j];
mat[i][n-1-j]=temp;
}
}
return mat;
}
}
python
python
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param mat int整型二维数组
# @param n int整型
# @return int整型二维数组
#
class Solution:
def rotateMatrix(self , mat: List[List[int]], n: int) -> List[List[int]]:
# write code here
for i in range(n):
for j in range(i):
mat[i][j],mat[j][i]=mat[j][i],mat[i][j]
for i in range(n):
for j in range(n//2):
mat[i][j],mat[i][n-1-j]=mat[i][n-1-j],mat[i][j]
return mat
设计LRU缓存结构
题目
设计LRU(最近最少使用)缓存结构,该结构在构造时确定大小,假设大小为 capacity ,操作次数是 n ,并有如下功能:
-
Solution(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
-
get(key):如果关键字 key 存在于缓存中,则返回key对应的value值,否则返回 -1 。
-
set(key, value):将记录(key, value)插入该结构,如果关键字 key 已经存在,则变更其数据值 value,如果不存在,则向缓存中插入该组 key-value ,如果key-value的数量超过capacity,弹出最久未使用的key-value
提示:
1.某个key的set或get操作一旦发生,则认为这个key的记录成了最常使用的,然后都会刷新缓存。
2.当缓存的大小超过capacity时,移除最不经常使用的记录。
3.返回的value都以字符串形式表达,如果是set,则会输出"null"来表示(不需要用户返回,系统会自动输出),方便观察
4.函数set和get必须以O(1)的方式运行
5.为了方便区分缓存里key与value,下面说明的缓存里key用""号包裹
数据范围:
1≤𝑐𝑎𝑝𝑎𝑐𝑖𝑡𝑦<=1051
0≤𝑘𝑒𝑦,𝑣𝑎𝑙≤2×109
1≤𝑛≤105
示例1
输入:["set","set","get","set","get","set","get","get","get"],[[1,1],[2,2],[1],[3,3],[2],[4,4],[1],[3],[4]],2
返回值:["null","null","1","null","-1","null","-1","3","4"]
说明:我们将缓存看成一个队列,最后一个参数为2代表capacity,所以 Solution s = new Solution(2); s.set(1,1); //将(1,1)插入缓存,缓存是{"1"=1},set操作返回"null" s.set(2,2); //将(2,2)插入缓存,缓存是{"2"=2,"1"=1},set操作返回"null" output=s.get(1);// 因为get(1)操作,缓存更新,缓存是{"1"=1,"2"=2},get操作返回"1" s.set(3,3); //将(3,3)插入缓存,缓存容量是2,故去掉某尾的key-value,缓存是{"3"=3,"1"=1},set操作返回"null" output=s.get(2);// 因为get(2)操作,不存在对应的key,故get操作返回"-1" s.set(4,4); //将(4,4)插入缓存,缓存容量是2,故去掉某尾的key-value,缓存是{"4"=4,"3"=3},set操作返回"null" output=s.get(1);// 因为get(1)操作,不存在对应的key,故get操作返回"-1" output=s.get(3);//因为get(3)操作,缓存更新,缓存是{"3"=3,"4"=4},get操作返回"3" output=s.get(4);//因为get(4)操作,缓存更新,缓存是{"4"=4,"3"=3},get操作返回"4"
分析
也就是最近最少使用。要记录使用次数,map来
代码
java
java
import java.util.*;
public class Solution {
private static class Node {
int key, value;
Node prev, next;
Node(int k, int v) {
key = k;
value = v;
}
}
private final int capacity;
private final Node dummy = new Node(0, 0); // 哨兵节点
private final Map<Integer, Node> keyToNode = new HashMap<>();
public Solution(int capacity) {
// write code here/
this.capacity = capacity;
dummy.prev = dummy;
dummy.next = dummy;
}
public int get(int key) {
// write code here
Node node = getNode(key);
return node != null ? node.value : -1;
}
public void set(int key, int value) {
// write code here
Node node = getNode(key);
if (node != null) { // 有这本书
node.value = value; // 更新 value
return;
}
node = new Node(key, value); // 新书
keyToNode.put(key, node);
pushFront(node); // 放在最上面
if (keyToNode.size() > capacity) { // 书太多了
Node backNode = dummy.prev;
keyToNode.remove(backNode.key);
remove(backNode); // 去掉最后一本书
}
}
private Node getNode(int key) {
if (!keyToNode.containsKey(key)) { // 没有这本书
return null;
}
Node node = keyToNode.get(key); // 有这本书
remove(node); // 把这本书抽出来
pushFront(node); // 放在最上面
return node;
}
// 删除一个节点(抽出一本书)
private void remove(Node x) {
x.prev.next = x.next;
x.next.prev = x.prev;
}
// 在链表头添加一个节点(把一本书放在最上面)
private void pushFront(Node x) {
x.prev = dummy;
x.next = dummy.next;
x.prev.next = x;
x.next.prev = x;
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution solution = new Solution(capacity);
* int output = solution.get(key);
* solution.set(key,value);
*/
python
python
class Node:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
class Solution:
def __init__(self, capacity: int):
self.capacity = capacity
self.key_to_node = dict()
# 哨兵节点(双向链表)
self.dummy = Node()
self.dummy.prev = self.dummy
self.dummy.next = self.dummy
def get(self, key: int) -> int:
node = self.get_node(key)
return node.value if node else -1
def set(self, key: int, value: int) -> None:
node = self.get_node(key)
if node:
node.value = value
return
# 新增节点
node = Node(key, value)
self.key_to_node[key] = node
self.push_front(node)
# 超过容量,删除最后一个
if len(self.key_to_node) > self.capacity:
back_node = self.dummy.prev
del self.key_to_node[back_node.key]
self.remove(back_node)
def get_node(self, key):
if key not in self.key_to_node:
return None
node = self.key_to_node[key]
self.remove(node)
self.push_front(node)
return node
# 删除节点
def remove(self, x):
x.prev.next = x.next
x.next.prev = x.prev
# 插入到链表头部
def push_front(self, x):
x.prev = self.dummy
x.next = self.dummy.next
x.prev.next = x
x.next.prev = x
设计LFU缓存结构
题目
一个缓存结构需要实现如下功能。
- set(key, value):将记录(key, value)插入该结构
- get(key):返回key对应的value值
但是缓存结构中最多放K条记录,如果新的第K+1条记录要加入,就需要根据策略删掉一条记录,然后才能把新记录加入。这个策略为:在缓存结构的K条记录中,哪一个key从进入缓存结构的时刻开始,被调用set或者get的次数最少,就删掉这个key的记录;
如果调用次数最少的key有多个,上次调用发生最早的key被删除
这就是LFU缓存替换算法。实现这个结构,K作为参数给出
数据范围:0<𝑘≤105,∣𝑣𝑎𝑙∣≤2×109
要求:get和set的时间复杂度都是 𝑂(𝑙𝑜𝑔𝑛),空间复杂度是 𝑂(𝑛)
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,返回一个答案
示例1
输入:[[1,1,1],[1,2,2],[1,3,2],[1,2,4],[1,3,5],[2,2],[1,4,4],[2,1]],3
返回值:[4,-1]
说明:在执行"1 4 4"后,"1 1 1"被删除。因此第二次询问的答案为-1
备注:1≤𝑘≤𝑛≤1051≤k≤n≤105 −2×109≤𝑥,𝑦≤2×109−2×109≤x,y≤2×109
代码
java
java
import java.util.*;
public class Solution {
/**
* lfu design
* @param operators int整型二维数组 ops
* @param k int整型 the k
* @return int整型一维数组
*/
public int[] LFU (int[][] operators, int k) {
LFUCache lfu = new LFUCache(k);
List<Integer> res = new ArrayList<>();
for (int[] op : operators) {
if (op[0] == 1) { // set 操作
int key = op[1];
int val = op[2];
lfu.put(key, val);
} else if (op[0] == 2) { // get 操作
int key = op[1];
res.add(lfu.get(key));
}
}
// 转成 int[] 返回
int[] ans = new int[res.size()];
for (int i = 0; i < res.size(); i++) {
ans[i] = res.get(i);
}
return ans;
}
}
// 下面直接放你给的 LFUCache 完整代码
class LFUCache {
private static class Node {
int key, value, freq = 1;
Node prev, next;
Node(int key, int value) {
this.key = key;
this.value = value;
}
}
private final int capacity;
private final Map<Integer, Node> keyToNode = new HashMap<>();
private final Map<Integer, Node> freqToDummy = new HashMap<>();
private int minFreq;
public LFUCache(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
Node node = getNode(key);
return node != null ? node.value : -1;
}
public void put(int key, int value) {
Node node = getNode(key);
if (node != null) {
node.value = value;
return;
}
if (keyToNode.size() == capacity) {
Node dummy = freqToDummy.get(minFreq);
Node backNode = dummy.prev;
keyToNode.remove(backNode.key);
remove(backNode);
if (dummy.prev == dummy) {
freqToDummy.remove(minFreq);
}
}
node = new Node(key, value);
keyToNode.put(key, node);
pushFront(1, node);
minFreq = 1;
}
private Node getNode(int key) {
if (!keyToNode.containsKey(key)) {
return null;
}
Node node = keyToNode.get(key);
remove(node);
Node dummy = freqToDummy.get(node.freq);
if (dummy.prev == dummy) {
freqToDummy.remove(node.freq);
if (minFreq == node.freq) {
minFreq++;
}
}
node.freq++;
pushFront(node.freq, node);
return node;
}
private Node newList() {
Node dummy = new Node(0, 0);
dummy.prev = dummy;
dummy.next = dummy;
return dummy;
}
private void pushFront(int freq, Node x) {
Node dummy = freqToDummy.computeIfAbsent(freq, k -> newList());
x.prev = dummy;
x.next = dummy.next;
x.prev.next = x;
x.next.prev = x;
}
private void remove(Node x) {
x.prev.next = x.next;
x.next.prev = x.prev;
}
}
python
python
class Node:
def __init__(self, key=0, val=0):
self.key = key
self.val = val
self.freq = 1
self.prev = None
self.next = None
class Solution:
def LFU(self, operators: List[List[int]], k: int) -> List[int]:
res = []
# 内部LFU结构
class LFUCache:
def __init__(self, capacity):
self.capacity = capacity
self.key_to_node = dict()
self.freq_to_dummy = dict()
self.min_freq = 1
def get(self, key):
if key not in self.key_to_node:
return -1
node = self.key_to_node[key]
self._update(node)
return node.val
def put(self, key, val):
if self.capacity == 0:
return
if key in self.key_to_node:
node = self.key_to_node[key]
node.val = val
self._update(node)
return
if len(self.key_to_node) >= self.capacity:
dummy = self.freq_to_dummy[self.min_freq]
remove_node = dummy.prev
self._remove(remove_node)
del self.key_to_node[remove_node.key]
if dummy.prev == dummy:
del self.freq_to_dummy[self.min_freq]
new_node = Node(key, val)
self.key_to_node[key] = new_node
self._add(new_node, 1)
self.min_freq = 1
def _update(self, node):
old_freq = node.freq
self._remove(node)
dummy = self.freq_to_dummy[old_freq]
if dummy.prev == dummy:
del self.freq_to_dummy[old_freq]
if self.min_freq == old_freq:
self.min_freq += 1
node.freq += 1
self._add(node, node.freq)
def _add(self, node, freq):
if freq not in self.freq_to_dummy:
dummy = Node()
dummy.prev = dummy
dummy.next = dummy
self.freq_to_dummy[freq] = dummy
else:
dummy = self.freq_to_dummy[freq]
node.prev = dummy
node.next = dummy.next
dummy.next.prev = node
dummy.next = node
def _remove(self, node):
node.prev.next = node.next
node.next.prev = node.prev
cache = LFUCache(k)
for op in operators:
if op[0] == 1:
# set 操作
cache.put(op[1], op[2])
else:
# get 操作
res.append(cache.get(op[1]))
return res