孤狗
迎难而上,祝我好运。
2021版本
受欢迎的元素
- 给一个数组,求其中受欢迎的元素(出现频数超过一半)。Map统计即可。
- 若数组排好序了,求出现频数超过n/4的。既然排好序就可以考虑直接通过前后比较来计数了,不需要额外空间。
- follow-up: 优化时间复杂度?排好序就应该想下有没有贪心/挪指针/二分查找了。
1 | public int getPopularElement(int[] sorted) { |
flip一次求最多1的数量
- 给一个硬币面朝上朝下的0/1一维数组,求通过最多一次局部翻转(翻转起止位置自己定)能够得到的最大1的个数。
- 本质上就是求0比1数量多最多的subarray段,类似用贪心做法求max subarray sum。
1 | public int maxSumAfterFlip(int[] coins) { |
- follow-up:如果要记录起止位置?那么就在内层while循环中maxSum需要更新时对start, end赋值即可。
- follow-up:如果将一维数组换成二维matrix,求其中submatrix flip之后最多能得到的1的数量?
2018版本
10. regular-expression-matching
- 输入两个字符串,写个支持
.
和*
的正则表达式判断的method。例如s = "ab"
,p = "a*"
, 为True。 - 目标字符串s和正则字符串p之间相互比较,就需要维护一个boolean的二维数组
dp[i][j]
,表示s[0~i-1]
与p[0~j-1]
是否匹配。
1 | class Solution { |
36. valid-sudoku
- 给一个二维char数组,里面是一个未完成的数独9x9棋盘,要求每一行、每一列、每个3x3方块中数字1-9有且仅有出现一次。判断这个棋盘是否符合数独规则,返回布尔值。
- 方法一:直接双重循环遍历,注意可以利用index的变换同时判断行、列和box的情况
1 | public boolean isValidSudoku(char[][] board) { |
- 方法二:利用String encoding,直接将每个数字出现的行、列、box情况encode成String存入set。
1 | public boolean isValidSudoku(char[][] board) { |
37. sudoku-solver
- 给一个二位char数组,里面是一个未完成的数独9x9棋盘,要求每一行、每一列、每个3x3方块中数字1-9有且仅有出现一次。解出数独,解有且仅有一种,直接在原二维数组中将
.
改为数字的char。 - 暴力dfs,确定一个位置后就进入下一层DFS去判断。时间复杂度为O(9^m),m为可填的空位数。
1 | final String inRow = " in row "; |
41. first-missing-positive
- 给一个乱序整数数组,要求返回所缺正整数中的最小值。要求constant space,O(n).例如
[3,4,-1,1]
,返回2. - 尝试将在合理范围内的正数[1, nums.length]放入对应的位置,即与它的值对应的索引进行swap,注意执行swap之前需要判断该对应处的元素是否已经处在正确的位置,已经是正确位置就不能swap。最后从头遍历一波,第一个放错位置的即为所求。
1 | public int firstMissingPositive(int[] nums) { |
42. trapping-rain-water
- 给一个int数组,其中包含每个索引处墙的高度。求装满水时的横截面的积水的面积。
- 利用双指针,left向后更新maxLeft,right往前更新maxRight。每次在left处和right处的高度中取较小值与maxLeft和maxRight进行比较,如果是凹的就直接累加高度差即可。
1 | public int trap(int[] height) { |
43. multiply-strings
- 给两个字符串形式的int,模拟乘法求他们的积,返回字符串。
- 观察index的关系,num1的第i个数字乘以num2的第j个数字得到的两位数将出现在结果数的第
i + j
和第i + j + 1
位。因此就从最低位开始,双重循环相乘,不断更新结果数组即可。注意数组中保存的是一位数,有进位需要存到前一个位置。
1 | public String multiply(String num1, String num2) { |
44. wildcard-matching
- 和前面的regular-expression-matching很像,但这里用
?
代表任意一个字符、用*
代表任意长度的任意字符而不依赖它前面的字符(而且不只能匹配单一个字符,直接匹配任意长度的任意字符组合)。总的来说比上一题简单,要讨论的情况少了。 - 维护一个二维boolean数组,
dp[i][j]
表示s[0~i-1]
和p[0~j-1]
是否匹配。初始化方面,对于空的p,dp[i][0]
仍是除dp[0][0]
外全部false,不可能用空的p去匹配非空的s;对于空的s,dp[0][j]
就要看当前是否是'*'
且考虑dp[0][j-1]
。接着双重循环更新dp
-> 若当前字符p[j-1]是'*'
,则考虑取p的*
但不匹配s当前字符时,s[0~i-1]和p[0~j-2]
的匹配情况,即dp[i][j-1]
;或将'*'
假设为s[i-1]
那个字符,看看s[0~i-2]
与p[0~i-1]
的匹配情况,即dp[i-1][j]
;或者不取*
也不匹配s当前字符,s[0~i-2]和p[0~j-2]
的匹配情况,即dp[i-1][j-1]
.
-> 若当前字符p[j-1]不是'*'
,就直接看s[i-1]和p[j-1]的匹配情况再结合dp[i-1][j-1]
了。
除了DP,这个题目似乎还可以用贪心给两个字符串分别用一个指针一直向后挪。
1 | public boolean isMatch(String s, String p) { |
56. merge-intervals
- 给一个Interval类的list,将重叠部分进行合并,返回合并之后的list。
- 方法一:使用自定义排序,按照start再按照end排序,然后入栈,每次与栈顶比较。
1 | public List<Interval> merge(List<Interval> intervals) { |
- 方法二:将左、右boundary分别排序,然后快慢指针遍历。当
left[fast + 1] > right[fast]
,说明slow~fast可以组成一个独立的interval,存入list后将慢指针放到fast + 1即可。
1 | public List<Interval> merge(List<Interval> intervals) { |
59. spiral-matrix-ii
- 给一个整数n,生成一个n*n的二维数组方阵,使得螺旋式遍历的结果为1,2,3,…,n^2。
- 直接用循环搞。
1 | public int[][] generateMatrix(int n) { |
72. edit-distance
- 给两个字符串word1, word2,求使用addition, replacement, removement三种操作的情况下最少多少步可以从word1转换成word2。
dp[i][j]
表示当前长度i的子字符串1转换成长度j的子字符串2最少需要的操作数。初始条件显然是dp[0][x] = dp[x][0] = x
。对于任意一个dp[i][j]
,需要由上、左上、左三个方格的计算结果决定,往上dp[i-1][j]
表示一个deletion,往左dp[i][j-1]
表示一个addition,左上dp[i-1][j-1]
表示一个replacement。每次选取三者中最小的,加上1就是当前的最小操作数了。
1 | public int minDistance(String word1, String word2) { |
91. decode-ways
- 给一个纯数字的字符串,看有多少中方式解析成对应的字母A-Z。如
12
可解析为AB
或L
两种,返回2。 - 联想到“跳楼梯的方式”那题,每次可以选择1位数或者2位数,dp[i]表示取i个字符总共有多少decode的方式,那么当前的方式数就取决于前一位或者前两位的方式数。注意如果取两位digit,需要判断是否在10~26范围之内。
1 | public int numDecodings(String s) { |
95. unique-binary-search-trees-ii
- 只给一个整数n表示二分查找树的节点数,返回所有结构不同的树。而上一问(96)则只是输出不同树的个数,只是个小DP。
- 方法一:DP。dp[i]表示节点数为i的时候后所有可能的unique BST根节点。
1 | public List<TreeNode> generateTrees(int n) { |
- 方法二:分治法,分别build左右两边的子树。
1 | public List<TreeNode> generateTrees(int n) { |
OA: Email地址处理
- 给String数组包含email地址,如name@domain,其中name需要将(1)dots(‘.’) between some characters去除 (2)如果有’+’,’+’和后面的全去除。将处理完的email地址归类,统计每个email出现个数,返回里面>1个email兄弟的个数。
1 | public int getEmailCount(String[] emails) { |
OA: 两个篮子取水果
- lc 159变种。只有两个篮子可以放水果,每个篮子只能放一样水果,但可以放无数个相同的水果。从任意一个位置开始往右取,求最多取多少水果。
- 滑动窗口。
OA: 上一个最接近时间
- 类似于lc 681. 给一个时间string如
hh:mm
,求前一个最近的时间,可以重复使用已有的数字。 - 将所有出现过的数字存入TreeSet,从最低位开始尝试将当前数字替换成比他小的最大的数,若存在这样的数就是previou closest time了,若没有这样的数则需要替换成最大值,注意需要validate。
1 | public static String prevClosestTime(String time) { |
OA: 开花时间
- 给一个数组,index表示开花时间,element表示开花的位置,第i天arr[i]位置的花会开,为了维持M个group,问最晚的天数k。
- 考虑逆向处理,这样就可以保证先找到是最晚天数。在最后一天所有的花都开了,使用TreeSet存放没有开的花的index,往前找最大的天数、往后找最小的天数,若前后都间隔了>= k,说明前后都满足连续>= k朵花开放的条件,这样clusterCount++。但是若当前没有开导致前后的小于k,就需要减少clusterCount。当clusterCount == m时就是最晚的、有m个至少k朵花开放的天数。
1 | public static int getDay(int[] flowers, int k, int m) { |
以下是实习面经
求有序数组的平方数
- 给一个有序数组,求其中各项平方后的结果并排好序。
- 方法一:双指针,一个从0开始往后,一个从最后一个元素往前,比较大小然后从后往前地存入结果数组。
O(N)
。 - 方法二:从前往后,找到第一个非负数开始,往前后两个方向merge,搜索部分复杂度
O(N)
,merge部分复杂度O(N)
。 - 方法三:既然是有序,那么查找的时候就用二分查找,找『0插入的地方』,然后往两边merge。搜索部分复杂度
O(logN)
,merge部分复杂度O(N)
。再来复习一遍二分查找找第一个出现/插入的位置:
1 | private int binarySearch(int[] nums, int target) { |
- follow-up:这次是求
x ^ 2 + a * x
而不单单是平方了。 - 思路也是类似,找到函数的最小值,往两边merge,这里对称轴出现在
-a/2
,所以找到这个值之后往两边merge即可。
String字符交换
- 给两个字符,问能否交换str1的两个字符得到str2.
- 需要考虑两个字符串本身是否相等?长度小于2?
- 方法一:从前往后遍历两个数组,把不同的位置拼到List里,然后判断不同的是否恰好两位且可以互换。
1 | private boolean convert(String s1, String s2) { |
- 方法二:优化空间,不用额外的List,而是直接用一个变量记录第一个不同的索引,之后再出现不同直接判断就可以了。
1 | private boolean convert(String s1, String s2) { |
同义词 734 737
- 给一堆同义词[(“restaurant”, “cafe”), (“ratings”, “reviews”), …],再给一些queries[(“restaurant ratings”, “cafe reviews”), …],要求返回每个query里的对应词是否都是synonym。
- 如果不需要考虑传递性,就直接把字符作为key、对应的所有同义词的set作为value存入map,正反都放一次,比如
map.get("restaurant").add("cafe")), map.get("restaurant").add("cafe")
,这样在query的时候就可以直接调用了。
1 | // 维护一个总的map,每个单词作为key,对等的单词塞入它维护的Set中 |
- 需要考虑是否可以传递,比如
a=b, b=c -> a=c?
,可能就需要有个并查集找共同老大。或者直接通过DFS遍历所有可达点。
1 | // 和前一个版本的区别是这个可以无限传递a=b=c=d... |
- 或者通过并查集,初始化时每个单词都是自己的root;然后根据同义词关系将前者的老大设为后者。判断句子是否同义时就找两个单词的老大是否相等即可
1 | // 并查集。初始化时每个单词都是自己的root;然后根据同义词关系将前者的老大设为后者。 |
flip game 293 294
- 给一个只含有
+
和-
的字符串,两个人每次选择一个++
flip成--
。 - 第一问:求下一步所有可能的情况。直接找连续出现的
++
然后取前面部分的substring拼上--
再拼上后续部分即可。 - 第二问:问先开始的玩家能否保证赢(操作后再也没有
++
让对方没法再flip)。暴力的方法是从头开始循环找到每个startsWith("++")
,然后替换成--
再递归判断对方能否稳赢。一旦对方没法稳赢,就直接return了。但是复杂度略高。可以用Set或者Map缓存中间结果,避免重复递归。
1 | class Solution { |
首个prefix不匹配字符串
- 给出一个按字典序排好序的字符串数组dictionary,找出其中第一个不是以指定字符串prefix作为前缀的字符串。如
[aa, aaa, ax, b, c], aa -> ax
- 看到“排好序”,“找”,就想到二分查找了。
1 | class Google { |
flatten iterator 251
- 给一个二维List,要求模拟一维iterator,用hasNext和next从头到尾遍历。
- 方法一: 第一版的两个iterator遍历的方法,其实不必要存下整个vec2d。
1 | public class Vector2D implements Iterator<Integer> { |
- 经过简洁:省略了很多条件语句。
1 | public class Vector2D implements Iterator<Integer> { |
判断二叉树是否对称 101
- recursive:对称就是在递归判断左子树和右子树是否对称
1 | /** |
- iterative:用Stack吞吐,左先入栈再到右,弹出判断左右是否相等。然后先入左-左再入右-右,然后左-右和右-左(保证对称)
1 | class Solution { |
- follow-up:判断多叉树是否对称。直觉就是把left、right换成一个
List<TreeNode>
,判断对称的时候一个从前往后取子节点、另一个从后往前取子节点。
outbound spiral
- 給一個String 用outbound spiral方式輸出,例如
abcd -> cdba
,abcde -> cdbae
。(其实没太看懂这题啥意思。。。) - 双指针,从中间点开始往两边取。
1 | class Google { |
Longest substirng with k distinct characters 340
- 给一个字符串和k,求最长子字符串的长度使得其中只含有k个不同的字符。
- substring问题->双指针。O(N)。
1 | class Google { |
正则 10
- 输入两个字符串,写个支持
.
和*
的正则表达式判断的method。 - 目标字符串s和正则字符串p之间相互比较,就需要维护一个boolean的二维数组dp[i][j],表示
s[0~i-1]
与p[0~j-1]
是否匹配。
1 | class Solution { |
X删除字眼
- 给一个字符串如
aabbaac
,给一个forbidden字眼a
,返回删除后的字符串bbc
,若不存在就返回本身。 - O(N)直接用StringBuilder拼接。
1 | class Google { |
字母矩阵移动组成单词
- 给一个列数生成由大写字母组成的矩阵,再给一个单词,从左上角开始挪动,求挪动的路径(用&连接).
- 求路径,用BFS可以保证最短。在point中加入一个path段记录到达该处的路径,一旦找到目标字符就直接把path拼进去。再以该处为起点继续找后续字符(需要清空path)。
1 | class Google { |
43. multiply-strings
解析在此
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33class Solution {
public String multiply(String num1, String num2) {
if (num1 == null || num2 == null
|| num1.length() == 0 || num2.length() == 0) {
return "";
}
int m = num1.length(), n = num2.length();
int[] result = new int [m + n];
char[] cnum1 = num1.toCharArray(), cnum2 = num2.toCharArray();
// from least significant bit
for (int i = m - 1; i >= 0; i--) {
for (int j = n - 1; j >= 0; j--) {
int mult = (cnum1[i] - '0') * (cnum2[j] - '0');
int first = i + j, second = i + j + 1;
int sum = mult + result[second]; // add carry from prev steps
result[first] += sum / 10; // accumulate
result[second] = sum % 10; // overwrite
}
}
StringBuilder sb = new StringBuilder();
int i = 0;
// find the first non-zero item
while (i < result.length && sb.length() == 0 && result[i] == 0) {
i++;
}
while (i < result.length) {
sb.append(result[i++]);
}
return sb.length() == 0? "0": sb.toString();
}
}
格子中最长连续 562
- 给一个只有0和1的int二维数组,求横或竖或两个对角方向上最长连续出现1的个数。
- 方法一:O(N^2)从每个点出发往四个方向分别遍历,求最长长度。
1 | class Solution { |
1 | class Solution { |
给出一系列字母求相除结果 399
- 给一系列字符串的倍数关系,给出后续query的结果。
- 图论。可以抽象成一个双向图,每个字符串都作为节点,然后权重就是两个节点的商。我的做法分三步:遍历一遍形成str到index的映射,然后建立邻接矩阵并把权重填充进去(正反都放,不存在就是0),最后就query的时候就用DFS,遍历当前节点所有能走到的节点看看能否到达终点。
1 | class Solution { |
十进制转七进制 可参考405
- 十进制转二进制就是除+膜。不过需要注意负数除法的时候不能带上最前面都符号位。
- 推广到div进制。
1 | final private char[] map = {'0', '1', '2', '3','4','5','6','7','8','9','a','b','c','d','e','f'}; |
九宫格 面巾
- 给一个只含有数字【1到9】的矩阵,从矩阵中寻找九宫格(3x3的正方形,横着加,竖着加,对角线加都是15),返回矩阵中这样的九宫格的个数。
- 遍历所有九宫格的中心点,求对角线x2、行x3、列x3。有个hint是,只有中心点是5的九宫格才有可能符合要求,因此只需要在5处跑一遍3x3的判断即可。
字符串重组
- 版本一:pattern和str,判断是否match。比如:输入“acg”和“abcdefg”,输出true。但“agc”和“abcdefg”就不对了,因为顺序不同。双指针。
- 版本二:莉蔻567。不同之处在于不是subsequence而是严格的substring,且顺序不需要关注,因为是后者包含前者的permutation。
1 | class Solution { |
- 与之类似的莉蔻
1 | class Solution { |
1 | public int lengthOfLongestSubstring(String s) { |
- 莉蔻还有类似的双指针破双字符串的题,如 76最小长度的substring ,即给一个t,求s中最短的字符串使得t中出现的字母都包含在该子串中。
1 | class Solution { |
- 上面这个需要和 727最小长度的subsequence 区分,这里除了出现的字符一样,出现分的先后顺序也要一致。
1 | public String minWindow(String S, String T) { |
stream observer
- 已知一组字符串,每次观察一个输入的字符,若输入的字符顺序和字符串组里的match,就返回match的list,否则返回空。问题的形式让我有点懵,涉及到了interface,然后让我自己定义一个class,自定member variable, hierarchy等等
- 我是用sliding window加上trie做的。先找出list中最长单词的长度k。然后维护一个长度为k的window。建trie的时候注意是反向建,每个单词从最后一个字母开始往前建。同时查的时候也是从window的最右边往前查。但如果有多个匹配(例如abc, abcd都在List中),则需要返回List。
1 | public static class StreamObserver { |
有向无环图最长路径
- 给出公司并购的关系列表,比如
[["baidu", "ofo"], ["mobike", "alibaba"],...]
,表示baidu并购了ofo,摩拜并购了阿里巴巴。。。求最长的一个并购链。保证无环。 - 类似简化版拓扑,完整版拓扑可参考这个submission,不过这里其实只需要找到入度为0的节点,然后更新可达点的同时更新所走的步数。
带时间戳的Map
- 带时间戳的map:
put(k, v, timestamp) get(k , timestamp)
EX:
1 | put(1, haha, 3) |
- 维护
Map<Integer, Map<Integer, String>>
,新插入的key就新建一个TreeMap,然后时间戳为key,String为value存入。读取的时候就取floorKey(timestamp)
,也就是最接近的不大于timestamp的值,有的话即为所求了。 - Follow-up是问用ArrayList, LinkedList, BST, Balanced BST实现的get和put的时间复杂度分别是多少。
树的遍历
- preorder, inorder, post-order。要求掌握iterative和recursive的方法。
1 | public List<Integer> preorderTraversal(TreeNode root) { |
Frequencey Iterator (类似于利口604)
- 给一个int数组,奇数位是count偶数位是number,实现next和hasNext函数。如
[2,4,0,9,5,3]
,那输出就是[4,4,3,3,3,3,3]
。 - 自定义类保存数字和对应的频数,然后入Queue开始输出就好了。
1 | class FreqNumber { |
toeplitz matrix
- 判断一matrix是不是toeplitz matrix, 就是所有左上到右下对角线的数字都相等。
- 对角线方向元素其实就是相邻两行元素的前后判断。
1 | public class Google { |
- follow-up: 如果matrix特别大,每次只能存两行,只用matrix提供的
matrix.next
,matrix.has_next
, 和matrix.size
实现。其实上面的方法已经只存两行了。
3sum
- 给一个数组,求其中所有的a, b, c使得
a + b + c == 0
1 | public List<List<Integer>> threeSum(int[] nums) { |
validate 2D array
- 2-D array,里面有R,G,B,Y四种类型的element,如果横竖没有三个element是同一类型的话,这个2-Darray就是valid的。先让写个function判断一个2-D array是否valid。
- 感觉和那个OOXX(348 Design tic tac toe)的游戏很像,不过就不能通过简单的加一减一来搞了。这里就是暴力了,判断当前字符的前一行、下一行,以及前一列、下一列。
1 | public static boolean isValidMatrix(char[][] matrix) { |
- follow-up:能否并行化计算?各种方式有什么优劣?可以replicate原array,每个thread只处理一个颜色。也可以划分成小块,但是边界线情况讨论起来特别复杂,而且会有很多个thread。
2D array search & sum
- 240 Search a 2D Matrix II,可以直接从第一行的最大列开始找,因为右下方的一定是比当前元素大的,所以只需要查找左下和左前即可。
1 | // O(M + N) |
- 304. Range Sum Query 2D - Immutable 也是缓存中间结果的思想,利用额外的O(MN)的空间存下来,需要取sum的时候就减左侧、上侧再加回左上侧结果即可。
1 | private int[][] sumRegion; |
- follow-up是如果要对矩阵时不时进行更新怎么办。如果还是用上面这个的思路,返回sumRegion的时间就不再是constant了,同时update也不是constant。为了更新,还需要把matrix存下来(必须的)。
1 | private int[][] sumRegion; |
- 另一个高效办法是Binary Index Tree。我们先看看range sum 1D的情况,2D的暂时放一放。。。
1 | int[] nums; |
voting with timestamp
- 给一个
voteList=[(a, 100), (b, 150), (a, 200)] #(name, timestamp)
和时间T,找T时间之前, 得票数最高的人(或任一得票数最高的人)。 - 直接扫一波vote list,只保留在时间T之前的票存到Map中,顺便记下一个最大值和对应的人名。
- follow-up:给voteList、时间T,求top k候选人的list。也是先O(N)扫一波统计,然后再一波loop存入Heap,如果Heap规模达到k就需要比较堆顶和当前票数,若当前票数更大就需要把出堆再push。
- follow-up:给voteList、top k候选人的list,求时间T。只想到最暴力的办法,先把所有出现过的时间t存入从小到大的PriorityQueue,然后从小到大暴力搜“给定voteList和T”,看看求得的list和所给的list是否一致。不过这里每次不是从头开始做,而是累加,即前一个t不行,就取优先队列中的下一个t继续往后加,不用从头来过。
feed tree
- 一棵树,所有节点的value都是正整数,问只能增加某些节点值的情况下,如何调整使得从root到所有leaf的path上经过的节点值之和相等,返回增加的值的和,使这个和最小.
- 类似贪心,在后续遍历的过程中顺便求叶子到当前节点的和并返回,再用全局变量根据左右两边孩子的差值确定需要补多少,更新的条件是该节点一定有两个孩子来比较,这样就能保证和最小。
1 | /* |
remove node
- Given a binary tree, write a function to erase a list of nodes from the tree and return the forest created from this operation.自己决定输入输出形式,自己定义treenode结构.
- 一个思路是,可以在TreeNode中增加parent节点,这样在删除的时候就方便很多,直接设置就可以了。
1 | class TreeNode { |
- 如果不加parent,就需要通过postOrder遍历,先递归到当前节点的左和右孩子,再看看孩子是否要删掉,是就直接设为null。
1 | public List<TreeNode> delete(TreeNode root, List<TreeNode> toRemove) { |
361. Bomb Enemy
- 给一个只含有
0, W, E
的char棋盘,炸弹只能放在0处,炸弹可以向上下左右扩散,求一颗炸弹最多炸死多少E。 - 对于每一行统计敌人数,一旦遇到W就重置为0;对于每一列则需要维护额外的一个数组,也是遇到W就重置。
1 | class Solution { |
- follow-up:如果在E上也可以放炸弹,要如何修改?那么就加多一个判断是否等于
E
的步骤,见上面注释部分。
以上是地里大概找的一部分面经。
以下是利口上的孤狗tag题。
4. Median of 2 sorted arrays
- 给两个有序int数组,返回二者合并后的中位数。
O(M + N)
:归并排序依次合并,合并到一半长度知道中位数了。O(log(M + N))
:中位数是用来将数组分割成相等长度的两部分的,因此使用二分查找找出刚好能分成等长的两部分并且前部分的最大值<=后部分的最小值。
1 | class Solution { |
17. letter-combinations
- 给一串数字的字符串,求这些数字可能输出的所有字母字符串,对应关系为手机的按键。
- DFS:
1 | class Solution { |
20. valid-parentheses
- 给一个字符串,判断其中三种括号
(), [], {}
是否匹配。 - Stack: 左括号本身并不入栈,而是它对应的右括号入栈,这样当右括号出现的时候只需判断二者是否相等或者是否栈已经空了即可。
1 | public boolean isValid(String s) { |
22. generate-parentheses
- 给一个int表示括号的对儿数,输出所有符合括号匹配规则的字符串,存入List中返回。
- DFS:左括号、右括号分别
1 | // O(2^2n)? |
23. merge-k-sorted-lists
- 给一个ListNode节点数组,分别是排好序的若干链表头,要求输出合并后的新链表头.
- 直接用PriorityQueue:
1 | public ListNode mergeKLists(ListNode[] lists) { |
- 分治法。
1 | // divide and conquer, O(nlogk) |
31. next-permutation
- 给一个int数组,将它改造成按字典序的下一个排列,若不存在则直接按从小到大的顺序调整。要求In-place对原数组操作,不能申请额外空间。
- 这个想法从右往左找相邻的两个数使得
first < second
,然后再从右往左找首次出现的比first大的数,二者对调,然后将second及其之后的内容reverse一下即可。
1 | // 1324 -> 1342 |
42. trapping-rain-water
- 给一个int数组,其中包含每个索引处墙的高度。求装满水时的横截面的积水的面积。
- 用两个辅助数组分别表示两侧最高高度,然后再一波遍历看能否装水。
1 | class Solution { |
- follow-up:不能用额外空间呢?
1 | class Solution { |
676. implement-magic-dictionary
- 给一个字典需要维护某种结构使得查询比较高效,查询是通过一个单词输入后判断字典中是否存在一个单词只替换输入串中的一个字符就能得到。
- 有点暴力的方法,在输入一个字符串数组的时候就维护一个从String映射到Character的Map,对于每个单词的每一个字符都换成
*
作为key,然后被替换的字符作为value存入。之后check的时候也是把每个字符都换成*
之后再确认map中有没有对应的key。
1 | class MagicDictionary { |