一.TopK
常见解法:heap(max,min),quickSelect,bucket sort
follow up:
1.k,n - k优化
2.重复数据很多怎么办
3.online/offline algorithm?online = stream
4.query次数特别多,不同k值
5.大数据无法放入内存处理
6.系统设计无限扩展
二.Jump Game
Jump game1:DP 或者 Greedy
Jump game2:DP 或者 Greedy
Jump game3:BFS 或者 DFS
Jump game4:单向BFS 或者 双向BFS
Jump game5:DP
Jump game6:Sliding window + PQ 或者 Sliding window + 单调队列
Jump game7:DP 或者 单调队列
Frog jump:DP 或者 更优化的剪枝DP
其中 dfs + memo 的 dp 最好想到实现,最难的是单调队列
注意判断不同的选择和题目起点终点的要求
三.Parentheses 括号
1.Rolling state的思路,类似扫描线数飞机,我们遇到左括号 +1,右括号 -1,来判断是否平衡。中间任意时候平衡被破坏都invalid
ps:由于括号的特殊性,不同类型或者找invalid的括号index的时候,需要用到stack来辅助保存当下的状态
stack可以用来保存:①不同类型括号 ②括号index ③前一层的数字和
2.backtracking类似subset,生成括号
四.Majority Number
什么是摩尔投票法?Boyer-Moore Voting
该算法时间复杂度为O(n),空间复杂度为O(1)。只需要对原数组进行两趟扫描,并且简单易实现。第一趟扫描我们得到一个候选节点candidate,第二趟扫描我们判断candidate出现的次数是否大于n/2
第一趟扫描中,我们需要记录2个值:
1.candidate,初值可以为任何数
2.count,初值为0
之后,对于数组中的每一个元素,首先判断count是否为0,若为0,则把candidate设置为当前元素,之后判断candidate是否与当前元素相等,若相等则count += 1,否则count -= 1
python代码:
candidate = 0
count = 0
for value in input:
if count == 0:
candidate = value
if candidate == value:
count += 1
else:
count -= 1
在第一趟扫描结束后,如果数组中存在多数元素,那么candidate即为其值,如果原数组不存在多数元素,则candidate的值没有意义。所以需要第二趟扫描来统计candidate出现的次数来判断其是否为多数元素
总结
1.遇到目标count++,不是目标count--,count为0替换当前目标
2.记得最后对最后剩下的目标全局再次计数以确保K众数存在
3.对于大数据统计可以parallel优化
五.Two Sum
最高频的是 2 sum,O(n)最快hashmap
2 sum bst 相关考虑iterator可以节省空间
3 sum系列,要用到two pointer双指针
4 sum系列,开始和 k sum类似,如果只问count,可以考虑简化为2 sum或者用dp背包来数数
k sum,如果是问具体内部取了哪些数字,我们就用backtracking去展开所有的组合,暴力2^N*k去做;用recursive去ksum变成(k-1)sum,一直到剩下最后的2个数字用2sum,最后时间复杂度为N^(k-1)次一般更快当k小N大的时候
two pointer:时间慢一些,便于去重,找相近答案。可以集合iterator处理树类
hashmap/set:只可以找确定的target,速度快,不便于去重
六.Palindrome回文
回文数是指正序(从左到右)和倒序(从右到左)读都是一样的数字integer,字符串string,或者字符序列subsequence
注意回文数可以长度为奇数或者偶数,单个字符也是回文
回文知识点:DP,中心扩散,暴力,greedy,backtracking (math,kmp,rolling hash)
总结
1.palindrome一般可以双指针,或者reverse来做比较
2.palindrome substring一般可以DP或者中心扩散法,马拉车不要求。DP的时候如果选择右上三角形,只和左下角的前一个状态有关,因为palindrome一次比较边上两个character
3.palindrome subsequence,熟悉LCS可以直接用LCS,不用LCS也可以直接DP,和左下(match),左,下(not match)相关
4.backtracking的话直接按照模板写,可以DP做预先优化来判断substring(i,j) 是否为palindrome
模板
1.中心扩散法
private boolean isPalindrome(String s) {
int left = 0, right = s.length() - 1;
while (left < right) {
if (s.charAt(left) == s.charAt(right)) {
left++;
right--;
} else {
return false;
}
}
return true;
}
2.常规DP
public String longestPalindrome(String s) {
int N = s.length();
boolean[][] dp = new boolean[N][N];
int start = 0, end = 0;
for (int i = N - 1; i >= 0; i--)
for (int j = i; j < N; j++)
if (s.charAt(i) == s.charAt(j) && (j - i <= 2 || dp[i + 1][j - 1])) {
if (end - start < j - i) {
start = i;
end = j;
}
dp[i][j] = true;
}
return s.substring(start, end + 1);
}
七.买卖股票
State machine(状态机)
选择有:buy,sell,(rest)
状态有:当前天数 i
目前为止最多交易次数 k
当前的持有状态:1表示持有,0表示没有持有
题目要求cool down我们买的时候可以根据前天 i - 2 的状态来保持冷却日
这个问题的状态有三个:
1.天数(到第几天的时候)
2.允许交易的最大次数
3.当前的持有状态(即rest的状态,我们不妨用1表示持有,0表示没有持有)
八.calculator
1.用栈来处理 + - * /
2.用递归来处理 '(',')'
3.Java可以用 Character.isDigit() 或者 '0' <= char <= '9'来检查是否是一个数字
4.建议同时使用stack和递归
int i = 0;
public int calculator(String s) {
Stack<Integer> stack = new Stack<>();
char operator = '+';
int num = 0;
while (i < s.length()) {
char c = s.charAt(i++);
if (Character.isDigit(c)) num = num * 10 + (c - '0');
if (c == '(') num = calculate(s);
if (i >= s.length() || c == '+' || c == '-' || c == '*' || c == '/' || c == ')') {
if (operator == '+') stack.push(-num);
else if (operator == '-') stack.push(stack.pop() * num);
else if (operator == '*') stack.push(stack.pop() / num);
operator = c;
num = 0;
}
if (c == ')') break;
}
}
如果想将空间压缩成 O(1)
class Solution {
int i = -1;
public int calculate(String s) {
int ans = 0,bef = 0;
char op = '+';
int num = 0;
while (i < s.length() - 1) {
char ch = s.charAt(++i);
if (ch == '(') num = calculate(s);
if (cha >= '0' && ch <= '9') num = num * 10 + (ch - '0');
if (i == s.length() - 1 || ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == ')') {
if (op == '+') {
ans += bef;
bef = num;
} else if (op == '-') {
ans += bef;
bef =- num;
} else if (op == '*') {
bef *= num;
} else {
bef /= num;
}
op = ch;
num = 0;
}
if (ch == ')') break;
}
if (ch == ')') break;
}
return ans + bef;
}
九.number of island
考察的知识点
1.DFS
2.BFS
3,Union Find
4.Path to describe shape
5.Tarjan's Algorithm
6.Distributed processing as a follow up
总结
1.DFS,BFS,Union Find必须熟悉,stack和queue
2.Trajan's考到的概率极低
void dfs(int[][] grid, int r, int c) {
//判断 base case
if (!inArea(grid, r, c)) {
return;
}
//如果这个格子不是岛屿,直接返回
if (grid[r][c] != 1) {
return;
}
grid[r][c] = 2; //将格子标记为'已遍历过'
//访问上,下,左,右四个相邻节点
dfs(grid, r-1, c);
dfs(grid, r+1, c);
dfs(grid, r, c-1);
dfs(grid, r, c+1);
}
//判断坐标(r,c)是否在网格中
boolean inArea(int[][] grid, int r, int c) {
return 0 <= r && r < grid.length && 0 <= c && c < grid[0].length;
}