Easy 01TwoSum
方法一:
方法二:
哈希表
因为暴力解法的时间复杂度是 O(N^2),所以要追求时间复杂度 小一点,而哈希表找的时间复杂度是 O(1)
把nums
的值作为hashmap
的key
“最佳”答案:
1 2 3 4 5 6 7 8 9 10 public int [] twoSum(int [] nums, int target) { HashMap<Integer, Integer> hashMap = new HashMap <>(); for (int i = 0 ; i < nums.length; i++) { if (hashMap.containsKey(target - nums[i])){ return new int []{hashMap.get(target - nums[i]), i}; } hashMap.put(nums[i], i); } return new int [0 ]; }
07ReverseInteger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public int reverse (int x) { int last, num = 0 ; while (x != 0 ){ last = x % 10 ; if (num > Integer.MAX_VALUE / 10 || (num == Integer.MAX_VALUE / 10 ) && last > 7 ) return 0 ; if (num < Integer.MIN_VALUE / 10 || (num == Integer.MIN_VALUE / 10 ) && last < -8 ) return 0 ; num *= 10 ; num += last; x /= 10 ; } return num; }
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public boolean isPalindrome (int x) { if (x < 0 ) return false ; int div = 1 ; while (x / div >= 10 ){ div *= 10 ; } while (x > 0 ){ int high = x / div; int low = x % 10 ; if (high != low) return false ; x = (x % div) / 10 ; div /= 100 ; } return true ; }
13RomanToInteger 要理解题意,并不是两两结合来算,而是直接看前一个是不是比后一个小,小就减,大就加
这里数据量太少,用 hashMap
体现不出,直接用 switch-case
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 public int romanToInt (String s) { HashMap<Character, Integer> hashMap = new HashMap <>(); hashMap.put('I' , 1 ); hashMap.put('V' , 5 ); hashMap.put('X' , 10 ); hashMap.put('L' , 50 ); hashMap.put('C' , 100 ); hashMap.put('D' , 500 ); hashMap.put('M' , 1000 ); int sum = 0 ; int pre = hashMap.get(s.charAt(0 )); for (int i = 1 ; i < s.length(); i++) { if (pre < hashMap.get(s.charAt(i))){ sum -= pre; }else { sum += pre; } pre = hashMap.get(s.charAt(i)); } sum += pre; return sum; }
14LongestCommonPrefix 做的时候想的是:设置一个n,代表最后的相同字符的index,然后从0开始,循环数组,每次取每个字符串第n个字符,看相不相等。
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 public String longestCommonPrefix (String[] strs) { String res = "" ; if (strs == null || strs.length == 0 ){ return res; } if (strs.length == 1 ){ return strs[0 ]; } int n = 0 ; while (true ){ int i; for (i = 1 ; i < strs.length; i++) { if (strs[i - 1 ].isEmpty() || strs[i].isEmpty()){ return res; } if (strs[i - 1 ].length() > n && strs[i].length() > n){ if (strs[i - 1 ].charAt(n) != strs[i].charAt(n)) break ; }else { break ; } } if (i == strs.length){ n++; }else { break ; } } res += strs[0 ].substring(0 , n); return res; }
答案是用分治,分左半部分和右半部分:
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 public String longestCommonPrefix (String[] strs) { if (strs == null || strs.length == 0 ) { return "" ; } else { return longestCommonPrefix(strs, 0 , strs.length - 1 ); } } public String longestCommonPrefix (String[] strs, int start, int end) { if (start == end) { return strs[start]; } else { int mid = (end - start) / 2 + start; String lcpLeft = longestCommonPrefix(strs, start, mid); String lcpRight = longestCommonPrefix(strs, mid + 1 , end); return commonPrefix(lcpLeft, lcpRight); } } public String commonPrefix (String lcpLeft, String lcpRight) { int minLength = Math.min(lcpLeft.length(), lcpRight.length()); for (int i = 0 ; i < minLength; i++) { if (lcpLeft.charAt(i) != lcpRight.charAt(i)) { return lcpLeft.substring(0 , i); } } return lcpLeft.substring(0 , minLength); }
20ValidParentheses 看答案了。。。
后遇到的要先闭合
所以用栈来存储左半部分,一遇到右半部分 的字符,就检查最晚入栈 的栈顶字符是不是对应的前半部分 的字符
如果是,就出栈,然后继续检查下一个…
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 33 34 public boolean isValid (String s) { int length = s.length(); if (length % 2 != 0 ) return false ; Stack<Character> stack = new Stack <>(); HashMap<Character, Character> hashMap = new HashMap <>(); hashMap.put(')' , '(' ); hashMap.put(']' , '[' ); hashMap.put('}' , '{' ); for (int i = 0 ; i < length; i++) { char c = s.charAt(i); if (!hashMap.containsKey(c)){ stack.push(c); }else if (stack.isEmpty() || stack.peek() != hashMap.get(c)){ return false ; }else { stack.pop(); } } if (!stack.isEmpty()) return false ; return true ; }
21MergeTwoSortedLists 用递归,先看两个链表头节点哪个小,先“吃掉”一个,然后继续让剩下的节点比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 public ListNode mergeTwoLists (ListNode l1, ListNode l2) { if (l1 == null ) return l2; else if (l2 == null ) return l1; else if (l1.val < l2.val){ l1.next = mergeTwoLists(l1.next, l2); return l1; }else { l2.next = mergeTwoLists(l1, l2.next); return l2; } }
26RemoveDuplicatesFromSortedArray 直接用双指针i、j,i决定无重复数组最后一个元素的索引,j来遍历数组
如果i元素与j元素相等,即重复,就继续往后遍历
如果不等,即不重复,i、j均往后移一位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int removeDuplicates (int [] nums) { int length = nums.length; int i, j; for (i = 0 , j = 1 ; i < length && j < length;) { if (nums[j] == nums[i]){ j++; }else { nums[i + 1 ] = nums[j]; i++; j++; } } return i + 1 ; }
27RemoveElement 拷贝覆盖
设置一个i,代表不一样的元素下标。直接遍历,遇到不一样的就把nums[i]替换成当前num: nums[i] = num
,然后 i++
1 2 3 4 5 6 7 8 9 10 11 public int removeElement (int [] nums, int val) { int i = 0 ; for (int num : nums) { if (num != val){ nums[i] = num; i++; } } return i; }
Stack 20ValidParentheses(E) 155MinStack(E) 我的方法是在插入和弹出时就计算最小值
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 33 34 35 36 37 class MinStack { private LinkedList<Integer> stack; private int min = Integer.MAX_VALUE; public MinStack () { stack = new LinkedList <>(); } public void push (int x) { stack.push(x); if (x < min) min = x; } public void pop () { if (top() == min){ stack.pop(); int big_min = Integer.MAX_VALUE; for (Integer integer : stack) { if (integer < big_min) big_min = integer; } min = big_min; }else { stack.pop(); } } public int top () { return stack.peek(); } public int getMin () { return min; } }
而官方是使用辅助栈,和主栈同步,储存每个元素作为栈顶时的最小值
因为只要栈顶元素不变,其他元素就不变,当前的最小值就不变
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 class MinStack { Deque<Integer> xStack; Deque<Integer> minStack; public MinStack () { xStack = new LinkedList <Integer>(); minStack = new LinkedList <Integer>(); minStack.push(Integer.MAX_VALUE); } public void push (int x) { xStack.push(x); minStack.push(Math.min(minStack.peek(), x)); } public void pop () { xStack.pop(); minStack.pop(); } public int top () { return xStack.peek(); } public int getMin () { return minStack.peek(); } }
71SimplifyPath(M) 我起初是打算一个一个判断,搞个栈,遇到非 /
、 ..
和 .
就跳过……但是 ..
就不好判断了。想了半天。然后就想到一个api split
,我直接就mlgb了,我tm直接我tm。接着看了答案,确实可以这样
直接 split("/")
把各个部分组成一个数组
因为两个 /
中间只存在 .
或者 ..
, .
、 ..
才有效。所以
遇到 ..
,且栈不为空,就弹出
遇到不是 .
的,且不是空串,就push
最后balabala输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public String simplifyPath (String path) { String[] paths = path.split("/" ); LinkedList<String> stack = new LinkedList <>(); for (String p : paths) { if (".." .equals(p)) { if (!stack.isEmpty()) stack.pop(); } else if (!"." .equals(p) && !p.isEmpty()) { stack.push(p); } } if (stack.isEmpty()) return "/" ; StringBuilder res = new StringBuilder (); while (!stack.isEmpty()){ res.insert(0 , stack.pop()); res.insert(0 , "/" ); } return res.toString(); }
94BinaryTreeInorderTraversal(M) 中序遍历,就是根节点放中间。
我就是直接递归:
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 33 34 35 class Solution { public List<Integer> inorderTraversal (TreeNode root) { List<Integer> all = new LinkedList <>(); if (root == null ) return all; if (root.left != null ){ List<Integer> left = inorderTraversal(root.left); all.addAll(left); } all.add(root.val); if (root.right != null ){ List<Integer> right = inorderTraversal(root.right); all.addAll(right); } return all; } }
但是时间和空间都很难看。。我想原因应该是每个递归都new了一个List。
然后实参为引用,改进:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { public List<Integer> inorderTraversal (TreeNode root) { LinkedList<Integer> res = new LinkedList <>(); myInorderTraversal(root, res); return res; } private void myInorderTraversal (TreeNode root, List<Integer> res) { if (root == null ) return ; TreeNode left = root.left; TreeNode right = root.right; if (left != null ) myInorderTraversal(left, res); res.add(root.val); if (right != null ) myInorderTraversal(right, res); } }
然后看答案,还有栈的解法,用迭代表示递归。看得懂,但我写不出来。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public List<Integer> inorderTraversal (TreeNode root) { List<Integer> res = new ArrayList <Integer>(); Deque<TreeNode> stk = new LinkedList <TreeNode>(); while (root != null || !stk.isEmpty()) { while (root != null ) { stk.push(root); root = root.left; } root = stk.pop(); res.add(root.val); root = root.right; } return res; }
103BinaryTreeZigzagLevelOrderTraversal(M) 我也不知道为啥官方为啥归到Stack里面。答案也没用栈
蛇形输出二叉树
我的思路是双端队列,然后每层弹出和插入都变一个方向,插入时收集数据放入list
比如二叉树是1 23 4567 89
第一趟遍历1,从first弹出,即弹出1,从last插入1的右左(32),此时queue为32
第二趟遍历32,从last弹出,即先弹2再3,从first依次插入2的左右(45)和3的左右(67),此时queue为7654
第三趟遍历7654,从first弹出,即先弹7再654,从last依次插入98,此时queue为98
第四趟遍历98,从last弹出,即先弹8再9,从first依次…没了
写的时候乱得厉害,一下first一下last,一下左右一下右左
事实证明我想复杂了…
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public List<List<Integer>> zigzagLevelOrder (TreeNode root) { Deque<TreeNode> queue = new LinkedList <>(); List<List<Integer>> res = new ArrayList <>(); boolean flag = true ; if (root != null ) { queue.addLast(root); ArrayList<Integer> list = new ArrayList <>(); list.add(root.val); res.add(list); } while (!queue.isEmpty()) { ArrayList<Integer> list = new ArrayList <>(); int size = queue.size(); for (int i = 0 ; i < size; i++) { if (flag) { TreeNode first = queue.pollFirst(); TreeNode right = first.right; if (right != null ) { queue.addLast(right); list.add(right.val); } TreeNode left = first.left; if (left != null ) { queue.addLast(left); list.add(left.val); } } else { TreeNode last = queue.pollLast(); if (last.left != null ) { TreeNode left = last.left; queue.addFirst(left); list.add(left.val); } if (last.right != null ) { TreeNode right = last.right; queue.addFirst(right); list.add(right.val); } } } flag = !flag; if (!list.isEmpty()) res.add(list); } return res; }
答案比我简洁多了,通过一个null来分隔每一层,遇到null就把得到的level_list存进res
维护一个顺序标志is_order_left,过一层就变,决定元素插入level_list是头插还是尾插
元素
level_list
queue
is_order_left
res
1
1
null,2,3
true
null
2,3,null
false
1
2
2(addFirst)
3,null,4,5
false
1
3
3,2(addFirst)
null,4,5,6,7
false
1
null
4,5,6,7,null
true
1,32
4
4(addLast)
5,6,7,null,8
true
1,32
5
4,5(addLast)
5,6,7,null,8,9
true
1,32
…
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 33 34 35 36 37 38 39 40 public List<List<Integer>> zigzagLevelOrder (TreeNode root) { if (root == null ) { return new ArrayList <List<Integer>>(); } List<List<Integer>> results = new ArrayList <List<Integer>>(); LinkedList<TreeNode> node_queue = new LinkedList <TreeNode>(); node_queue.addLast(root); node_queue.addLast(null ); LinkedList<Integer> level_list = new LinkedList <Integer>(); boolean is_order_left = true ; while (node_queue.size() > 0 ) { TreeNode curr_node = node_queue.pollFirst(); if (curr_node != null ) { if (is_order_left) level_list.addLast(curr_node.val); else level_list.addFirst(curr_node.val); if (curr_node.left != null ) node_queue.addLast(curr_node.left); if (curr_node.right != null ) node_queue.addLast(curr_node.right); } else { results.add(level_list); level_list = new LinkedList <Integer>(); if (node_queue.size() > 0 ) node_queue.addLast(null ); is_order_left = !is_order_left; } } return results; }
144BinaryTreePreorderTraversal(M) 用迭代,前序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public List<Integer> preorderTraversal (TreeNode root) { List<Integer> res = new ArrayList <>(); Deque<TreeNode> stk = new LinkedList <>(); while (root != null || !stk.isEmpty()) { while (root != null ) { stk.push(root); res.add(root.val); root = root.left; } root = stk.pop(); root = root.right; } return res; }
★145BinaryTreePostorderTraversal(M) 用迭代,后序遍历
这里要用一个变量来判断这个节点的右子节点是否已经入res
为什么只存一个? 因为子节点存完下一个就到它自己了,只需要存之前的那个
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 33 34 35 public List<Integer> postorderTraversal (TreeNode root) { List<Integer> res = new ArrayList <>(); if (root == null ) return res; Deque<TreeNode> stack = new LinkedList <>(); TreeNode prev = null ; while (root != null || !stack.isEmpty()) { while (root != null ) { stack.push(root); root = root.left; } root = stack.pop(); if (root.right == null || root.right == prev) { res.add(root.val); prev = root; root = null ; } else { stack.push(root); root = root.right; } } return res; }
150EvaluateReversePolishNotation(M) 逆波兰表示式
就那样,用栈实现
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 33 34 35 36 37 38 public int evalRPN (String[] tokens) { Deque<Integer> stack = new LinkedList <>(); for (String token : tokens) { if (token.isEmpty()) continue ; Integer first, sec; switch (token){ case "+" : first = stack.pop(); sec = stack.pop(); stack.push(sec + first); break ; case "-" : first = stack.pop(); sec = stack.pop(); stack.push(sec - first); break ; case "*" : first = stack.pop(); sec = stack.pop(); stack.push(sec * first); break ; case "/" : first = stack.pop(); sec = stack.pop(); stack.push(sec / first); break ; default : stack.push(Integer.valueof(token)); } } return stack.poll(); }
看了评论,也可以用数组实现栈,快一点
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 33 34 35 36 37 38 39 40 41 42 43 44 45 public int evalRPN (String[] tokens) { int [] stack = new int [tokens.length]; int index = -1 ; for (String token : tokens) { if (token.isEmpty()) continue ; int first, sec; switch (token){ case "+" : first = stack[index]; sec = stack[index - 1 ]; index--; stack[index] = sec + first; break ; case "-" : first = stack[index]; sec = stack[index - 1 ]; index--; stack[index] = sec - first; break ; case "*" : first = stack[index]; sec = stack[index - 1 ]; index--; stack[index] = sec * first; break ; case "/" : first = stack[index]; sec = stack[index - 1 ]; index--; stack[index] = sec / first; break ; default : index++; stack[index] = Integer.parseInt(token); } } return stack[0 ]; }
173BinarySearchTreeIterator(M) 二叉搜索树迭代器
因为二叉搜索树是左小右大的,直接中序遍历生成的序列就是从小到大的
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 class BSTIterator { ArrayList<Integer> res; int i = 0 ; public BSTIterator (TreeNode root) { res = new ArrayList <>(); myInorderTraversal(root, res); } public int next () { return res.get(i++); } public boolean hasNext () { if (i < res.size()) return true ; return false ; } private void myInorderTraversal (TreeNode root, List<Integer> res) { if (root == null ) return ; TreeNode left = root.left; TreeNode right = root.right; if (left != null ) myInorderTraversal(left, res); res.add(root.val); if (right != null ) myInorderTraversal(right, res); } }
227BasicCalculatorIi(M) 基本计算器(没有括号)
想到之前的逆波兰表示,整蒙圈了
看了评论
用一个char来存储一个数之前的符号,等拿到这个数(如果当前字符是非数字,那就是这个数已经拿到),就能根据这个char来判断
如果是加减,就直接入栈
如果是乘除,就把栈顶弹出,和已经拿到的这个数做运算
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public int calculate (String s) { Deque<Integer> stack = new LinkedList <>(); char [] nums = (s).toCharArray(); int num = 0 ; char oprt = '+' ; for (int i = 0 ; i < nums.length; i++) { if (Character.isDigit(nums[i])) { num = num * 10 + (nums[i] - '0' ); } if (!Character.isDigit(nums[i]) && nums[i] != ' ' || i == nums.length - 1 ) { int pre; switch (oprt){ case '+' : stack.push(num); break ; case '-' : stack.push(-num); break ; case '*' : pre = stack.pop(); stack.push(num * pre); break ; case '/' : pre = stack.pop(); stack.push(pre / num); break ; } oprt = nums[i]; num = 0 ; } } int sum = 0 ; while (!stack.isEmpty()){ sum += stack.pop(); } return sum; }
232ImplementQueueUsingStacks(E) 栈实现队列
就这样
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class MyQueue { Deque<Integer> left_stack; Deque<Integer> right_stack; public MyQueue () { left_stack = new LinkedList <>(); right_stack = new LinkedList <>(); } public void push (int x) { left_stack.push(x); } public int pop () { moveToRight(); Integer pop = right_stack.pop(); moveToLeft(); return pop; } public int peek () { moveToRight(); Integer peek = right_stack.peek(); moveToLeft(); return peek; } public boolean empty () { return left_stack.isEmpty(); } private void moveToRight () { while (!left_stack.isEmpty()){ right_stack.push(left_stack.pop()); } } private void moveToLeft () { while (!right_stack.isEmpty()){ left_stack.push(right_stack.pop()); } } }
316RemoveDuplicateLetters(M) 去除重复元素,并保持字典序
第一次字典序理解错了,完全瞎做
第二次还是理解不到位,忘了一个条件
当当前元素小于栈底元素,但字符串后面已经没有栈底这个元素了,就不能移除
第三个是参考答案
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 public String removeDuplicateLetters (String s) { Stack<Character> stack = new Stack <>(); HashSet<Character> seen = new HashSet <>(); HashMap<Character, Integer> last_occurrence = new HashMap <>(); for (int i = 0 ; i < s.length(); i++) last_occurrence.put(s.charAt(i), i); for (int i = 0 ; i < s.length(); i++) { char c = s.charAt(i); if (!seen.contains(c)) { while (!stack.isEmpty() && c < stack.peek() && last_occurrence.get(stack.peek()) > i) { seen.remove(stack.pop()); } seen.add(c); stack.push(c); } } StringBuilder sb = new StringBuilder (stack.size()); for (Character c : stack) sb.append(c.charValue()); return sb.toString(); }
331VerifyPreorderSerializationOfABinaryTree(M) 验证二叉树的前序序列化
不会。。。
答案真简单,但想想也应该是这样:
我们可以定义一个概念,叫做槽位,二叉树中任意一个节点或者空孩子节点都要占据一个槽位。二叉树的建立也伴随着槽位数量的变化。开始时只有一个槽位,如果根节点是空节点,就只消耗掉一个槽位,如果根节点不是空节点,除了消耗一个槽位,还要为孩子节点增加两个新的槽位。之后的节点也是同理。
有了上面的讨论,方法就很简单了。依次遍历前序序列化,根据节点是否为空,按照规则消耗/增加槽位。如果最后可以将所有的槽位消耗完,那么这个前序序列化就是合法的。
开始时只有一个可用槽位。
空节点和非空节点都消耗一个槽位。
空节点不增加槽位,非空节点增加两个槽位。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public boolean isValidSerialization (String preorder) { int slots = 1 ; for (String node : preorder.split("," )) { --slots; if (slots < 0 ) return false ; if (!node.equals("#" )) slots += 2 ; } return slots = = 0 ; }
341FlattenNestedListIterator(M) 扁平化嵌套列表迭代器
直接初始化就递归
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public interface NestedInteger { public boolean isInteger () ; public Integer getInteger () ; public List<NestedInteger> getList () ; } public class NestedIterator implements Iterator <Integer> { private Deque<Integer> list; public NestedIterator (List<NestedInteger> nestedList) { list = new LinkedList <>(); listPush(nestedList); } private void listPush (List<NestedInteger> nestedList) { for (NestedInteger nestedInteger : nestedList) { if (nestedInteger.isInteger()) list.push(nestedInteger.getInteger()); else { listPush(nestedInteger.getList()); } } } @Override public Integer next () { return list.pollLast(); } @Override public boolean hasNext () { return !list.isEmpty(); } }
从头开始 2AddTwoNumbers(M) 两数相加
我是用递归的,因为试着直接循环链表感觉太乱了,变量太多,脑子转不过来
因为每位都是个位十进制,只要处理好进位就行
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public class ListNode { int val; ListNode next; ListNode() { } ListNode(int val) { this .val = val; } ListNode(int val, ListNode next) { this .val = val; this .next = next; } } public ListNode addTwoNumbers (ListNode l1, ListNode l2) { ListNode res = new ListNode (); if (l1 != null ) res.val += l1.val; if (l2 != null ) res.val += l2.val; int more = res.val / 10 ; res.val %= 10 ; res.next = myAddTwoNumbers(l1 == null ? null : l1.next, l2 == null ? null : l2.next, more); return res; } public ListNode myAddTwoNumbers (ListNode l1, ListNode l2, int more) { if (l1 == null && l2 == null ){ if (more != 0 ){ return new ListNode (more); } return null ; } ListNode res = new ListNode (); if (l1 != null ) res.val += l1.val; if (l2 != null ) res.val += l2.val; res.val += more; more = res.val / 10 ; res.val %= 10 ; res.next = myAddTwoNumbers(l1 == null ? null : l1.next, l2 == null ? null : l2.next, more); return res; }
3LongestSubstringWithoutRepeatingCharacters(M) 无重复字符的最长子串
我的解法就是那样,但做了重复的判断,因为不知道map怎么删掉前i个元素
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 public int lengthOfLongestSubstring (String s) { int length = s.length(); if (length == 0 ) return 0 ; if (length == 1 ) return 1 ; HashMap<Character, Integer> map = new HashMap <>(); int count = 0 , max = 0 ; for (int i = 0 ; i < length; i++) { char now = s.charAt(i); if (map.get(now) == null ){ map.put(now, i); count++; if (count > max) max = count; }else { i = map.get(now); count = 0 ; map.clear(); } } return max; }
答案:
依次递增地枚举子串的起始位置,那么子串的结束位置也是递增的
假设我们选择字符串中的第 kk 个字符作为起始位置,并且得到了不包含重复字符的最长子串的结束位置为 r_k 。那么当我们选择第 k+1个字符作为起始位置时,首先从 k+1 到 r_k 的字符显然是不重复的,并且由于少了原本的第 k 个字符,我们可以尝试继续增大 r_k,直到右侧出现了重复字符为止
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public int lengthOfLongestSubstring (String s) { Set<Character> occ = new HashSet <>(); int n = s.length(); int rk = -1 , ans = 0 ; for (int i = 0 ; i < n; ++i) { if (i != 0 ) { occ.remove(s.charAt(i - 1 )); } while (rk + 1 < n && !occ.contains(s.charAt(rk + 1 ))) { occ.add(s.charAt(rk + 1 )); ++rk; } ans = Math.max(ans, rk - i + 1 ); } return ans; }
5LongestPalindromicSubstring 最长回文子串
不会,看了答案,用动态规划,因为回文串首尾去掉还是回文串,所以有状态转移,即求出边界(一个字符、两个字符),就能往上推
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 public String longestPalindrome (String s) { int l = s.length(); boolean [][] dp = new boolean [l][l]; String res = "" ; for (int subLength = 0 ; subLength < l; subLength++){ for (int left = 0 ; left + subLength < l; left++){ int right = left + subLength; if (subLength == 0 ){ dp[left][right] = true ; }else if (subLength == 1 ){ if (s.charAt(left) == s.charAt(right)){ dp[left][right] = true ; } }else { dp[left][right] = dp[left + 1 ][right - 1 ] && (s.charAt(left) == s.charAt(right)); } if (dp[left][right] && subLength + 1 > res.length()){ res = s.substring(left, left + 1 + subLength); } } } return res; }
6ZigzagConversion Z 字形变换
直接暴力
构造一个 numRows 个字符串的数组,然后一个一个字符遍历s,用 r 来控制加到哪一行,用一个 flag 来控制行的遍历顺序,遇到边界就反转 flag
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 33 34 35 36 37 38 39 public String convert (String s, int numRows) { if (s == null ) return "" ; int length; if ((length = s.length()) == 0 ) return "" ; if (length <= numRows || numRows == 1 ) return s; StringBuilder[] res = new StringBuilder [numRows]; boolean flag = true ; for (int i = 0 , r = 0 ; i < length; i++) { String c = s.substring(i, i + 1 ); if (res[r] == null ) res[r] = new StringBuilder (c); else res[r].append(c); if (flag) r++; else r--; if (r == numRows - 1 || r == 0 ) flag = !flag; } StringBuilder builder = new StringBuilder (); for (StringBuilder row : res) { builder.append(row); } return builder.toString(); }
然后看了答案,乖乖,跟我一样的思维。。。。不贴出来了
每日一题 217ContainsDuplicate(E) 存在重复元素
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 33 34 35 public boolean containsDuplicate (int [] nums) { Arrays.sort(nums); int n = nums.length; for (int i = 0 ; i < n - 1 ; i++) { if (nums[i] == nums[i + 1 ]) { return true ; } } return false ; }
49GroupAnagrams 字母异位词分组
利用哈希表的特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public List<List<String>> groupAnagrams (String[] strs) { HashMap<String, List<String>> hashMap = new HashMap <>(); for (String str : strs) { char [] chars = str.toCharArray(); Arrays.sort(chars); String s = String.valueOf(chars); List<String> list; if ((list = hashMap.get(s)) == null ){ list = new LinkedList <>(); } list.add(str); hashMap.put(s, list); } return new LinkedList <>(hashMap.values()); }
遍历,顺便把字符串的字符排个序,看看 HashMap 里有没有,要没有就 new 一个 List ,把没排序的 add 进 List,再把 List 放回去
看了答案,居然有 api:
1 2 3 4 public V getOrDefault (Object key, V defaultValue) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? defaultValue : e.value; }
还有一种方法,用质数:
用质数表示26个字母,把字符串的各个字母相乘,这样可保证字母异位词的乘积必定是相等的
738MonotoneIncreasingDigits cnm,集合类用多了,惯性就用了集合,即使方法对了,也是慢
只要下一位比之前的首位大,就要把之前的都变成 9,记得记录此时的索引,免得下一次再算一次
我的:
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 33 public int monotoneIncreasingDigits (int N) { LinkedList<Integer> list = new LinkedList <>(); int point = 0 ; while (N != 0 ) { int low = N % 10 ; if (list.isEmpty()) list.addLast(low); else if (low > list.getLast()) { int size = list.size(); for (int i = size - 1 ; i >= point; i--) list.set(i, 9 ); list.addLast(low - 1 ); point = size; }else { list.addLast(low); } N /= 10 ; } int mult = 1 , sum = 0 ; Iterator<Integer> itr = list.iterator(); while (itr.hasNext()) { Integer next = itr.next(); sum += next * mult; mult *= 10 ; } return sum; }
官方的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public int monotoneIncreasingDigits (int N) { char [] strN = Integer.toString(N).toCharArray(); int i = 1 ; while (i < strN.length && strN[i - 1 ] <= strN[i]) { i += 1 ; } if (i < strN.length) { while (i > 0 && strN[i - 1 ] > strN[i]) { strN[i - 1 ] -= 1 ; i -= 1 ; } for (i += 1 ; i < strN.length; ++i) { strN[i] = '9' ; } } return Integer.parseInt(new String (strN)); }
先看看有几位是递增的,就不用重复判断了
290WordPattern 一位一位同时遍历,组成键值对插入 HashMap
如果 key 不存在,且不存在相同的 value,就插入
key 存在,value 也相同,跳过
其他情况都 false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public boolean wordPattern (String pattern, String s) { String[] two = s.split(" " ); if (pattern.length() != two.length) return false ; HashMap<Character, String> map = new HashMap <>(); for (int i = 0 ; i < pattern.length(); i++) { char c = pattern.charAt(i); if (map.containsKey(c)) { if (!map.get(c).equals(two[i])) return false ; }else if (map.containsValue(two[i])){ return false ; }else map.put(c, two[i]); } return true ; }
714BestTimeToBuyAndSellStockWithTransactionFee 买卖股票的最佳时机含手续费
很明显是 DP
找状态,从第 n 天开始
设 为第 i 天交易结束后,dp[i][0] 表示手上没有股票的收益,dp[i][1] 为有股票的收益,price( i ) 表示第 i 天的股价,fee 表示手续费
dp[i][0] 代表:
前一天,有两种情况: dp[i - 1][0] 和 dp[i - 1][1],则有
dp[i][0] = dp[i - 1][0]
dp[i][0] = dp[i - 1][1] + price( i ) + fee
有状态转移:dp[i][0] = max( dp[i - 1][0], dp[i - 1][1] + price( i ) + fee )
dp[i][1] 代表:
前一天,有两种情况: dp[i - 1][1] 和 dp[i - 1][0],则有
dp[i][1] = dp[i - 1][1]
dp[i][1] = dp[i - 1][0] - price( i )
有状态转移:dp[i][1] = max( dp[i - 1][1], dp[i - 1][0] - price( i ) )
而假设 i 是最后一天,dp[i][0] 肯定比 dp[i][1] 大
最后返回 dp[i][0] 就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public int maxProfit (int [] prices, int fee) { int n = prices.length; int [][] dp = new int [n][2 ]; dp[0 ][0 ] = 0 ; dp[0 ][1 ] = -prices[0 ]; for (int i = 1 ; i < n; ++i) { dp[i][0 ] = Math.max(dp[i - 1 ][0 ], dp[i - 1 ][1 ] + prices[i] - fee); dp[i][1 ] = Math.max(dp[i - 1 ][1 ], dp[i - 1 ][0 ] - prices[i]); } return dp[n - 1 ][0 ]; } public int maxProfit (int [] prices, int fee) { int n = prices.length; int sell = 0 , buy = -prices[0 ]; for (int i = 1 ; i < n; ++i) { sell = Math.max(sell, buy + prices[i] - fee); buy = Math.max(buy, sell - prices[i]); } return sell; }
48RotateImage 旋转图像
n * n 图像顺时针旋转 90°
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void rotate (int [][] matrix) { int n = matrix.length; for (int i = 0 ; i < n / 2 ; ++i) { for (int j = 0 ; j < n; ++j) { int temp = matrix[i][j]; matrix[i][j] = matrix[n - i - 1 ][j]; matrix[n - i - 1 ][j] = temp; } } for (int i = 0 ; i < n; ++i) { for (int j = 0 ; j < i; ++j) { int temp = matrix[i][j]; matrix[i][j] = matrix[j][i]; matrix[j][i] = temp; } } }
746MinCostClimbingStairs 使用最小花费爬楼梯
1 2 3 4 5 6 7 8 9 public int minCostClimbingStairs (int [] cost) { int n = cost.length; int [] dp = new int [n + 1 ]; dp[0 ] = dp[1 ] = 0 ; for (int i = 2 ; i <= n; i++) { dp[i] = Math.min(dp[i - 1 ] + cost[i - 1 ], dp[i - 2 ] + cost[i - 2 ]); } return dp[n]; }
假设数组 cost 的长度为 n,则 n 个阶梯分别对应下标 0 到 n-1,楼层顶部对应下标 n,问题等价于计算达到下标 n 的最小花费。可以通过动态规划求解。
创建长度为 n + 1 的数组 dp,其中 dp[i] 表示达到下标 i 的最小花费。
由于可以选择下标 0 或 1 作为初始阶梯,因此有 dp[0] = dp[1] = 0
387FirstUniqueCharacterInAString( E ) 字符串中的第一个唯一字符
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 33 34 public int firstUniqChar (String s) { HashMap<Character, Integer> first = new HashMap <>(); for (int i = 0 ; i < s.length(); i++) { char now = s.charAt(i); if (first.containsKey(now)){ first.put(now, -1 ); }else { first.put(now, i); } } int minIndex = Integer.MAX_VALUE; for (Map.Entry<Character, Integer> entry : first.entrySet()) { Integer index = entry.getValue(); if (index != -1 && index < minIndex) minIndex = index; } if (minIndex == Integer.MAX_VALUE) return -1 ; return minIndex; }
455AssignCookies( E ) 先排个序,然后就那样,没啥说的。。
贪心
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public int findContentChildren (int [] g, int [] s) { Arrays.sort(g); Arrays.sort(s); int res = 0 , i = 0 , j = 0 ; while (i < g.length && j < s.length){ if (g[i] <= s[j]){ i++; j++; res++; }else { j++; } } return res; }
85MaximalRectangle( H ) 看了 答案 ,艰难地敲了出来,暴力解法
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 33 34 public int maximalRectangle (char [][] matrix) { if (matrix == null || matrix.length == 0 ) return 0 ; int row = matrix.length; int col = matrix[0 ].length; int [][] length = new int [row][col]; for (int i = 0 ; i < row; i++) { length[i][0 ] = Character.getNumericValue(matrix[i][0 ]); for (int j = 1 ; j < col; j++) { if (matrix[i][j] == '1' ){ length[i][j] = length[i][j - 1 ] + 1 ; } } } int max = 0 ; for (int i = 0 ; i < row; i++) { for (int j = 0 ; j < col; j++) { if (length[i][j] == '0' ) continue ; int width = length[i][j]; max = Math.max(max, length[i][j]); for (int k = i - 1 ; k >= 0 ; k--){ width = Math.min(width, length[k][j]); max = Math.max(max, width * (i - k + 1 )); } } } return max; }
单调栈,类比 84LargestRectangleInHistogram
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 33 public int maximalRectangle (char [][] matrix) { int m = matrix.length; if (m == 0 ) { return 0 ; } int n = matrix[0 ].length; int [][] left = new int [m][n]; for (int i = 0 ; i < m; i++) { for (int j = 0 ; j < n; j++) { if (matrix[i][j] == '1' ) { left[i][j] = (j == 0 ? 0 : left[i][j - 1 ]) + 1 ; } } } int ret = 0 ; for (int i = 0 ; i < m; i++) { for (int j = 0 ; j < n; j++) { if (matrix[i][j] == '0' ) { continue ; } int width = left[i][j]; int area = width; for (int k = i - 1 ; k >= 0 ; k--) { width = Math.min(width, left[k][j]); area = Math.max(area, (i - k + 1 ) * width); } ret = Math.max(ret, area); } } return ret; }
84LargestRectangleInHistogram CV 答案
遍历高,采用单调栈,拿到左右边界
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 public int largestRectangleArea (int [] heights) { int n = heights.length; int [] left = new int [n]; int [] right = new int [n]; Stack<Integer> mono_stack = new Stack <Integer>(); for (int i = 0 ; i < n; ++i) { while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) { mono_stack.pop(); } left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek()); mono_stack.push(i); } mono_stack.clear(); for (int i = n - 1 ; i >= 0 ; --i) { while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) { mono_stack.pop(); } right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek()); mono_stack.push(i); } int ans = 0 ; for (int i = 0 ; i < n; ++i) { ans = Math.max(ans, (right[i] - left[i] - 1 ) * heights[i]); } return ans; }
188BestTimeToBuyAndSellStockIv( H ) 买卖股票的最佳时机 IV
淦,做了 中等的买卖股票 ,没有限制买卖次数,这道题有限制买卖次数,还是做不出来呜呜呜。。。
!!!注意
buy 数组是进行恰好 j 笔交易,并且当前手上持有一支股票,这种情况下的最大利润
sell 表示恰好进行 j 笔交易,并且当前手上不持有股票,这种情况下的最大利润
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 public int maxProfit (int k, int [] prices) { if (prices.length == 0 ) { return 0 ; } int n = prices.length; k = Math.min(k, n / 2 ); int [][] buy = new int [n][k + 1 ]; int [][] sell = new int [n][k + 1 ]; buy[0 ][0 ] = -prices[0 ]; sell[0 ][0 ] = 0 ; for (int i = 1 ; i <= k; ++i) { buy[0 ][i] = sell[0 ][i] = Integer.MIN_VALUE / 2 ; } for (int i = 1 ; i < n; ++i) { buy[i][0 ] = Math.max(buy[i - 1 ][0 ], sell[i - 1 ][0 ] - prices[i]); for (int j = 1 ; j <= k; ++j) { buy[i][j] = Math.max(buy[i - 1 ][j], sell[i - 1 ][j] - prices[i]); sell[i][j] = Math.max(sell[i - 1 ][j], buy[i - 1 ][j - 1 ] + prices[i]); } } return Arrays.stream(sell[n - 1 ]).max().getAsInt(); }
简化:
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 public int maxProfit (int k, int [] prices) { if (prices.length == 0 ) { return 0 ; } int n = prices.length; k = Math.min(k, n / 2 ); int [] buy = new int [k + 1 ]; int [] sell = new int [k + 1 ]; buy[0 ] = -prices[0 ]; sell[0 ] = 0 ; for (int i = 1 ; i <= k; ++i) { buy[i] = sell[i] = Integer.MIN_VALUE / 2 ; } for (int i = 1 ; i < n; ++i) { buy[0 ] = Math.max(buy[0 ], sell[0 ] - prices[i]); for (int j = 1 ; j <= k; ++j) { buy[j] = Math.max(buy[j], sell[j] - prices[i]); sell[j] = Math.max(sell[j], buy[j - 1 ] + prices[i]); } } return Arrays.stream(sell).max().getAsInt(); }
121BestTimeToBuyAndSellStock( E ) 每次看看这个价格是不是最低值,如果是那就替换,如果不是那就和把自己当成卖出的价格,看看利润有没有最大利润大
1 2 3 4 5 6 7 8 9 10 11 public int maxProfit (int [] prices) { int maxGot = 0 , min = Integer.MAX_VALUE; for (int i = 0 ; i < prices.length; i++) { int now = prices[i]; if (now < min) min = now; else if (now - min > maxGot) maxGot = now - min; } return maxGot; }
330PatchingArray( H ) 按要求补齐数组
不会。。。
对于任意 1 ≤ y < x,y 已经被覆盖,x 在数组中,因此 y + x 也被覆盖,区间 [x + 1,2x - 1](即区间 [1,x - 1][1,x − 1] 内的每个数字加上 x 之后得到的区间)内的所有数字也被覆盖,由此可得区间 [1,2x - 1] 内的所有数字都被覆盖
每次找到未被数组 nums 覆盖的最小的整数 x,在数组中补充 x,然后寻找下一个未被覆盖的最小的整数,重复上述步骤直到区间 [1,n] 中的所有数字都被覆盖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int minPatches (int [] nums, int n) { int res = 0 ; long x = 1 ; int length = nums.length, index = 0 ; while (x <= n) { if (index < length && nums[index] <= x) { x += nums[index]; index++; } else { x *= 2 ; res++; } } return res; }
239SlidingWindowMaximum( H ) 滑动窗口最大值
我的做法是最大堆,但是超时了。。。
官方 第一个题解也是最大堆,还有其他更“高级”的解法
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 33 34 35 36 37 38 39 public int [] maxSlidingWindow(int [] nums, int k) { int n = nums.length; PriorityQueue<int []> pq = new PriorityQueue <int []>(new Comparator <int []>() { public int compare (int [] pair1, int [] pair2) { return pair1[0 ] != pair2[0 ] ? pair2[0 ] - pair1[0 ] : pair2[1 ] - pair1[1 ]; } }); for (int i = 0 ; i < k; ++i) { pq.offer(new int []{nums[i], i}); } int [] ans = new int [n - k + 1 ]; ans[0 ] = pq.peek()[0 ]; for (int i = k; i < n; ++i) { pq.offer(new int []{nums[i], i}); while (pq.peek()[1 ] <= i - k) { pq.poll(); } ans[i - k + 1 ] = pq.peek()[0 ]; } return ans; }
509FibonacciNumber( E ) 斐波那契数
直接动态规划
1202SmallestStringWithSwaps( M ) 交换字符串中的元素
并查集!!!
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 class Solution { public String smallestStringWithSwaps (String s, List<List<Integer>> pairs) { if (pairs.size() == 0 ) { return s; } int len = s.length(); UnionFind unionFind = new UnionFind (len); for (List<Integer> pair : pairs) { int index1 = pair.get(0 ); int index2 = pair.get(1 ); unionFind.union(index1, index2); } char [] charArray = s.toCharArray(); Map<Integer, PriorityQueue<Character>> hashMap = new HashMap <>(len); for (int i = 0 ; i < len; i++) { int root = unionFind.find(i); hashMap.computeIfAbsent(root, key -> new PriorityQueue <>()).offer(charArray[i]); } StringBuilder stringBuilder = new StringBuilder (); for (int i = 0 ; i < len; i++) { int root = unionFind.find(i); stringBuilder.append(hashMap.get(root).poll()); } return stringBuilder.toString(); } private class UnionFind { private int [] parent; private int [] rank; public UnionFind (int n) { this .parent = new int [n]; this .rank = new int [n]; for (int i = 0 ; i < n; i++) { this .parent[i] = i; this .rank[i] = 1 ; } } public void union (int x, int y) { int rootX = find(x); int rootY = find(y); if (rootX == rootY) { return ; } if (rank[rootX] == rank[rootY]) { parent[rootX] = rootY; rank[rootY]++; } else if (rank[rootX] < rank[rootY]) { parent[rootX] = rootY; } else { parent[rootY] = rootX; } } public int find (int x) { if (x != parent[x]) { parent[x] = find(parent[x]); } return parent[x]; } } }
523ContinuousSubarraySum 动归超时,数组过大
我的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public boolean checkSubarraySum (int [] nums, int k) { int [] cav = new int [nums.length]; for (int i = 0 ; i < nums.length; i++) { cav[i] = nums[i]; } for (int count = 2 ; count <= nums.length; count++) { for (int i = 0 ; i + count - 1 < nums.length; i++) { cav[i] += nums[i + count - 1 ]; if (cav[i] % k == 0 ) return true ; } } return false ; }
答案:同余定理 。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public boolean checkSubarraySum (int [] nums, int k) { int m = nums.length; if (m < 2 ) { return false ; } Map<Integer, Integer> map = new HashMap <Integer, Integer>(); map.put(0 , -1 ); int remainder = 0 ; for (int i = 0 ; i < m; i++) { remainder = (remainder + nums[i]) % k; if (map.containsKey(remainder)) { int prevIndex = map.get(remainder); if (i - prevIndex >= 2 ) { return true ; } } else { map.put(remainder, i); } } return false ; }
没想到,暴力还更快:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public boolean checkSubarraySum (int [] nums, int k) { for (int i = 0 ; i < nums.length - 1 ; i++) { if (nums[i] == 0 && nums[i + 1 ] == 0 ) { return true ; } } for (int i = 0 ; i < nums.length; i++) { int sum = nums[i]; for (int j = i + 1 ; j < nums.length; j++) { sum += nums[j]; if (sum % k == 0 ) { return true ; } } if (sum < k) { break ; } } return false ; }
剑指 Offer 15二进制中1的个数 1 2 3 4 5 6 7 8 9 public int hammingWeight (int n) { int res = 0 ; while (n != 0 ){ res += (n & 1 ); n >>>= 1 ; } return res; }
17打印从1到最大的n位数 考虑大数,用 String 存放
递归生成
具体解释:LeetCode 评论
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 StringBuilder res; int nine = 0 , count = 0 , start, n;char [] num, loop = {'0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' };public String printNumbers (int n) { this .n = n; res = new StringBuilder (); num = new char [n]; start = n - 1 ; dfs(0 ); res.deleteCharAt(res.length() - 1 ); return res.toString(); } void dfs (int x) { if (x == n) { String s = String.valueOf(num).substring(start); if (!s.equals("0" )) res.append(s + "," ); if (n - start == nine) start--; return ; } for (char i : loop) { if (i == '9' ) nine++; num[x] = i; dfs(x + 1 ); } nine--; }
21调整数组顺序使奇数位于偶数前面 我用了三种方法,觉得最后一种最装逼,但是时间只击败了 30%,看了评论,可能是数据量少的原因
搞个 list,偶数加后面,奇数加前面
搞个数组,两个指针,奇数放前面,左指针++,偶数放后面,右指针–
两个指针,分别从左右开始,当左偶右奇,交换
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public int [] exchange(int [] nums) { int length = nums.length; int left = 0 , right = length - 1 ; while (left < right){ if (nums[left] % 2 == 0 && nums[right] % 2 != 0 ){ int temp = nums[left]; nums[left] = nums[right]; nums[right] = temp; left++; right--; }else if (nums[left] % 2 != 0 && nums[right] % 2 == 0 ){ left++; right--; }else if (nums[left] % 2 != 0 ){ left++; }else if (nums[right] % 2 == 0 ){ right--; } } return nums; }
22链表中倒数第k个节点 先让 temp = head 往后跑 k 个,如果空,就直接返回 head
如果不空,就开始迭代,temp 每往后走一个,head 就往后走一个,直到 temp 为空,这时候 head 就是结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public ListNode getKthFromEnd (ListNode head, int k) { int i = k; ListNode temp = head; ListNode res = head; while (i != 0 ){ temp = temp.next; i--; } if (temp == null ) return res; while (temp != null ){ temp = temp.next; res = res.next; } return res; }
24反转链表 指针太多,脑子差点转不过来
要记录三个,一个之前的,一个现在的,一个 next
从第二个开始遍历,遍历到的是 现在的
记录 next,替换 现在的 next 为 之前的,把 现在的 设为 之前的
再把 next 赋值给 现在的
继续循环
只可意会不可言传…
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 public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public ListNode reverseList (ListNode head) { if (head == null ) return null ; ListNode pre = head; head = head.next; pre.next = null ; while (head != null ){ ListNode next = head.next; head.next = pre; pre = head; head = next; } return pre; }
25合并两个排序的链表 双指针
注意是引用!!!
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 public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } class Solution { private ListNode root = new ListNode (0 ); private ListNode now = root; public ListNode mergeTwoLists (ListNode l1, ListNode l2) { while (l1 != null && l2 != null ){ if (l1.val > l2.val){ now.next = l2; l2 = l2.next; }else { now.next = l1; l1 = l1.next; } now = now.next; } if (l1 == null ) now.next = l2; if (l2 == null ) now.next = l1; return root.next; } }
27二叉树的镜像 没啥说的,直接递归交换左右
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } class Solution { public TreeNode mirrorTree (TreeNode root) { if (root == null ) return null ; TreeNode left = root.left; root.left = mirrorTree(root.right); root.right = mirrorTree(left); return root; } }
28对称的二叉树 想歪了,想不出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } class Solution { public boolean isSymmetric (TreeNode root) { return root = = null ? true : recur(root.left, root.right); } boolean recur (TreeNode L, TreeNode R) { if (L == null && R == null ) return true ; if (L == null || R == null || L.val != R.val) return false ; return recur(L.left, R.right) && recur(L.right, R.left); } }
14- I cuttingRope 动态规划
用 dp[i] 表示 i 分成 2 个或以上数的最大乘积
j 表示第一个乘数
当 i ≥ 2 时,假设对正整数 i 拆分出的第一个正整数是 j(1 ≤ j < i),则有以下两种方案:
将 i 拆分成 j 和 i − j 的和,且 i − j 不再拆分成多个正整数,此时的乘积是 j × (i − j)
将 i 拆分成 j 和 i − j 的和,且 i − j 继续拆分成多个正整数,此时的乘积是 j × dp[i−j]
当到下一个 i 时,就让 j 从 1 开始,比较 j * ( i - j ) 和 j * dp[i - j] 哪个大,再和之前记录的 dp[i] 比较,直到 j 到达 i 的一半( 这里的一半因为:比如5 = 2 + 3,而 j 表示第一个乘数,如果 j 遍历到 3,那就是 3 + 2,重复了 )
1 2 3 4 5 6 7 8 9 public int cuttingRope (int n) { int [] dp = new int [n + 1 ]; for (int i = 2 ; i <= n; i++) { for (int j = 1 ; j < i / 2 + 1 ; j++) { dp[i]= Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j])); } } return dp[n]; }
16数值的整数次方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public double myPow (double x, int n) { if (x == 0 ) return 0 ; long b = n; double res = 1.0 ; if (b < 0 ) { x = 1 / x; b = -b; } while (b > 0 ) { if ((b & 1 ) == 1 ) res *= x; x *= x; b >>= 1 ; } return res; }
18删除链表的节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public ListNode deleteNode (ListNode head, int val) { if (head.val == val) return head.next; ListNode temp = head; ListNode pre = head; while (temp != null ){ if (temp.val == val){ pre.next = temp.next; } pre = temp; temp = temp.next; } return head; }
26树的子结构 没做出来…
解析 讲的很明白
1 2 3 4 5 6 7 8 public boolean isSubStructure (TreeNode A, TreeNode B) { return (A != null && B != null ) && (recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B)); } boolean recur (TreeNode A, TreeNode B) { if (B == null ) return true ; if (A == null || A.val != B.val) return false ; return recur(A.left, B.left) && recur(A.right, B.right); }
29顺时针打印矩阵
解析
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 public int [] spiralOrder(int [][] matrix) { if (matrix.length == 0 ) return new int [0 ]; int left = 0 , right = matrix[0 ].length - 1 , top = 0 , bottom = matrix.length - 1 , res_i = 0 ; int [] res = new int [(right + 1 ) * (bottom + 1 )]; while (true ) { for (int i = left; i <= right; i++) res[res_i++] = matrix[top][i]; if (++top > bottom) break ; for (int i = top; i <= bottom; i++) res[res_i++] = matrix[i][right]; if (left > --right) break ; for (int i = right; i >= left; i--) res[res_i++] = matrix[bottom][i]; if (top > --bottom) break ; for (int i = bottom; i >= top; i--) res[res_i++] = matrix[i][left]; if (++left > right) break ; } return res; }
30包含min函数的栈 辅助栈存非严格递减的元素
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 class MinStack { Stack<Integer> A, B; public MinStack () { A = new Stack <>(); B = new Stack <>(); } public void push (int x) { A.add(x); if (B.empty() || B.peek() >= x) B.add(x); } public void pop () { if (A.pop().equals(B.peek())) B.pop(); } public int top () { return A.peek(); } public int min () { return B.peek(); } }
31栈的压入、弹出序列 1 2 3 4 5 6 7 8 9 10 11 12 public boolean validateStackSequences (int [] pushed, int [] popped) { Deque<Integer> stack = new LinkedList <>(); int i = 0 ; for (int num : pushed) { stack.push(num); while (!stack.isEmpty() && stack.peek() == popped[i]) { stack.pop(); i++; } } return stack.isEmpty(); }
32-I从上到下打印二叉树 层序遍历,因为不用按层来存,所以比下面的一题简单
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 public int [] levelOrder(TreeNode root) { if (root == null ){ return new int [0 ]; } List<TreeNode> order = new ArrayList <>(); int count = 1 ; order.add(root); for (int i = 0 ; i < order.size(); i++) { TreeNode now = order.get(i); if (now.left != null ){ order.add(now.left); count++; } if (now.right != null ){ order.add(now.right); count++; } } int [] res = new int [count]; int i = 0 ; for (TreeNode node : order) { res[i++] = node.val; } return res; }
32-II从上到下打印二叉树 II 要按层来存,即 List<List<Integer>>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public List<List<Integer>> levelOrder (TreeNode root) { List<List<Integer>> res = new LinkedList <>(); if (root == null ){ return res; } Deque<TreeNode> order = new LinkedList <>(); order.addLast(root); while (!order.isEmpty()){ LinkedList<Integer> line = new LinkedList <>(); for (int i = order.size(); i > 0 ; i--){ TreeNode now = order.poll(); if (now.left != null ) order.addLast(now.left); if (now.right != null ) order.addLast(now.right); line.addLast(now.val); } res.add(line); } return res; }
32-III从上到下打印二叉树 III 这题在上面的基础上添加了蛇形打印
只要多一个判断 flag
即可
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 33 public List<List<Integer>> levelOrder (TreeNode root) { List<List<Integer>> res = new LinkedList <>(); if (root == null ) return res; Deque<TreeNode> order = new LinkedList <>(); order.addLast(root); boolean flag = true ; while (!order.isEmpty()){ LinkedList<Integer> line = new LinkedList <>(); for (int i = order.size(); i > 0 ; i--){ TreeNode now = order.poll(); if (now.left != null ){ order.addLast(now.left); } if (now.right != null ){ order.addLast(now.right); } if (flag){ line.addLast(now.val); }else { line.addFirst(now.val); } } flag = !flag; res.add(line); } return res; }
33二叉搜索树的后序遍历序列 后序遍历,最后一个是根节点
递归,终点是数组只剩下最多两个数
找到左子树和右子树,继续递归分治
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public boolean verifyPostorder (int [] postorder) { return myVerifyPostorder(postorder, 0 , postorder.length - 1 ); } private boolean myVerifyPostorder (int [] postorder, int start, int end) { if (start >= end - 1 ) return true ; int temp = start; while (postorder[temp] < postorder[end]) temp++; int middle = temp; while (postorder[temp] > postorder[end]) temp++; return temp = = end && myVerifyPostorder(postorder, start, middle - 1 ) && myVerifyPostorder(postorder, middle, end - 1 ); }
34二叉树中和为某一值的路径 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { LinkedList<List<Integer>> res = new LinkedList <>(); LinkedList<Integer> path = new LinkedList <>(); public List<List<Integer>> pathSum (TreeNode root, int sum) { recur(root, sum); return res; } void recur (TreeNode root, int tar) { if (root == null ) return ; path.add(root.val); tar -= root.val; if (tar == 0 && root.left == null && root.right == null ) res.add(new LinkedList (path)); recur(root.left, tar); recur(root.right, tar); path.removeLast(); } }
35复杂链表的复制 自己做,也是用哈希表,但用的是递归,结果直接栈溢出。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public Node copyRandomList (Node head) { HashMap<Node, Node> map = new HashMap <>(); Node cur = head; while (cur != null ) { map.put(cur, new Node (cur.val)); cur = cur.next; } cur = head; while (cur != null ) { map.get(cur).next = map.get(cur.next); map.get(cur).random = map.get(cur.random); cur = cur.next; } return map.get(head); } }
36二叉搜索树与双向链表 想到了中序遍历,但是没想到怎么转换引用
题解
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 class Solution { Node pre, head; public Node treeToDoublyList (Node root) { if (root == null ) return null ; dfs(root); head.left = pre; pre.right = head; return head; } void dfs (Node cur) { if (cur == null ) return ; dfs(cur.left); if (pre != null ) pre.right = cur; else head = cur; cur.left = pre; pre = cur; dfs(cur.right); } }
37序列化二叉树 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class Codec { public String serialize (TreeNode root) { if (root == null ) { return "[]" ; } StringBuilder res = new StringBuilder ("[" ); Deque<TreeNode> list = new LinkedList <>(); list.addLast(root); while (!list.isEmpty()) { TreeNode now = list.poll(); if (now != null ){ list.addLast(now.left); list.addLast(now.right); res.append(now.val).append("," ); } else { res.append("null," ); } } res.deleteCharAt(res.length() - 1 ).append("]" ); return res.toString(); } public TreeNode deserialize (String data) { if (data.equals("[]" )){ return null ; } String[] myData = data.substring(1 , data.length() - 1 ).split("," ); TreeNode root = new TreeNode (Integer.parseInt(myData[0 ])); Deque<TreeNode> list = new LinkedList <>(); list.addLast(root); int i = 1 ; while (!list.isEmpty()){ TreeNode now = list.poll(); if (!myData[i].equals("null" )){ now.left = new TreeNode (Integer.parseInt(myData[i])); list.addLast(now.left); } i++; if (!myData[i].equals("null" )){ now.right = new TreeNode (Integer.parseInt(myData[i])); list.addLast(now.right); } i++; } return root; } }
38字符串的排列 使用回溯,采用交换
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 class Solution { List<String> res = new LinkedList <>(); char [] c; public String[] permutation(String s) { c = s.toCharArray(); dfs(0 ); return res.toArray(new String [res.size()]); } void dfs (int x) { if (x == c.length - 1 ) { res.add(String.valueOf(c)); return ; } HashSet<Character> set = new HashSet <>(); for (int i = x; i < c.length; i++) { if (set.contains(c[i])) continue ; set.add(c[i]); swap(i, x); dfs(x + 1 ); swap(i, x); } } void swap (int a, int b) { char tmp = c[a]; c[a] = c[b]; c[b] = tmp; } }
39数组中出现次数超过一半的数字 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public int majorityElement (int [] nums) { int x = 0 , votes = 0 ; for (int num : nums){ if (votes == 0 ) x = num; votes += num == x ? 1 : -1 ; } return x; }
40最小的k个数(TopK) 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 class Solution { public int [] getLeastNumbers(int [] arr, int k) { randomizedSelected(arr, 0 , arr.length - 1 , k); int [] vec = new int [k]; for (int i = 0 ; i < k; ++i) { vec[i] = arr[i]; } return vec; } public void randomizedSelected (int [] arr, int l, int r, int k) { if (l >= r) { return ; } int pos = randomizedPartition(arr, l, r); int num = pos - l + 1 ; if (k == num) { return ; } else if (k < num) { randomizedSelected(arr, l, pos - 1 , k); } else { randomizedSelected(arr, pos + 1 , r, k - num); } } public int randomizedPartition (int [] nums, int l, int r) { int i = new Random ().nextInt(r - l + 1 ) + l; swap(nums, r, i); return partition(nums, l, r); } public int partition (int [] nums, int l, int r) { int pivot = nums[r]; int i = l - 1 ; for (int j = l; j <= r - 1 ; ++j) { if (nums[j] <= pivot) { i = i + 1 ; swap(nums, i, j); } } swap(nums, i + 1 , r); return i + 1 ; } private void swap (int [] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } }
41数据流中的中位数 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 class MedianFinder { PriorityQueue<Integer> right; PriorityQueue<Integer> left; public MedianFinder () { right = new PriorityQueue <>(); left = new PriorityQueue <>((a, b) -> b - a); } public void addNum (int num) { if (left.size() == right.size()){ left.add(num); right.add(left.poll()); }else { right.add(num); left.add(right.poll()); } } public double findMedian () { return left.size() == right.size() ? (left.peek() + right.peek()) / 2.0 : right.peek(); } }
42连续子数组的最大和 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public int maxSubArray (int [] nums) { int max = nums[0 ]; int former = 0 ; int cur = nums[0 ]; for (int num : nums) { cur = num; if (former > 0 ) cur += former; if (cur > max) max = cur; former = cur; } return max; }
45把数组排成最小的数 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 33 public String minNumber (int [] nums) { int length = nums.length; String[] strs = new String [length]; StringBuilder builder = new StringBuilder (); for (int i = 0 ; i < length; i++) { strs[i] = String.valueOf(nums[i]); } myQuickSort(strs, 0 , length - 1 ); for (String str : strs) { builder.append(str); } return builder.toString(); } public void myQuickSort (String[] strs, int left, int right) { if (left >= right) return ; int i = left, j = right; String temp = strs[i]; while (i < j) { while ((strs[j] + strs[left]).compareTo(strs[left] + strs[j]) >= 0 && i < j) j--; while ((strs[i] + strs[left]).compareTo(strs[left] + strs[i]) <= 0 && i < j) i++; temp = strs[i]; strs[i] = strs[j]; strs[j] = temp; } strs[i] = strs[left]; strs[left] = temp; myQuickSort(strs, left, i - 1 ); myQuickSort(strs, i + 1 , right); }
46把数字翻译成字符串 DP + 空间优化
解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public int translateNum (int num) { String s = String.valueOf(num); int a = 1 , b = 1 ; for (int i = 2 ; i <= s.length(); i++) { String tmp = s.substring(i - 2 , i); int c = tmp.compareTo("10" ) >= 0 && tmp.compareTo("25" ) <= 0 ? a + b : a; b = a; a = c; } return a; }
47礼物的最大价值 DP,有手就行
可把顶边和左边的先算一遍,就减少了 > 0
的判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public int maxValue (int [][] grid) { int row = grid.length; int col = grid[0 ].length; for (int i = 0 ; i < row; ++i){ for (int j = 0 ; j < col; ++j){ int up = 0 , left = 0 ; if (i > 0 ){ up = grid[i - 1 ][j]; } if (j > 0 ){ left = grid[i][j - 1 ]; } grid[i][j] = Math.max(grid[i][j] + left, grid[i][j] + up); } } return grid[row - 1 ][col - 1 ]; }
48最长不含重复字符的子字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public int lengthOfLongestSubstring (String s) { int r = -1 , res = 0 ; HashSet<Character> set = new HashSet <>(); int length = s.length(); for (int l = 0 ; l < length; l++) { if (l != 0 ) set.remove(s.charAt(l - 1 )); while (r + 1 < length && !set.contains(s.charAt(r + 1 ))){ set.add(s.charAt(r + 1 )); r++; } res = Math.max(res, r - l + 1 ); } return res; }
50第一个只出现一次的字符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public char firstUniqChar (String s) { int [] map = new int [26 ]; int length = s.length(); char [] ch = s.toCharArray(); for (int i = 0 ; i < length; ++i) { map[ch[i] - 'a' ]++; } for (int i = 0 ; i < length; ++i) { if (map[ch[i] - 'a' ] == 1 ) return ch[i]; } return ' ' ; }
51数组中的逆序对 难。
题解
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 class Solution { public int reversePairs (int [] nums) { int length = nums.length; if (length < 2 ) return 0 ; int [] copy = new int [length]; for (int i = 0 ; i < length; i++) { copy[i] = nums[i]; } int [] tmp = new int [length]; return reversePairs(copy, 0 , length - 1 , tmp); } private int reversePairs (int [] nums, int left, int right, int [] tmp) { if (left == right) return 0 ; int mid = left + (right - left) / 2 ; int leftPairs = reversePairs(nums, left, mid, tmp); int rightPairs = reversePairs(nums, mid + 1 , right, tmp); if (nums[mid] <= nums[mid + 1 ]) return leftPairs + rightPairs; int crossPairs = mergeAndCount(nums, left, mid, right, tmp); return leftPairs + rightPairs + crossPairs; } private int mergeAndCount (int [] nums, int left, int mid, int right, int [] tmp) { for (int i = left; i <= right; i++) { tmp[i] = nums[i]; } int i = left; int j = mid + 1 ; int count = 0 ; for (int k = left; k <= right; k++) { if (i == mid + 1 ) { nums[k] = tmp[j]; j++; } else if (j == right + 1 ) { nums[k] = tmp[i]; i++; } else if (tmp[i] <= tmp[j]) { nums[k] = tmp[i]; i++; } else { nums[k] = tmp[j]; j++; count += (mid - i + 1 ); } } return count; } }
52两个链表的第一个公共节点 太渣了,暴力解法来着
精选解法太妙了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public ListNode getIntersectionNode (ListNode headA, ListNode headB) { ListNode ATmp = headA; ListNode BTmp = headB; while (ATmp != BTmp){ ATmp = ATmp != null ? ATmp.next : headB; BTmp = BTmp != null ? BTmp.next : headA; } return ATmp; }
53-I在排序数组中查找数字 I 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public int search (int [] nums, int target) { return helper(nums, target) - helper(nums, target - 1 ); } int helper (int [] nums, int tar) { int i = 0 , j = nums.length - 1 ; while (i <= j) { int m = (i + j) / 2 ; if (nums[m] <= tar) i = m + 1 ; else j = m - 1 ; } return i; } }
53-II 0~n-1中缺失的数字 1 2 3 4 5 6 7 8 9 10 11 public int missingNumber (int [] nums) { int i = 0 , j = nums.length - 1 ; while (i <= j) { int m = (i + j) / 2 ; if (nums[m] == m) i = m + 1 ; else j = m - 1 ; } return i; }
54二叉搜索树的第k大节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { int res, k; public int kthLargest (TreeNode root, int k) { this .k = k; myKthLargest(root); return res; } private void myKthLargest (TreeNode root) { if (root == null ) return ; myKthLargest(root.right); if (k == 0 ) return ; if (--k == 0 ) res = root.val; myKthLargest(root.left); } }
55-I二叉树的深度 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 public int maxDepth (TreeNode root) { if (root == null ) return 0 ; List<TreeNode> queue = new LinkedList <>() {{ add(root); }}, tmp; int res = 0 ; while (!queue.isEmpty()) { tmp = new LinkedList <>(); for (TreeNode node : queue) { if (node.left != null ) tmp.add(node.left); if (node.right != null ) tmp.add(node.right); } queue = tmp; res++; } return res; }
56-I数组中数字出现的次数 I 这tm才是位运算。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public int [] singleNumbers(int [] nums) { int ret = 0 ; for (int n : nums) { ret ^= n; } int div = 1 ; while ((div & ret) == 0 ) { div <<= 1 ; } int a = 0 , b = 0 ; for (int n : nums) { if ((div & n) != 0 ) { a ^= n; } else { b ^= n; } } return new int []{a, b}; }
56-II数组中数字出现的次数 II 1 2 3 4 5 6 7 8 public int singleNumber (int [] nums) { int ones = 0 , twos = 0 ; for (int num : nums) { ones = ones ^ num & ~twos; twos = twos ^ num & ~ones; } return ones; }
57 和为s的两个数字 傻了,我就直接遍历 + 二分了,淦
优解:对撞双指针( 排序数组,可先考虑双指针 )
题解
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class Solution { public int [] twoSum(int [] nums, int target) { int i = 0 , j = nums.length - 1 ; while (i < j) { int s = nums[i] + nums[j]; if (s < target) i++; else if (s > target) j--; else return new int []{nums[i], nums[j]}; } return new int [0 ]; } }
57-II 和为s的连续正数序列 还是双指针,但不是对撞了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public int [][] findContinuousSequence(int target) { int start = 1 , end = 2 ; LinkedList<int []> res = new LinkedList <>(); while (start < end){ int sum = (start + end) * (end - start + 1 ) / 2 ; if (sum == target){ int [] sub = new int [end - start + 1 ]; for (int i = 0 ; i < (end - start + 1 ); i++){ sub[i] = start + i; } res.add(sub); start++; }else if (sum < target){ end++; }else { start++; } } return res.toArray(new int [res.size()][]); }