刷题日记 - 10
难度:中等
链表 哈希表
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 89 90 91 92 93 94 95 96 97 98
| class LRUCache {
HashMap<Integer, BiNode> map; BiNode head, tail; int count; int capacity;
public LRUCache(int capacity) { map = new HashMap<>(); count = 0; this.capacity = capacity; head = new BiNode(-1, -1); tail = new BiNode(-1, -1); head.right = tail; tail.left = head; } public int get(int key) { int res = -1; if (map.containsKey(key)) { BiNode node = map.get(key); res = node.val; moveToFront(node); } return res; } public void put(int key, int value) { BiNode node = null; if (map.containsKey(key)) { node = map.get(key); node.val = value; moveToFront(node); } else { count++; if (count > capacity) { removeLastNode(); count--; } node = new BiNode(key, value); map.put(key, node); node.left = head; node.right = head.right; head.right.left = node; head.right = node; } }
private void moveToFront(BiNode node) { if (node.left != head) { node.left.right = node.right; node.right.left = node.left; node.right = head.right; head.right.left = node; head.right = node; node.left = head; } }
private void removeLastNode() { int key = tail.left.key; tail.left.left.right = tail; tail.left = tail.left.left; map.remove(key); } }
class BiNode { int key; int val; BiNode left; BiNode right;
public BiNode(int key, int val) { this.key = key; this.val = val; } }
|
- 时间复杂度O(1)的get:采用哈希表。
- 实现LRU:每次取出数据都要更新使用情况,用双向链表来模拟,每次使用都将对应节点取出并移动到头部。
选择了合适的数据结构,然后根据LRU逻辑实现get和put的代码即可。
难度:中等
链表 归并排序(递归)
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
| class Solution {
public ListNode sortList(ListNode head) { if (head == null) return null; return merge(head, null); }
private ListNode merge(ListNode head, ListNode tail) { if (head.next == tail) { head.next = null; return head; } ListNode slow = head, fast = head; while (fast != tail) { fast = fast.next; slow = slow.next; if (fast == tail) break; fast = fast.next; } ListNode mid = slow; ListNode node1 = merge(head, mid); ListNode node2 = merge(mid, tail); return mergeSortedList(node1, node2); }
private ListNode mergeSortedList(ListNode head1, ListNode head2) { ListNode dummy = new ListNode(-1); ListNode t = dummy, p = head1, q = head2; while (p != null && q != null) { if (p.val <= q.val) { t.next = p; p = p.next; t = t.next; } else { t.next = q; q = q.next; t = t.next; } } t.next = (p == null) ? q : p; return dummy.next; } }
|
难点:
- 链表的分割和递归处理:快慢指针寻找中点,分为两个部分。(
mid
节点既作为左边链表的尾部又作为右边部分的头部)
- 递归的合并操作:通过递归地将链表分割直至每部分只剩下一个节点,然后逐层合并成有序链表。
难度:简单
二叉树
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Solution { public boolean isSameTree(TreeNode p, TreeNode q) { return dfs(p,q); }
private boolean dfs(TreeNode p,TreeNode q){ if(p==null&&q==null){ return true; }else if((q!=null&&p!=null)&&q.val==p.val){ return true && dfs(p.left,q.left) && dfs(p.right,q.right); } 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 25 26 27 28 29 30 31 32 33
| class Solution { public Node connect(Node root) { if (root == null) return null;
LinkedList<Node> queue = new LinkedList<>(); queue.offerLast(root); queue.offerLast(null);
Node last = null;
while (true) { Node cur = queue.pollFirst(); if (last != null) { last.next = cur; } last = cur;
if (cur != null) { if (cur.left != null) queue.addLast(cur.left); if (cur.right != null) queue.addLast(cur.right); } else { if (queue.isEmpty()) break; queue.addLast(null); } } return root; } }
|
本题要求将二叉树节点的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 26 27 28 29 30 31 32
| class Solution { public void flatten(TreeNode root) { if (root == null) return;
TreeNode p = root; TreeNode q = null;
while (p != null) { if (p.left != null) { q = p.left; while (q.right != null) { q = q.right; } q.right = p.right;
p.right = p.left;
p.left = null; } p = p.right; } } }
|
需要理解二叉树展平成链表的过程:
将左子树接到右孩子,原右孩子接原左子树的最右孩子。用代码实现这一逻辑即可。
难度:简单
二叉树 dfs
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
| class Solution { public boolean hasPathSum(TreeNode root, int targetSum) { if (root == null) return false; return dfs(root, targetSum, 0); }
private boolean dfs(TreeNode node, int targetSum, int sum) { sum += node.val; if (node.left == null && node.right == null && sum == targetSum) return true; if (node.left != null && node.right == null) return dfs(node.left, targetSum, sum); if (node.left == null && node.right != null) return dfs(node.right, targetSum, sum); if (node.left != null && node.right != null) return dfs(node.left, targetSum, sum) || dfs(node.right, targetSum, sum); return false; } }
|
使用dfs计算每一条可能的路径和。
难度:中等
二叉树 dfs
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
| class Solution { List<Integer> array = new ArrayList<>();
public int sumNumbers(TreeNode root) { dfs(root, 0); int res = 0; for (int i = 0; i < array.size(); i++) { res += array.get(i); } return res; }
private void dfs(TreeNode node, int sum) { sum = sum * 10 + node.val;
if (node.left == null && node.right == null) array.add(sum);
if (node.left != null && node.right == null) dfs(node.left, sum);
if (node.left == null && node.right != null) dfs(node.right, sum);
if (node.left != null && node.right != null) { dfs(node.left, sum); dfs(node.right, sum); } } }
|
同上题一样。
难度:困难
二叉树 动态规划 dfs
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
| class Solution {
int res = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) { dfs(root); return res; }
private int dfs(TreeNode node){ if (node == null) return 0;
int l = 0, r = 0;
if (node.left != null) l = dfs(node.left);
if (node.right != null) r = dfs(node.right);
int takeCurNodeRootLPS = l + r + node.val;
res = Math.max(takeCurNodeRootLPS, res);
int oneLargestPath = Math.max(l, r) + node.val;
return oneLargestPath > 0 ? oneLargestPath : 0; } }
|
难点:
理解路径:在本题中,路径可以包含当前节点,当前节点左子树和右子树中的节点。也可以仅包含当前节点。
考虑以当前节点为拐点的最大路径:需要知道左子树所能提供的最大值和右子树所能提供的最大值,把这两条路径和自己的值相加即可,然后更新全局变量res
。
当前节点所能给父节点提供的最大路径:这个值要提供给父节点做参考,就是考虑以父节点为拐点的情况,那么在当前节点的这个栈帧内,就应该选择当前节点的左子树所能提供的最大值和右子树所能提供的最大值之中较大的那一个加上当前节点的值。如果这个最终值为小于0
,那么直接返回0
让父节点不要选择自己。
DFS 动态规划:DFS先触底然后再往上回溯,而上面节点的路径计算恰好需要下面节点的数据,从下之上,相当于递归的这个栈帧为我们传递了动态规划的数据。