403. frog-jump
给一个数组表示石头所处的x坐标,青蛙每次只能跳上一次跳跃长度的-1,0,1三种可能,判断能否跳到最后一个石头。
相当于BFS,每个石头处维护一个set存放他可以跳的长度,然后每次都往后跳看看能否有新的石头,有就更新那个石头的可跳长度。
249. group-shifted-strings
给一个String的数组,要求将其中能通过平移相同offset后相同的字符串group在一起.
先根据第一个字母算出与a
的offset,然后将后续所有字母都根据这个offset来挪,拼接成key存入map。
108. convert-sorted-array-to-binary-search-tree
取中间的为根节点,然后前后两半递归继续build。
127. word-ladder
很图论的题,每一个单词看作节点,路径有无根据「能否改一个字母变成它」判断,在BFS过程中可达就直接入queue。
126. word-ladder-ii
词典中每一个词都是一个node,要从起点到终点,如果要记录这些最短的路径具体是怎么走的,就需要在BFS判断可不可达并构建临街表的基础上再来一个DFS,从起点开始一直往后(不走回头路利用的是后出现的节点的距离一定是先经过的节点的距离+1),如果到了终点就把经过的路径存起来。最后形成的就是路径的列表了。
[Extra][path between 2 nodes in tree]
给一个二叉树,和两个节点A,B,找出从A到B的路径,返回结果要求顺序存到一个链表中。
找到A和B的共同祖先,然后倒序输出到A的路径,再输出到B的路径。
[extra][overlap interval]
给两个interval的数组,求二者overlap的部分.
定义一个endCurr表示当前的结尾,然后取两个数组中start较小的与它比较,如果重合就将start为起点、endCurr为结尾new一个新的interval存入结果,然后更新endCurr(如果比该start处对应的end小了的话)
[extra][inplace convert binary tree to BST]
给一个普通的二叉树,转换成二分查找树BST。
方法一:直接中序遍历并将结果存起来,排序后直接赋值回去,可以维持原有的树的形状。
方法二;如果要求in-place,就先将二叉树建成一个双向链表,然后排序(归并O(NlgN)),然后再将双向链表恢复成二分查找树(经过了平衡,没法维持原本的了)。将链表恢复成BST除了取中间值为根然后递归两边,还有一个tricky的方法是从叶开始往根build,复杂度O(N).
[extra][product between indices]
给一个正数数组,给两个索引x和y,求数组从x到y(inclusive)的积。
12345678910111213141516public class Apple {public int getProduct(int[] nums, int x, int y) {if (nums == null || nums.length == 0|| y < x || y >= nums.length || x < 0) {return 0;}// 缓存cache[i]表示从0到i - 1的积int[] cache = new int [nums.length + 1];cache[0] = 1;for (int i = 1; i <= nums.length; i++) {cache[i] = nums[i] * cache[i - 1];}// 从x到y的积就是"到y的积"除以"到x - 1的积"return cache[y + 1] / cache[x];}}数组中含有0呢? 前面的方法不能直接用,难道要用一个n*n的数组穷举出所有情况?其实前面的方法加多个标记0的即可。
123456789101112131415161718192021public class Apple {public int getProduct2(int[] nums, int x, int y) {if (nums == null || nums.length == 0|| y < x || y >= nums.length || x < 0) {return 0;}int[] cache = new int [nums.length + 1];int[] zeroCount = new int [nums.length + 1];cache[0] = 1;for (int i = 1; i <= nums.length; i++) {if (nums[i] = 0) {cache[i] = 1;zeroCount[i] = zeroCount[i - 1] + 1;} else {cache[i] = nums[i] * cache[i - 1];zeroCount[i] = zeroCount[i - 1];}}return zeroCount[y + 1] != zeroCount[x]? 0: cache[y + 1] / cache[x];}}
362. design-hit-counter
实现一个计数器,返回给定时间点之前300秒内的hit数。
[extra][subarray sum]
求所有等于target的subarray,有点类似209. minimum-size-subarray-sum.
双指针,一前一后,一旦前面挪动后所得的sum太大,就开始挪后面的。
anagrams
group问题,定义key:这里就是string按照字母顺序排好序之后的新String。
70. climbing-stairs
爬楼梯,每次只能爬1或2步,求共有多少种到达顶楼的方式。
DP,当前这一层的方式数取决于前一层的方式数加上前两层的方式数。用不着数组。
43. multiply-strings
解析在此
[extra][even space]
给一个句子,再给一个宽度,要求均匀分配空格。
32. longest-valid-parentheses
- 给一个只包含了
(
和)
的字符串,求其中合法匹配的最大子字符串长度。 - 动态规划,dp[i]表示到i索引为止的最长长度。12345678910111213141516171819202122232425262728class Solution {public int longestValidParentheses(String s) {if (s == null || s.length() == 0) {return 0;}int[] dp = new int [s.length()]; // maxLen till curr char (only for valid ')')char[] sChar = s.toCharArray();int ans = 0;for (int i = 0; i < sChar.length; i++) {// only ) is count as valid bitif (sChar[i] == ')') { // "xxx)"if (i > 0) {if (sChar[i - 1] == '(') { // prev is (, simply add 2 to prevprevdp[i] = 2 + (i > 1? dp[i - 2]: 0);} else { // prev is (int prev = i - dp[i - 1] - 1; // move to the very front of prev slotif (prev >= 0 && sChar[prev] == '(') { // only ( can match curr )dp[i] = (prev > 0? dp[prev - 1]: 0) + 2 + dp[i - 1]; // check ('s prev}}ans = Math.max(ans, dp[i]);}}}return ans;}}
543. diameter-of-binary-tree
可以普通O(N^2)的DFS或者优化后的O(N)。
472. concatenated-words
给一个String的数组,求其中能由其他String拼接而成的字符串。和wordbreak很像。
先根据长度排序,然后从字符串前面(维护一个Set)取字符串,然后就变成了wordbreak了。
138. copy-list-with-random-pointer
- 一个链表,每个节点除了含有label和next以外还含有一个指向任意节点的random引用。深复制这个链表。
方法一:用HashMap存放各个链表节点和它对应的复制品。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546/*** Definition for singly-linked list with a random pointer.* class RandomListNode {* int label;* RandomListNode next, random;* RandomListNode(int x) { this.label = x; }* };*/public class Solution {HashMap<RandomListNode, RandomListNode> map;public RandomListNode copyRandomList(RandomListNode head) {map = new HashMap<RandomListNode, RandomListNode>();if (head == null) {return null;}RandomListNode prev = null, curr = head, newHead = null;while (curr != null) {RandomListNode newNode = null;if (map.containsKey(curr)) {newNode = map.get(curr);} else {newNode = new RandomListNode(curr.label);map.put(curr, newNode);}if (prev != null) {prev.next = newNode;} else {newHead = newNode;}if (curr.random != null) {if (map.containsKey(curr.random)) {newNode.random = map.get(curr.random);} else {RandomListNode temp = new RandomListNode(curr.random.label);newNode.random = temp;map.put(curr.random, temp);}}prev = newNode;curr = curr.next;}return newHead;}}把每一个拷贝的节点拼接到原节点的后面,这样一轮循环过后所有节点都有了一份拷贝;第二轮循环就是为random赋值了,既然知道原节点之后就是对应的拷贝节点,那random的引用也就可以直接获得了。最后一轮循环则是把拷贝的节点正确地拼接起来,最后返回。
123456789101112131415161718192021222324252627282930313233343536373839404142public class Solution {public RandomListNode copyRandomList(RandomListNode head) {if (head == null) {return null;}RandomListNode curr = head, currNext = null, currCopy = null;while (curr != null) {currNext = curr.next; // 先暂存原节点的nextcurrCopy = new RandomListNode(curr.label);curr.next = currCopy; // 原节点next指向复制品currCopy.next = currNext; // 复制品next指向原节点的nextcurr = currNext;}curr = head; // 从原链表头开始遍历while (curr != null) {if (curr.random != null) {// 节点的next就是它的复制品curr.next.random = curr.random.next;}curr = curr.next.next;}RandomListNode fakeHead = new RandomListNode(0);curr = head;currCopy = fakeHead;// 恢复原链表同时产生复制品链表while (curr != null) {currNext = curr.next.next;currCopy.next = curr.next;currCopy = curr.next;curr.next = currNext;curr = currNext;}return fakeHead.next;}}
139. word-break
给一个字符串,判断能否由给定dict中的单词组成。
当前是否可以取决于”以当前字符结尾、前面某处开头的单词来自于dict”且”该起点往前也是可以的”。
98. validate-binary-search-tree
验证二叉树是否是BST。
- 递归检查左节点,然后当前节点与prev比较大小保证从小到达,然后更新prev再潜入右边。
- 或者用Stack迭代,中序遍历。
- 或者分而治之,给定节点的范围,判断当前节点是否符合,然后更新两侧的范围再潜入下去。1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950public class Apple {// 一直往左边潜下去,找到第一个元素,然后设置prevprivate TreeNode prev1 = null;private boolean validateBST1(TreeNode root) {if (root == null) {return true;}if (!validateBST1(root.left)) {return false;}if (prev1 != null && prev1.val >= root.val) {return false;}prev1 = root;return validateBST1(root.right);}private boolean validateBST2(TreeNode root) {if (root == null) {return true;}Stack<TreeNode> stack = new Stack<>();TreeNode prev = null;while (root != null || !stack.isEmpty()) {while (root != null) {stack.push(root);root = root.left;}root = stack.pop();if (prev != null && prev.val >= root.val) {return false;}prev = root;root = root.right;}return true;}private boolean validateBST3(TreeNode root) {return dvcq(root, Long.MIN_VALUE, Long.MAX_VALUE);}private boolean dvcq(TreeNode root, long min, long max) {if (root == null) {return true;}if (root.val <= min || root.val >= max) {return false;}return dvcq(root.left, min, Math.min(root.val, max))&& dvcq(root.right, Math.max(root.val, min), max);}}