动态规划

  • 基本思想:问题的最优解如果可以由子问题的最优解推导得到,则可以先求解子问题的最优解,在构造原问题的最优解;若子问题有较多的重复出现,则可以自底向上从最终子问题向原问题逐步求解。

1.具有相同的子问题:我们必须保证我们分割成的子问题也能按照相同的方法分割成更小的自问题, 并这些自问题的最终分割情况是可以解决的。
2.满足最优子结构:就是一个决策的子决策也是最优的
3.无后效性:这是DP中最重要的一点, 他要求每个子问题的决策不能对后面其他未解决的问题产影响, 如果产生就无法保证决策的最优性, 这就是无后效性。往往需要我们找到一个合适的状态。


自己设计HashMap

  1. class MyHashMap {
  2. private class Pair {
  3. private int key;
  4. private int value;
  5. public Pair(int key, int value) {
  6. this.key = key;
  7. this.value = value;
  8. }
  9. public int getKey() {
  10. return key;
  11. }
  12. public int getValue() {
  13. return value;
  14. }
  15. public void setValue(int value) {
  16. this.value = value;
  17. }
  18. }
  19. private static final int BASE = 769;
  20. private LinkedList[] data;
  21. public MyHashMap() {
  22. data = new LinkedList[BASE];
  23. for (int i = 0; i < BASE; ++i) {
  24. data[i] = new LinkedList<Pair>();
  25. }
  26. }
  27. public void put(int key, int value) {
  28. int h = hash(key);
  29. Iterator<Pair> iterator = data[h].iterator();
  30. while (iterator.hasNext()) {
  31. Pair pair = iterator.next();
  32. if (pair.getKey() == key) {
  33. pair.setValue(value);
  34. return;
  35. }
  36. }
  37. data[h].offerLast(new Pair(key, value));
  38. }
  39. public int get(int key) {
  40. int h = hash(key);
  41. Iterator<Pair> iterator = data[h].iterator();
  42. while (iterator.hasNext()) {
  43. Pair pair = iterator.next();
  44. if (pair.getKey() == key) {
  45. return pair.value;
  46. }
  47. }
  48. return -1;
  49. }
  50. public void remove(int key) {
  51. int h = hash(key);
  52. Iterator<Pair> iterator = data[h].iterator();
  53. while (iterator.hasNext()) {
  54. Pair pair = iterator.next();
  55. if (pair.key == key) {
  56. data[h].remove(pair);
  57. return;
  58. }
  59. }
  60. }
  61. private static int hash(int key) {
  62. return key % BASE;
  63. }
  64. }

1. 两数之和

  1. class Solution {
  2. public int[] twoSum(int[] nums, int target) {
  3. Map<Integer, Integer> map = new HashMap<>();
  4. for (int i = 0; i < nums.length; i++) {
  5. if (map.containsKey(target - nums[i])) {
  6. return new int[]{i, map.get(target - nums[i])};
  7. }
  8. map.put(nums[i], i);
  9. }
  10. return new int[]{-1, -1};
  11. }
  12. }

2. 两数相加

  1. class Solution {
  2. public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
  3. ListNode dummy = new ListNode(-1);
  4. ListNode cur = dummy;
  5. int carry = 0;
  6. while (l1 != null || l2 != null || carry != 0) {
  7. int x = l1 == null ? 0 : l1.val;
  8. int y = l2 == null ? 0 : l2.val;
  9. int sum = x + y + carry;
  10. ListNode newHead = new ListNode(sum % 10);
  11. cur.next = newHead;
  12. cur = newHead;
  13. carry = sum / 10;
  14. if (l1 != null) l1 = l1.next;
  15. if (l2 != null) l2 = l2.next;
  16. }
  17. return dummy.next;
  18. }
  19. }

3. 无重复字符的最长子串

  1. class Solution {
  2. public int lengthOfLongestSubstring(String s) {
  3. Map<Character, Integer> window = new HashMap<>();
  4. int left = 0, right = 0;
  5. int maxLen = 0;
  6. while (right < s.length()) {
  7. char c = s.charAt(right);
  8. window.put(c, window.getOrDefault(c, 0) + 1);
  9. right++;
  10. while (window.get(c) > 1) {
  11. char rc = s.charAt(left);
  12. window.put(rc, window.get(rc) - 1);
  13. left++;
  14. }
  15. maxLen = Math.max(maxLen, right - left);
  16. }
  17. return maxLen;
  18. }
  19. }

215. 数组中的第K个最大元素

  1. class Solution {
  2. public int findKthLargest(int[] nums, int k) {
  3. PriorityQueue<Integer> pq = new PriorityQueue<>();
  4. for (int val : nums) {
  5. if (pq.size() < k) {
  6. pq.offer(val);
  7. } else if (val > pq.peek()) {
  8. pq.poll();
  9. pq.offer(val);
  10. }
  11. }
  12. return pq.peek();
  13. }
  14. }

25. K 个一组翻转链表

  1. class Solution {
  2. public int findKthLargest(int[] nums, int k) {
  3. PriorityQueue<Integer> pq = new PriorityQueue<>();
  4. for (int val : nums) {
  5. if (pq.size() < k) {
  6. pq.offer(val);
  7. } else if (val > pq.peek()) {
  8. pq.poll();
  9. pq.offer(val);
  10. }
  11. }
  12. return pq.peek();
  13. }
  14. }

146. LRU 缓存机制

微信图片_20210727113737.jpg
重点在makeRecently——remove之后再put

  1. class LRUCache {
  2. int cap;
  3. //有序的双向链表
  4. LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<>();
  5. public LRUCache(int capacity) {
  6. this.cap = capacity;
  7. }
  8. //get操作会有一次makeRecently
  9. public int get(int key) {
  10. if (!cache.containsKey(key)) {
  11. return -1;
  12. }
  13. makeRecently(key);
  14. return cache.get(key);
  15. }
  16. //put操作如果key存在的话会更新值再做makerecently
  17. //如果不存在且大于容量会通过迭代器remove掉OldKey
  18. public void put(int key, int val) {
  19. if (cache.containsKey(key)) {
  20. cache.put(key, val);
  21. makeRecently(key);
  22. return;
  23. }
  24. if (cache.size() >= this.cap) {
  25. // 链表头部就是最久未使用的 key
  26. int oldKey = cache.keySet().iterator().next();
  27. cache.remove(oldKey);
  28. }
  29. // 将新的 key 添加链表尾部
  30. cache.put(key, val);
  31. }
  32. private void makeRecently(int key) {
  33. int val = cache.get(key);
  34. // 删除 key,重新插入到队尾
  35. cache.remove(key);
  36. cache.put(key, val);
  37. }
  38. }
  1. class LRUCache {
  2. private HashMap<Integer, Node> map;
  3. private DoubleList cache;
  4. private int cap;
  5. public LRUCache(int capacity) {
  6. this.cap = capacity;
  7. map = new HashMap<>();
  8. cache = new DoubleList();
  9. }
  10. public int get(int key) {
  11. if (!map.containsKey(key))
  12. return -1;
  13. int val = map.get(key).val;//Node.val
  14. // 利用 put 方法把该数据提前
  15. put(key, val);
  16. return val;
  17. }
  18. public void put(int key, int val) {
  19. // 先把新节点 x 做出来
  20. Node x = new Node(key, val);
  21. if (map.containsKey(key)) {
  22. // 删除旧的节点,新的插到头部
  23. cache.remove(map.get(key));
  24. cache.addFirst(x);
  25. // 更新 map 中对应的数据
  26. map.put(key, x);
  27. } else {
  28. if (cap == cache.size()) {
  29. // 删除链表最后一个数据
  30. Node last = cache.removeLast();
  31. map.remove(last.key);
  32. }
  33. // 直接添加到头部
  34. cache.addFirst(x);
  35. map.put(key, x);
  36. }
  37. }
  38. }
  39. class Node {
  40. public int key, val;
  41. public Node next, prev;
  42. public Node(int k, int v) {
  43. this.key = k;
  44. this.val = v;
  45. }
  46. }
  47. class DoubleList {
  48. // 头尾虚节点
  49. private Node head, tail;
  50. private int size;
  51. public DoubleList() {
  52. // 初始化双向链表的数据
  53. head = new Node(0, 0);
  54. tail = new Node(0, 0);
  55. head.next = tail;
  56. tail.prev = head;
  57. size = 0;
  58. }
  59. // 在链表尾部添加节点 x,时间 O(1)
  60. public void addLast(Node x) {
  61. x.prev = tail.prev;
  62. x.next = tail;
  63. tail.prev.next = x;
  64. tail.prev = x;
  65. size++;
  66. }
  67. // 删除链表中的 x 节点(x 一定存在)
  68. public void remove(Node x) {
  69. x.prev.next = x.next;
  70. x.next.prev = x.prev;
  71. size--;
  72. }
  73. // 删除链表中第一个节点,并返回该节点,时间 O(1)
  74. public Node removeFirst() {
  75. if (head.next == tail)
  76. return null;
  77. Node first = head.next;
  78. remove(first);
  79. return first;
  80. }
  81. // 返回链表长度,时间 O(1)
  82. public int size() { return size; }
  83. }

15. 三数之和

  1. class Solution {
  2. public List<List<Integer>> threeSum(int[] nums) {
  3. List<List<Integer>> ret = new ArrayList<>();
  4. int len = nums.length;
  5. if (nums == null || len < 3) {
  6. return ret;
  7. }
  8. Arrays.sort(nums);
  9. for (int i = 0; i < len; i++) {
  10. int L = i + 1;
  11. int R = len - 1;
  12. if (i > 0 && nums[i] == nums[i - 1]) continue;
  13. while (L < R) {
  14. int sum = nums[i] + nums[L] + nums[R];
  15. if (sum == 0) {
  16. ret.add(Arrays.asList(nums[i],nums[L],nums[R]));
  17. while (L < R && nums[L] == nums[L + 1]) L++;
  18. while (L < R && nums[R] == nums[R - 1]) R--;
  19. L++;
  20. R--;
  21. } else if (sum < 0){
  22. L++;
  23. } else {
  24. R--;
  25. }
  26. }
  27. }
  28. return ret;
  29. }
  30. }

103. 二叉树的锯齿形层序遍历

  1. class Solution {
  2. public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
  3. List<List<Integer>> ret = new ArrayList<>();
  4. if (root == null) return ret;
  5. Queue<TreeNode> queue = new LinkedList<>();
  6. queue.add(root);
  7. while (!queue.isEmpty()) {
  8. int cnt = queue.size();
  9. List<Integer> temp = new ArrayList<>();
  10. while (cnt-- > 0) {
  11. TreeNode node = queue.poll();
  12. temp.add(node.val);
  13. if (node.left != null) queue.add(node.left);
  14. if (node.right != null) queue.add(node.right);
  15. }
  16. if (ret.size() % 2 == 0) {
  17. ret.add(new ArrayList<>(temp));
  18. } else {
  19. Collections.reverse(temp);
  20. ret.add(new ArrayList<>(temp));
  21. }
  22. }
  23. return ret;
  24. }
  25. }

160. 相交链表

  1. public class Solution {
  2. public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
  3. if (headA == null || headB == null) return null;
  4. ListNode pA = headA, pB = headB;
  5. while (pA != pB) {
  6. pA = pA == null ? headB : pA.next;
  7. pB = pB == null ? headA : pB.next;
  8. }
  9. return pA;
  10. }
  11. }

236. 二叉树的最近公共祖先

想象一个最简单场景——p, q为root的左右子树,则left和right都不为null
都为null则返回null

  1. class Solution {
  2. public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
  3. if (root == null || root == p || root == q) return root;
  4. TreeNode left = lowestCommonAncestor(root.left, p, q);
  5. TreeNode right = lowestCommonAncestor(root.right, p, q);
  6. if (left == null && right == null) return null;
  7. else if (left != null && right != null) return root;
  8. else return left == null ? right : left;
  9. }
  10. }

42. 接雨水

  1. class Solution {
  2. public int trap(int[] height) {
  3. int len = height.length;
  4. int[] max_r = new int[len];
  5. int[] max_l = new int[len];
  6. int res = 0;
  7. for (int i = 1; i < len; i++) {
  8. max_l[i] = Math.max(max_l[i - 1], height[i - 1]);
  9. }
  10. for (int j = len - 2; j >= 0; j--) {
  11. max_r[j] = Math.max(max_r[j + 1], height[j + 1]);
  12. }
  13. for (int i = 0; i < len; i++) {
  14. int min = Math.min(max_l[i], max_r[i]);
  15. if (min <= height[i]) continue;
  16. else {
  17. res += min - height[i];
  18. }
  19. }
  20. return res;
  21. }
  22. }

优化:空间复杂度降为O(1)——双指针

  1. public int trap(int[] height) {
  2. if (height.length == 0) return 0;
  3. int sum = 0;
  4. int n = height.length;
  5. int left = 0, right = n - 1;
  6. int l_max = height[0];
  7. int r_max = height[n - 1];
  8. while (left <= right) {
  9. l_max = Math.max(l_max, height[left]);
  10. r_max = Math.max(r_max, height[right]);
  11. if (l_max < r_max) {
  12. sum += l_max - height[left];
  13. left++;
  14. } else {
  15. sum += r_max - height[right];
  16. right--;
  17. }
  18. }
  19. return sum;
  20. }

415. 字符串相加

  1. class Solution {
  2. public String addStrings(String num1, String num2) {
  3. StringBuilder sb = new StringBuilder();
  4. int carry = 0, i = num1.length() - 1, j = num2.length - 1;
  5. while (i >= 0 || j >= 0 || carry != 0) {
  6. if(i>=0) carry += num1.charAt(i)-'0';
  7. if(j>=0) carry += num2.charAt(j)-'0';
  8. sb.append(carry%10);
  9. carry /= 10;
  10. i--;
  11. j--;
  12. }
  13. return sb.reverse().toString();
  14. }
  15. }

54. 螺旋矩阵

  1. class Solution {
  2. public List<Integer> spiralOrder(int[][] matrix) {
  3. List<Integer> res = new ArrayList<>();
  4. if (matrix == null || matrix.length == 0) return res;
  5. int t = 0;
  6. int l = 0;
  7. int b = matrix.length - 1;
  8. int r = matrix[0].length - 1;
  9. while (true) {
  10. for (int i = l; i <= r; i++) res.add(matrix[t][i]);
  11. if (++t > b) break;
  12. for (int i = t; i <= b; i++) res.add(matrix[i][r]);
  13. if (--r < l) break;
  14. for (int i = r; i >= l; i--) res.add(matrix[b][i]);
  15. if (--b < t) break;
  16. for (int i = b; i >= t; i--) res.add(matrix[i][l]);
  17. if (++l > r) break;
  18. }
  19. return res;
  20. }
  21. }

88. 合并两个有序数组

  1. class Solution {
  2. public void merge(int[] nums1, int m, int[] nums2, int n) {
  3. int len1 = m - 1;
  4. int len2 = n - 1;
  5. int len = m + n - 1;
  6. while (len1 >= 0 && len2 >= 0) {
  7. nums1[len--] = nums1[len1] > nums2[len2] ? nums1[len1--] : nums2[len2--];
  8. }
  9. //如果走完nums1 nums2里面的元素还没有转移完,一定是比之前比较和和转移小的元素
  10. //直接拷贝到nums1的前面
  11. while (len2 >= 0) {
  12. nums1[len--] = nums2[len2--];
  13. }
  14. }
  15. }

33. 搜索旋转排序数组

  1. class Solution {
  2. public int search(int[] nums, int target) {
  3. int lo = 0, hi = nums.length - 1;
  4. while (lo <= hi) {
  5. int mid = lo + (hi - lo) / 2;
  6. if (nums[mid] == target) {
  7. return mid;
  8. }
  9. // 先根据 nums[mid] 与 nums[lo] 的关系判断 mid 是在左段还是右段
  10. if (nums[mid] >= nums[lo]) {
  11. // 再判断 target 是在 mid 的左边还是右边,从而调整左右边界 lo 和 hi
  12. if (target >= nums[lo] && target < nums[mid]) {
  13. hi = mid - 1;
  14. } else {
  15. lo = mid + 1;
  16. }
  17. } else {
  18. if (target > nums[mid] && target <= nums[hi]) {
  19. lo = mid + 1;
  20. } else {
  21. hi = mid - 1;
  22. }
  23. }
  24. }
  25. return -1;
  26. }
  27. }

200. 岛屿数量

  1. class Solution {
  2. private int m, n;
  3. private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
  4. public int numIslands(char[][] grid) {
  5. m = grid.length;
  6. n = grid[0].length;
  7. int count = 0;
  8. for (int i = 0; i < m; i++) {
  9. for (int j = 0; j < n; j++) {
  10. if (grid[i][j] == '1') {
  11. dfs(grid, i, j);
  12. count++;
  13. }
  14. }
  15. }
  16. return count;
  17. }
  18. private void dfs(char[][] grid, int r, int c) {
  19. if (r < 0 || r >= m || c < 0 || c >= n || grid[r][c] != '1') {
  20. return;
  21. }
  22. grid[r][c] = '2';
  23. for (int[] d : direction) {
  24. dfs(grid, r + d[0], c + d[1]);
  25. }
  26. }
  27. }

199. 二叉树的右视图

  1. class Solution {
  2. public List<Integer> rightSideView(TreeNode root) {
  3. List<Integer> res = new ArrayList<>();
  4. if (root == null) {
  5. return res;
  6. }
  7. Queue<TreeNode> queue = new LinkedList<>();
  8. queue.add(root);
  9. while (!queue.isEmpty()) {
  10. int cnt = queue.size();
  11. while (cnt-- > 0) {
  12. TreeNode temp = queue.poll();
  13. if (cnt == 0) {
  14. res.add(temp.val);
  15. }
  16. if (temp.left != null) queue.add(temp.left);
  17. if (temp.right != null) queue.add(temp.right);
  18. }
  19. }
  20. return res;
  21. }
  22. }

31. 下一个排列

  1. class Solution {
  2. public void nextPermutation(int[] nums) {
  3. int len = nums.length;
  4. int firstIndex = -1;
  5. //1.需要将后面大数的与前面小数的交换
  6. for (int i = len - 2; i >= 0; i--) {
  7. if (nums[i] < nums[i + 1]) {
  8. firstIndex = i;
  9. break;
  10. }
  11. }
  12. //2.如果不存在逆序整个数组
  13. if (firstIndex == -1) {
  14. reverse(nums, 0, nums.length - 1);
  15. return;
  16. }
  17. int secondIndex = -1;
  18. //3.从后往前找尽可能的大数,因为firstindex后面的是降序
  19. for (int i = len - 1; i >= 0; i--) {
  20. if (nums[i] > nums[firstIndex]) {
  21. secondIndex = i;
  22. break;
  23. }
  24. }
  25. //4.交换大数与小数
  26. swap(nums, firstIndex, secondIndex);
  27. //5.firstindex后面是降序,reverse使其升序
  28. reverse(nums, firstIndex + 1, nums.length - 1);
  29. }
  30. private void reverse(int[] nums, int i, int j) {
  31. while (i < j) {
  32. swap(nums, i++, j--);
  33. }
  34. }
  35. private void swap(int[] nums, int i, int i1) {
  36. int tmp = nums[i];
  37. nums[i] = nums[i1];
  38. nums[i1] = tmp;
  39. }
  40. }

102. 二叉树的层序遍历

  1. class Solution {
  2. public List<List<Integer>> levelOrder(TreeNode root) {
  3. List<List<Integer>> ret = new ArrayList<>();
  4. if (root == null) return ret;
  5. Queue<TreeNode> queue = new LinkedList<>();
  6. queue.add(root);
  7. while (!queue.isEmpty()) {
  8. int cnt = queue.size();
  9. List<Integer> temp = new ArrayList<>();
  10. while (cnt-- > 0) {
  11. TreeNode node = queue.poll();
  12. temp.add(node.val);
  13. if (node.left != null) queue.add(node.left);
  14. if (node.right != null) queue.add(node.right);
  15. }
  16. ret.add(new ArrayList<>(temp));
  17. }
  18. return ret;
  19. }
  20. }

300. 最长递增子序列

  1. class Solution {
  2. public int lengthOfLIS(int[] nums) {
  3. int n = nums.length;
  4. if (n < 2) {
  5. return n;
  6. }
  7. int[] dp = new int[n];
  8. Arrays.fill(dp, 1);
  9. int res = 0;
  10. for (int i = 1; i < n; i++) {
  11. for (int j = 0; j < i; j++) {
  12. if (nums[i] > nums[j]) {
  13. dp[i] = Math.max(dp[i], dp[j] + 1);
  14. }
  15. }
  16. res = Math.max(res, dp[i]);
  17. }
  18. return res;
  19. }
  20. }

105. 从前序与中序遍历序列构造二叉树

  1. class Solution {
  2. public TreeNode buildTree(int[] preorder, int[] inorder) {
  3. return traverse(preorder, 0, preorder.length - 1,
  4. inorder, 0, inorder.length - 1);
  5. }
  6. private TreeNode traverse(int[] preorder, int pS, int pE,
  7. int[] inorder, int iS, int iE) {
  8. if (pS > pE) return null;
  9. int rootVal = preorder[pS];
  10. int index = -1;
  11. for (int i = iS; i <= iE; i++) {
  12. if (inorder[i] == rootVal) {
  13. index = i;
  14. break;
  15. }
  16. }
  17. TreeNode root = new TreeNode(rootVal);
  18. int leftL = index - iS;
  19. root.left = traverse(preorder, pS + 1, pS +leftL,
  20. inorder, iS, index - 1);
  21. root.right = traverse(preorder, pS +leftL + 1, pE,
  22. inorder, index + 1, iE);
  23. return root;
  24. }
  25. }

20. 有效的括号

  1. class Solution {
  2. public boolean isValid(String s) {
  3. Stack<Character> stack = new Stack<>();
  4. for (char c : s.toCharArray()) {
  5. if (c == '(') stack.push(')');
  6. else if (c == '[') stack.push(']');
  7. else if (c == '{') stack.push('}');
  8. //没有考虑stack.isEmpty(),当只有右括号的时候不会入栈,此时栈是空的,不用后面的判断语句就可返回false
  9. else if (stack.isEmpty() || c != stack.pop()) return false;
  10. }
  11. //只有左括号的情况
  12. return stack.isEmpty();
  13. }
  14. }

46. 全排列

  1. class Solution {
  2. List<List<Integer>> ret = new ArrayList<>();
  3. public List<List<Integer>> permute(int[] nums) {
  4. if (nums.length == 0) return ret;
  5. List<Integer> track = new ArrayList<>();
  6. boolean[] hasVisited = new boolean[nums.length];
  7. backtrack(nums, track, hasVisited);
  8. return ret;
  9. }
  10. private void backtrack(int[] nums, List<Integer> track, boolean[] visited) {
  11. if (track.size() == nums.length) {
  12. ret.add(new ArrayList<>(track));
  13. return;
  14. }
  15. for (int i = 0; i < nums.length; i++) {
  16. if (visited[i]) continue;
  17. visited[i] = true;
  18. track.add(nums[i]);
  19. backtrack(nums, track, visited);
  20. track.remove(track.size() - 1);
  21. visited[i] = false;
  22. }
  23. return;
  24. }
  25. }

56. 合并区间

  1. class Solution {
  2. public int[][] merge(int[][] intervals) {
  3. int len = intervals.length;
  4. Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);
  5. int[][] res = new int[len][2];
  6. int idx = -1;//索引
  7. for (int[] interval : intervals) {
  8. //判断遍历元素和结果数组
  9. if (idx == -1 || interval[0] > res[idx][1]) {
  10. res[++idx] = interval;
  11. } else {
  12. res[idx][1] = Math.max(res[idx][1], interval[1]);
  13. }
  14. }
  15. //因为合并区间之后会小于len,所以要截取要不然会有[0, 0]
  16. return Arrays.copyOf(res, idx + 1);
  17. }
  18. }

143. 重排链表

  1. class Solution {
  2. public void reorderList(ListNode head) {
  3. if (head == null || head.next == null) return;
  4. ListNode slow = head, fast = head.next;
  5. while (fast.next != null && fast.next != null) {
  6. slow = slow.next;
  7. fast = fast.next.next;
  8. }
  9. ListNode needReverse = reverse(slow.next);
  10. slow.next = null;
  11. while (needReverse != null) {
  12. ListNode next = head.next;
  13. ListNode needNext = needReverse.next;
  14. needReverse.next = head.next;
  15. head.next = needReverse;
  16. head = next;
  17. needReverse = needNext;
  18. }
  19. }
  20. private ListNode reverse(ListNode head) {
  21. if (head == null || head.next == null) return head;
  22. ListNode dummy = new ListNode(-1);
  23. while (head != null) {
  24. ListNode next = head.next;
  25. head.next = dummy.next;
  26. dummy.next = head;
  27. head = next;
  28. }
  29. return dummy.next;
  30. }
  31. }

112. 路径总和

  1. //错误写法
  2. class Solution {
  3. public boolean hasPathSum(TreeNode root, int targetSum) {
  4. if (root == null) return false;
  5. if (root.left == null && root.right == null && targetSum == root.val) {
  6. return true;
  7. }
  8. hasPathSum(root.left, targetSum - root.val);
  9. hasPathSum(root.right, targetSum - root.val);
  10. return false;
  11. }
  12. }
  13. //函数返回类型是boolean类型的,所以递归的时候直接返回就可以了
  14. //对递归理解还是不够
  15. return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);

129. 求根节点到叶节点数字之和

字节实习面试算法题

  1. public int sumNumbers(TreeNode root) {
  2. return helper(root, 0);
  3. }
  4. public int helper(TreeNode root, int i){
  5. if (root == null) return 0;
  6. int temp = i * 10 + root.val;
  7. if (root.left == null && root.right == null)
  8. return temp;
  9. return helper(root.left, temp) + helper(root.right, temp);
  10. }
  1. //思考每个节点需要做什么
  2. //如果是叶子节点,把该节点的值加到sum中
  3. //如果不是叶子节点,把左子树节点和右子树的节点加上该节点的值*10;
  4. private int sum = 0;
  5. public int sumNumbers(TreeNode root) {
  6. if (root == null) return sum;
  7. if (root.left == null && root.right == null) {
  8. sum += root.val;
  9. }
  10. if (root.left != null) root.left.val += root.val*10;
  11. if (root.right != null) root.right.val += root.val*10;
  12. sumNumbers(root.left);
  13. sumNumbers(root.right);
  14. return sum;
  15. }

69. x 的平方根

  1. class Solution {
  2. public int mySqrt(int x) {
  3. if (x <= 1) return x;
  4. int l = 1, h = x;
  5. while (l <= h) {
  6. int mid = l + (h - l) / 2;
  7. int sqrt = x / mid;
  8. if (sqrt == mid) {
  9. return mid;
  10. } else if (mid < sqrt){
  11. l = mid + 1;
  12. } else {
  13. h = mid - 1;
  14. }
  15. }
  16. return h;
  17. }
  18. }

23. 合并K个升序链表

  1. class Solution {
  2. public ListNode mergeKLists(ListNode[] lists) {
  3. if (lists.length == 0) return null;
  4. ListNode dummyHead = new ListNode(0);
  5. ListNode cur = dummyHead;
  6. PriorityQueue<ListNode> pq = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);
  7. for (ListNode list : lists) {
  8. if (list != null) {
  9. pq.add(list);
  10. }
  11. }
  12. while (!pq.isEmpty()) {
  13. ListNode nextnode = pq.poll();
  14. cur.next = nextnode;
  15. cur = cur.next;
  16. if (nextnode.next != null) {
  17. pq.add(nextnode.next);
  18. }
  19. }
  20. return dummyHead.next;
  21. }
  22. }

5. 最长回文子串

  1. class Solution {
  2. public String longestPalindrome(String s) {
  3. String res = "";
  4. int n = s.length();
  5. for (int i = 0; i < n; i++) {
  6. String t1 = palindrome(s, i, i + 1);
  7. String t2 = palindrome(s, i, i);
  8. res = res.length() > t1.length() ? res : t1;
  9. res = res.length() > t2.length() ? res : t2;
  10. }
  11. return res;
  12. }
  13. public String palindrome(String s, int i, int j) {
  14. while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) {
  15. i--;
  16. j++;
  17. }
  18. return s.substring(i + 1, j);
  19. }
  20. }

113. 路径总和 II

  1. class Solution {
  2. List<List<Integer>> ret = new ArrayList<>();
  3. public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
  4. if (root == null) return ret;
  5. List<Integer> track = new ArrayList<>();
  6. backtrack(root, targetSum, track);
  7. return ret;
  8. }
  9. private void backtrack(TreeNode root, int targetSum, List<Integer> track) {
  10. if (root == null) return ;
  11. track.add(root.val);
  12. targetSum -= root.val;
  13. if (targetSum == 0 && root.left == null && root.right == null) {
  14. ret.add(new ArrayList<>(track));
  15. }
  16. backtrack(root.left, targetSum, track);
  17. backtrack(root.right, targetSum, track);
  18. track.remove(track.size() - 1);
  19. }
  20. }

124. 二叉树中的最大路径和

  1. class Solution {
  2. private int ret = Integer.MIN_VALUE;
  3. public int maxPathSum(TreeNode root) {
  4. /**
  5. 对于任意一个节点, 如果最大和路径包含该节点, 那么只可能是两种情况:
  6. 1. 其左右子树中所构成的和路径值较大的那个加上该节点的值后向父节点回溯构成最大路径,不能都在,都在就会重复
  7. 2. 左右子树都在最大路径中, 加上该节点的值构成了最终的最大路径
  8. **/
  9. getMax(root);
  10. return ret;
  11. }
  12. private int getMax(TreeNode node) {
  13. if (node == null) return 0;
  14. int left = Math.max(0, getMax(node.left));//如果子树为负应该置0表示不包含子树
  15. int right = Math.max(0, getMax(node.right));
  16. ret = Math.max(ret, node.val + left + right);//判断在该节点包含左右子树的路径和是否大于当前最大路径和
  17. return Math.max(left, right) + node.val;// 返回经过root的单边最大分支给当前root的父节点计算使用
  18. }
  19. }

[

](https://leetcode-cn.com/problems/implement-queue-using-stacks/)

94. 二叉树的中序遍历

好好理解迭代——不太好懂

  1. public List<Integer> inorderTraversal(TreeNode root) {
  2. List<Integer> res = new ArrayList<Integer>();
  3. Stack<TreeNode> stack = new Stack<TreeNode>();
  4. while(stack.size()>0 || root!=null) {
  5. //不断往左子树方向走,每走一次就将当前节点保存到栈中
  6. //这是模拟递归的调用
  7. if(root!=null) {
  8. stack.add(root);
  9. root = root.left;
  10. //当前节点为空,说明左边走到头了,从栈中弹出节点并保存
  11. //然后转向右边节点,继续上面整个过程
  12. } else {
  13. TreeNode tmp = stack.pop();
  14. res.add(tmp.val);
  15. root = tmp.right;//这一句当时没理解
  16. }
  17. }
  18. return res;
  19. }

76. 最小覆盖子串

  1. 输入:s = "ADOBECODEBANC", t = "ABC"
  2. 输出:"BANC"
  1. class Solution {
  2. public String minWindow(String s, String t) {
  3. Map<Character, Integer> window = new HashMap<>();
  4. Map<Character, Integer> need = new HashMap<>();
  5. for (int i = 0; i < t.length(); i++) {
  6. char c = t.charAt(i);
  7. need.put(c, need.getOrDefault(c, 0) + 1);
  8. }
  9. int left = 0, right = 0, vaild = 0;
  10. int start = 0, len = Integer.MAX_VALUE;
  11. while (right < s.length()) {
  12. char addChar = s.charAt(right);
  13. window.put(addChar, window.getOrDefault(addChar, 0) + 1);
  14. right++;
  15. if (need.containsKey(addChar) && need.get(addChar).equals(window.get(addChar))) vaild++;
  16. while (vaild == need.size()) {
  17. if (right - left < len) {
  18. start = left;
  19. len = right - left;
  20. }
  21. char removeChar = s.charAt(left);
  22. left++;
  23. if (need.containsKey(removeChar) && window.get(removeChar).equals(need.get(removeChar))) vaild--;
  24. window.put(removeChar, window.get(removeChar) - 1);
  25. }
  26. }
  27. return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
  28. }
  29. }

958. 二叉树的完全性检验

  1. public boolean isCompleteTree(TreeNode root) {
  2. if (root == null) return true;
  3. Queue<TreeNode> queue = new LinkedList<>();
  4. queue.add(root);
  5. TreeNode temp;
  6. boolean flag = false;
  7. while (!queue.isEmpty()) {
  8. temp = queue.remove();
  9. if (temp == null) {
  10. flag = true;//如果遇到空,执行下一次
  11. continue;
  12. }
  13. if (flag) return false;//下一次不能有非空,否则不是完全树
  14. queue.add(temp.left);
  15. queue.add(temp.right);
  16. }
  17. return true;
  18. }

[

](https://leetcode-cn.com/problems/add-two-numbers/)
[

](https://leetcode-cn.com/problems/min-stack/)

39. 组合总和

  1. 输入: candidates = [2,3,6,7], target = 7
  2. 输出: [[7],[2,2,3]]
  1. class Solution {
  2. List<List<Integer>> ret = new ArrayList<>();
  3. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  4. List<Integer> track = new ArrayList<>();
  5. backtrack(0, candidates, target, track);
  6. return ret;
  7. }
  8. private void backtrack(int index, int[] candidates, int target, List<Integer> track) {
  9. if (target == 0) {
  10. ret.add(new ArrayList<>(track));
  11. return;
  12. }
  13. for (int i = index; i < candidates.length; i++) {
  14. if (candidates[i] <= target) {
  15. track.add(candidates[i]);
  16. backtrack(i, candidates, target - candidates[i], track);
  17. track.remove(track.size() - 1);
  18. }
  19. }
  20. }
  21. }

98. 验证二叉搜索树

  1. class Solution {
  2. long pre = Long.MIN_VALUE;
  3. public boolean isValidBST(TreeNode root) {
  4. if (root == null) {
  5. return true;
  6. }
  7. // 访问左子树
  8. if (!isValidBST(root.left)) {
  9. return false;
  10. }
  11. // 访问当前节点:如果当前节点小于等于中序遍历的前一个节点,说明不满足BST,返回 false;否则继续遍历。
  12. if (root.val <= pre) {
  13. return false;
  14. }
  15. pre = root.val;
  16. // 访问右子树
  17. return isValidBST(root.right);
  18. }
  19. }

19. 删除链表的倒数第 N 个结点

  1. class Solution {
  2. public ListNode removeNthFromEnd(ListNode head, int n) {
  3. ListNode fast = head;
  4. while (n-- >0) {
  5. fast = fast.next;
  6. }
  7. if (fast == null) return head.next;
  8. ListNode slow = head;
  9. while (fast.next != null) {
  10. fast = fast.next;
  11. slow = slow.next;
  12. }
  13. slow.next = slow.next.next;
  14. return head;
  15. }
  16. }

32. 最长有效括号

  1. class Solution {
  2. public int longestValidParentheses(String s) {
  3. int max=0;//存放最大的长度
  4. int len=s.length();//字符串长度
  5. Stack<Integer> stack=new Stack<Integer>();//暂存字符
  6. stack.push(-1);//初始化栈底
  7. for(int i=0;i<len;i++) {
  8. if(s.charAt(i)=='(')//字符串存在(
  9. stack.push(i);
  10. else {
  11. stack.pop();//下标出栈
  12. if(stack.isEmpty()) {//出栈以后,栈为空
  13. stack.push(i);
  14. }else {
  15. max=Math.max(max, i-stack.peek());//选出最长的长度
  16. }
  17. }
  18. }
  19. return max;
  20. }
  21. }

思想:栈——左括号入栈,右括号是让栈里的元素出栈计算长度,如果出栈后栈为空,则让当前元素入栈。
注意:栈中元素是下标

209. 长度最小的子数组

滑动窗口——当sum大于等于target时计算长度移动左指针

  1. class Solution {
  2. //滑动窗口
  3. //使用 while 循环时对左侧窗口在哪里进行移动,在计算结果之前移动还是计算结果之后移动,里面有很大的差别,
  4. //这也就是和很多题解中使用 for 循环时最后计算结果有 right - left + 1 存在的原因 。
  5. public int minSubArrayLen(int target, int[] nums) {
  6. int left = 0, right = 0;
  7. int result = Integer.MAX_VALUE;
  8. int sum = 0;
  9. while (right < nums.length) {
  10. sum += nums[right];
  11. right++;
  12. while (sum >= target) {
  13. result = (result < right -left) ? result : right -left;
  14. sum -= nums[left];
  15. left++;
  16. }
  17. }
  18. return result == Integer.MAX_VALUE ? 0 : result;
  19. }
  20. }

221. 最大正方形

  1. class Solution {
  2. public int maximalSquare(char[][] matrix) {
  3. /**
  4. dp[i][j]表示matrix[i - 1][j - 1]为右下角所能构成的最大正方形边长, 则递推式为:
  5. dp[i][j] = 1 + min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]);
  6. **/
  7. int m = matrix.length;
  8. int n =matrix[0].length;
  9. int max = 0;
  10. int[][] dp = new int[m + 1][n + 1];
  11. for (int i = 1; i <= m; i++) {
  12. for (int j = 1; j <= n; j++) {
  13. if (matrix[i - 1][j - 1] == '1') {
  14. dp[i][j] = 1 + Math.min(dp[i-1][j-1], Math.min(dp[i-1][j], dp[i][j-1]));
  15. max = Math.max(max, dp[i][j]);
  16. }
  17. }
  18. }
  19. return max * max;
  20. }
  21. }

165. 比较版本号

  1. 输入:version1 = "1.01", version2 = "1.001"
  2. 输出:0
  3. 解释:忽略前导零,"01" "001" 都表示相同的整数 "1"
  1. class Solution {
  2. public int compareVersion(String version1, String version2) {
  3. int i = 0, j = 0;
  4. //每过一个段a和b重新计算
  5. char[] v1 = version1.toCharArray();
  6. char[] v2 = version2.toCharArray();
  7. while (i < version1.length() || j < version2.length()) {
  8. int a = 0, b= 0;
  9. while(i < version1.length() && v1[i] != '.') a = a * 10 + v1[i++] - '0';
  10. while(j < version2.length() && v2[j] != '.') b = b * 10 + v2[j++] - '0';
  11. if (a > b) return 1;
  12. else if (a < b) return -1;
  13. i++;
  14. j++;
  15. }
  16. return 0;
  17. }
  18. }

169. 多数元素

  1. class Solution {
  2. public int majorityElement(int[] nums) {
  3. int vote = 0, x = 0;
  4. for (int val : nums) {
  5. if (vote == 0) x = val;
  6. vote += val == x ? 1 : -1;
  7. }
  8. return x;
  9. }
  10. }

83. 删除排序链表中的重复元素

删除所有重复的元素,使每个元素 只出现一次

  1. class Solution {
  2. public ListNode deleteDuplicates(ListNode head) {
  3. if (head == null || head.next == null) return head;
  4. ListNode next = head.next;
  5. if (head.val == next.val) {
  6. while (next != null && head.val == next.val) {
  7. next = next.next;
  8. }
  9. head.next = deleteDuplicates(next);
  10. } else {
  11. head.next = deleteDuplicates(next);
  12. }
  13. return head;
  14. }
  15. }

82. 删除排序链表中的重复元素 II

重复元素需要删除

  1. class Solution {
  2. public ListNode deleteDuplicates(ListNode head) {
  3. if (head == null || head.next == null) return head;
  4. ListNode next = head.next;
  5. if (head.val == next.val) {
  6. while (next != null && head.val == next.val) {
  7. next = next.next;
  8. }
  9. head = deleteDuplicates(next);
  10. } else {
  11. head.next = deleteDuplicates(next);
  12. }
  13. return head;
  14. }
  15. }

48. 旋转图像

  1. //先上下,再对角线
  2. class Solution {
  3. public void rotate(int[][] matrix) {
  4. int m = matrix.length, n = matrix[0].length;
  5. for (int i = 0; i < m / 2; i++) {
  6. for (int j = 0; j < n; j++) {
  7. int t = matrix[i][j];
  8. matrix[i][j] = matrix[m - i - 1][j];
  9. matrix[m - i - 1][j] = t;
  10. }
  11. }
  12. for (int i =0; i < m; i++) {
  13. for (int j = 0; j < i; j++) {
  14. int t =matrix[i][j];
  15. matrix[i][j] = matrix[j][i];
  16. matrix[j][i] = t;
  17. }
  18. }
  19. }
  20. }

718. 最长重复子数组

  1. 输入:
  2. A: [1,2,3,2,1]
  3. B: [3,2,1,4,7]
  4. 输出:3
  5. 解释:
  6. 长度最长的公共子数组是 [3, 2, 1]
  1. class Solution {
  2. public int findLength(int[] nums1, int[] nums2) {
  3. if (nums1.length == 0 || nums2.length == 0) return 0;
  4. int m = nums1.length, n = nums2.length;
  5. int[][] dp = new int[m + 1][n + 1];//有索引偏移,dp[i][j]代表以A[i-1]与B[j-1]结尾的公共字串的长度,
  6. //数组中的m + 1和 n + 1是数组长度,而不是可以取到该索引处
  7. int result = 0;
  8. for (int i = 1; i <= m; i++) {
  9. for (int j = 1; j <= n; j++) {
  10. if (nums1[i - 1] == nums2[j - 1]) {
  11. dp[i][j] = dp[i - 1][j - 1] + 1;
  12. result = Math.max(result, dp[i][j]);
  13. }
  14. }
  15. }
  16. return result;
  17. }
  18. }

162. 寻找峰值

二分查找——判断nums[i]和nums[i + 1]的关系

  1. class Solution {
  2. public int findPeakElement(int[] nums) {
  3. int left = 0, right = nums.length - 1;
  4. while (left < right) {
  5. int mid = left + (right - left) / 2;
  6. if (nums[mid] > nums[mid + 1]) {
  7. right = mid;
  8. } else {
  9. left = mid + 1;
  10. }
  11. }
  12. return left;
  13. }
  14. }

0.36进制加法

  1. class Solution {
  2. public String addStrings(String num1, String num2) {
  3. StringBuilder sb = new StringBuilder();
  4. int carry = 0, i = num1.length() - 1, j = num2.length - 1;
  5. while (i >= 0 || j >= 0 || carry != 0) {
  6. if(i>=0) carry += getInt(num1.charAt(i));
  7. if(j>=0) carry += getInt(num2.charAt(j));
  8. sb.append(getChar(carry%36));
  9. carry /= 36;
  10. i--, j--;
  11. }
  12. return sb.reverse().toString();
  13. }
  14. private char getChar(int n) {
  15. if (n <= 9) return n + '0';
  16. else return n - 10 + 'a';
  17. }
  18. private int getInt(char ch) {
  19. if ('0' <= ch && ch <= '9')
  20. return ch - '0';
  21. else
  22. return ch - 'a' + 10;
  23. }
  24. }

148. 排序链表

使用归并排序,快慢指针找中点,拆分链表,再合并链表

  1. class Solution {
  2. public ListNode sortList(ListNode head) {
  3. if (head == null || head.next == null) return head;
  4. ListNode slow = head, fast = head.next;
  5. ListNode l, r;
  6. while (fast != null && fast.next != null) {
  7. slow = slow.next;
  8. fast = fast.next.next;
  9. }
  10. r = sortList(slow.next);
  11. slow.next = null;
  12. l = sortList(head);
  13. return merge(l, r);
  14. }
  15. private ListNode merge(ListNode l1, ListNode l2) {
  16. ListNode dummy = new ListNode(-1);
  17. ListNode p = dummy;
  18. while (l1 != null && l2 != null) {
  19. if (l1.val < l2.val) {
  20. p.next = l1;
  21. l1 = l1.next;
  22. } else {
  23. p.next = l2;
  24. l2 = l2.next;
  25. }
  26. p = p.next;
  27. }
  28. p.next = l1 == null ? l2 : l1;
  29. return dummy.next;
  30. }
  31. }

394. 字符串解码

输入:s = “3[a]2[bc]” 输出:“aaabcbc”

  1. class Solution {
  2. public String decodeString(String s) {
  3. StringBuilder res = new StringBuilder();
  4. int multi = 0;
  5. Stack<Integer> stack_multi = new Stack<>();
  6. Stack<String> stack_res = new Stack<>();
  7. for(char c : s.toCharArray()) {
  8. if(c == '[') {
  9. stack_multi.push(multi);
  10. stack_res.push(res.toString());
  11. multi = 0;
  12. res = new StringBuilder();
  13. }
  14. else if(c == ']') {
  15. StringBuilder tmp = new StringBuilder();
  16. int cur_multi = stack_multi.pop();
  17. for(int i = 0; i < cur_multi; i++) tmp.append(res);
  18. res = new StringBuilder(stack_res.pop() + tmp);
  19. }
  20. else if(c >= '0' && c <= '9') multi = multi * 10 + c - '0';//如果k不是个位数而是n位整数的话就要通过不停的乘10来更新值
  21. else res.append(c);
  22. }
  23. return res.toString();
  24. }
  25. }

114. 二叉树展开为链表

字节算法题 - 图2

  • 首先将根节点的左子树变成链表
  • 其次将根节点的右子树变成链表
  • 最后将变成链表的右子树放在变成链表的左子树的最右边 ```java class Solution { public void flatten(TreeNode root) {
    1. if(root == null){
    2. return ;
    3. }
    4. //将根节点的左子树变成链表
    5. flatten(root.left);
    6. //将根节点的右子树变成链表
    7. flatten(root.right);
    8. TreeNode temp = root.right;
    9. //把树的右边换成左边的链表
    10. root.right = root.left;
    11. //记得要将左边置空
    12. root.left = null;
    13. //找到树的最右边的节点
    14. while(root.right != null) root = root.right;
    15. //把右边的链表接到刚才树的最右边的节点
    16. root.right = temp;
    } }

``` [

](https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/)