- 二叉树
- 题型5:LCA最近公共祖先
- 题型6:二叉树转单、双、循环链表
- 题型7:按照遍历结果反向构建二叉树
- 105.从前序与中序遍历序列构造二叉树(中等)">105.从前序与中序遍历序列构造二叉树(中等)
- 889.根据前序和后序遍历构造二叉树(中等)">889.根据前序和后序遍历构造二叉树(中等)
- 106.从中序与后序遍历序列构造二叉树(中等)">106.从中序与后序遍历序列构造二叉树(中等)
- 剑指Offer 33.二叉搜索树的后序遍历序列(中等)">剑指Offer 33.二叉搜索树的后序遍历序列(中等)
- 题型8:二叉树上的最长路径和
- 543.二叉树的直径(简单)">543.二叉树的直径(简单)
- 剑指Offer 34.二叉树中和为某一值的路径(中等)">剑指Offer 34.二叉树中和为某一值的路径(中等)
- 124.二叉树中的最大路径和 (困难)">124.二叉树中的最大路径和 (困难)
- 437. 路径总和III (困难)">437. 路径总和III (困难)
- 堆和Trie
- 堆
- 23.合并K个升序链表(困难) (例题)">23.合并K个升序链表(困难) (例题)
- 347.前K个高频元素(中等) (例题)">347.前K个高频元素(中等) (例题)
- 295.数据流的中位数(困难)(例题)">295.数据流的中位数(困难)(例题)
- 973.最接近原点的K个点(中等)">973.最接近原点的K个点(中等)
- 313.超级丑数(中等)">313.超级丑数(中等)
- Trie
- 208.实现Trie (前缀树)(中等) 标准Trie树">208.实现Trie (前缀树)(中等) 标准Trie树
- 以下为选做题目:
- 面试题17.17.多次搜索(中等) 标准AC自动机,不过写AC自动机太复杂,Trie树搞定">面试题17.17.多次搜索(中等) 标准AC自动机,不过写AC自动机太复杂,Trie树搞定
- 212.单词搜索II(困难)">212.单词搜索II(困难)
- 堆
二叉树
题型5:LCA最近公共祖先
236.二叉树的最近公共祖先(中等)
解题思路:
核心点在于:
- 如果当前节点 node 的左右子树各含有 p 或 q 节点,那么 node节点则为 p 和 q 节点的最近公共祖先
如果当前节点 node 等于 p 或 q 节点,那么当前节点 node 为 p 和 q 节点的最近公共祖先
递归解法一
class Solution {private TreeNode lca;public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {dfs(root, p, q);return lca;}private int dfs(TreeNode root, TreeNode p, TreeNode q) {if(root == null) {return 0;}int leftContains = dfs(root.left, p, q);if(lca != null) {return 2;}int rightContains = dfs(root.right, p, q);// 剪枝if(lca != null) {return 2;}int rootContains = 0;if(root == p || root == q) {rootContains = 1;}if(rootContains == 0 && leftContains == 1 && rightContains == 1) {lca = root;}if(rootContains == 1 && (leftContains == 1 || rightContains == 1)) {lca = root;}return rootContains + leftContains + rightContains;}}
递归解法二
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root == null) {return null;}if(root == p || root == q) {return root;}TreeNode lNode = lowestCommonAncestor(root.left, p, q);TreeNode rNode = lowestCommonAncestor(root.right, p, q);if(lNode != null && rNode != null) {return root;} else if(lNode == null) {return rNode;} else {return lNode;}}}
剑指Offer 68 - I.二叉搜索树的最近公共祖先(中等)
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {while(root != null) {if(p.val < root.val && root.val < q.val) {return root;} else if(p.val < root.val && q.val < root.val) {root = root.left;} else if(p.val > root.val && q.val > root.val) {root = root.right;} else {return root;}}return null;}}
题型6:二叉树转单、双、循环链表
114.二叉树展开为链表(中等)
哨兵节点 + 前序遍历
class Solution {// 伪头结点private TreeNode dumyHead = new TreeNode();// 核心点:tail 要设置为 全局变量private TreeNode tail = dumyHead;public void flatten(TreeNode root) {if(root == null) {return;}// 先序遍历TreeNode left = root.left;TreeNode right = root.right;// 往伪头结点插入tail.right = root;tail = root;root.left = null;flatten(left);flatten(right);}}
后序遍历
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/class Solution {public void flatten(TreeNode root) {if(root == null) {return;}// 后序遍历flatten(root.left);flatten(root.right);TreeNode left = root.left;TreeNode right = root.right;// 将左子树作为右子树root.right = left;root.left = null;// 将原右子树接至当前右子树的最右端TreeNode p = root;while(p.right != null) {p = p.right;}p.right = right;}}
面试题17.12. BiNode(中等)
class Solution {private TreeNode dumyHead = new TreeNode();private TreeNode tail = dumyHead;public TreeNode convertBiNode(TreeNode root) {inorder(root);return dumyHead.right;}private void inorder(TreeNode root) {if(root == null) {return;}inorder(root.left);TreeNode right = root.right;tail.right = root;tail = root;tail.left = null;inorder(right);}}
剑指Offer 36.二叉搜索树与双向链表(中等)
class Solution {private Node head = new Node();private Node tail = head;public Node treeToDoublyList(Node root) {if(root == null) {return null;}// 中序遍历inorder(root);tail.right = head.right;head.right.left = tail;return head.right;}private void inorder(Node node) {if(node == null) {return;}inorder(node.left);tail.right = node;node.left = tail;tail = node;inorder(node.right);}}
面试题04.03.特定深度节点链表(中等)
队列-层次遍历
class Solution {public ListNode[] listOfDepth(TreeNode tree) {if(tree == null) {return new ListNode[0];}// 层次遍历Queue<TreeNode> queue = new LinkedList<>();List<ListNode> res = new ArrayList<>();queue.offer(tree);while(!queue.isEmpty()) {int size = queue.size();ListNode dumyHeadNode = new ListNode(), tmp = dumyHeadNode;for(int i = 0; i < size; i++) {TreeNode node = queue.poll();tmp.next = new ListNode(node.val);tmp = tmp.next;if(node.left != null) {queue.offer(node.left);}if(node.right != null) {queue.offer(node.right);}}res.add(dumyHeadNode.next);}return res.toArray(new ListNode[0]);}}
递归-深度优先遍历
class Solution {public ListNode[] listOfDepth(TreeNode tree) {List<ListNode> res = new ArrayList<>();dfs(tree, res, 0);return res.toArray(new ListNode[0]);}private void dfs(TreeNode root, List<ListNode> res, int level) {if(root == null) {return;}ListNode newNode = new ListNode(root.val);if(res.size() <= level) {res.add(newNode);} else {// 头插法newNode.next = res.get(level);res.set(level, newNode);}// 往右子树向下遍历dfs(root.right, res, level + 1);dfs(root.left, res, level + 1);}}
题型7:按照遍历结果反向构建二叉树
105.从前序与中序遍历序列构造二叉树(中等)
递归
解题思路:
- 在前序遍历数组中,能明确根节点的值。
结合步骤一,在中序遍历数组中,能明确左右子树的节点范围。
class Solution {public TreeNode buildTree(int[] preorder, int[] inorder) {// 递归return myBuildTree(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);}private TreeNode myBuildTree(int[] preorder, int i, int j, int[] inorder, int p, int r) {if(i > j) {return null;}int val = preorder[i];TreeNode root = new TreeNode(val);// 找出根节点在中序遍历数组中的下标int rIdx = p;while(rIdx <= r && inorder[rIdx] != val) {rIdx++;}// 计算根节点的左子树的节点大小int leftTreeSize = rIdx - p;TreeNode left = myBuildTree(preorder, i + 1, i + leftTreeSize, inorder, p, rIdx - 1);TreeNode right = myBuildTree(preorder, i + leftTreeSize + 1, j, inorder, rIdx + 1, r);root.left = left;root.right = right;return root;}}
889.根据前序和后序遍历构造二叉树(中等)
class Solution {public TreeNode constructFromPrePost(int[] pre, int[] post) {return buildTree(pre, 0, pre.length - 1, post, 0, post.length - 1);}private TreeNode buildTree(int[] pre, int preStart, int preEnd, int[] post, int postStart, int postEnd) {if(preStart > preEnd) {return null;}TreeNode root = new TreeNode(pre[preStart]);if(preStart == preEnd) {return root;}int idx = postStart;while(post[idx] != pre[preStart + 1]) {idx++;}int leftTreeSize = idx - postStart;TreeNode left = buildTree(pre, preStart + 1, preStart + 1 + leftTreeSize, post, postStart, idx);TreeNode right = buildTree(pre, preStart + 1 + leftTreeSize + 1, preEnd, post, idx + 1, postEnd -1);root.left = left;root.right = right;return root;}}
106.从中序与后序遍历序列构造二叉树(中等)
class Solution {public TreeNode buildTree(int[] inorder, int[] postorder) {return myBuildTree(inorder, 0, inorder.length - 1, postorder, 0, postorder.length - 1);}private TreeNode myBuildTree(int[] inorder, int inStart, int inEnd, int[] postorder, int postStart, int postEnd) {if(inStart > inEnd) {return null;}int rootVal = postorder[postEnd];TreeNode root = new TreeNode(rootVal);if(postStart == postEnd) {return root;}int index = inStart;while(inorder[index] != rootVal) {index++;}int leftTreeSize = index - inStart;TreeNode left = myBuildTree(inorder, inStart, index - 1, postorder, postStart, postStart + leftTreeSize - 1);TreeNode right = myBuildTree(inorder, index + 1, inEnd, postorder, postStart + leftTreeSize, postEnd - 1);root.left = left;root.right = right;return root;}}
剑指Offer 33.二叉搜索树的后序遍历序列(中等)
解题思路:
二叉树前中后序遍历的组成结构:
前序:[ 根节点 | 左子树节点 | 右子树节点 ]
中序:[ 左子树节点 | 根节点 | 右子树节点 ]
后序:[ 左子树节点 | 右子树节点 | 根节点 ]
BST 特性:任意一个节点的值都比其左子树节点的值大,比其右子树节点的值小。
class Solution {public boolean verifyPostorder(int[] postorder) {return cur(postorder, 0, postorder.length - 1);}private boolean cur(int[] postorder, int postStart, int postEnd) {if(postStart >= postEnd) {return true;}// 根节点值int rootVal = postorder[postEnd];int idx = postStart;// 遍历左子树的值,找出第一个比根节点大的值while(idx < postEnd && postorder[idx] < rootVal) {idx++;}// 判断右子树范围内的值,此范围内的值需要比根节点的值大for(int i = idx; i < postEnd; i++) {if(postorder[i] < rootVal) {return false;}}return cur(postorder, postStart, idx - 1) && cur(postorder, idx, postEnd - 1);}}
题型8:二叉树上的最长路径和
543.二叉树的直径(简单)
递归
解题思路:转换成求树深即可。即比较每个节点的左右子树深度之和,求最大值。
class Solution {private int ans = 0;public int diameterOfBinaryTree(TreeNode root) {if(root == null) {return 0;}maxDepth(root);return ans;}private int maxDepth(TreeNode root) {if(root == null) {return 0;}int left = maxDepth(root.left);int right = maxDepth(root.right);int dia = left + right;ans = Math.max(ans, dia);return Math.max(left, right) + 1;}}
剑指Offer 34.二叉树中和为某一值的路径(中等)
class Solution {public List<List<Integer>> pathSum(TreeNode root, int target) {List<List<Integer>> res = new ArrayList<>();dfs(root, res, new ArrayList<Integer>(), target, 0);return res;}private void dfs(TreeNode root, List<List<Integer>> res, List<Integer> path, int target, int pathSum) {if(root == null) {return;}pathSum += root.val;path.add(root.val);// 从树的根节点开始往下一直到叶节点所经过的节点形成一条路径,即必须是到叶子节点if(root.left == null && root.right == null && target == pathSum) {List<Integer> tmp = new ArrayList<>(path);res.add(tmp);path.remove(path.size() - 1);return;}dfs(root.left, res, path, target, pathSum);dfs(root.right, res, path, target, pathSum);path.remove(path.size() - 1);}}
124.二叉树中的最大路径和 (困难)
437. 路径总和III (困难)
堆和Trie
堆
23.合并K个升序链表(困难) (例题)
解题思路:利用优先级队列来构造小顶堆,然后利用小顶堆的特性来实现升序链表
class Solution {public ListNode mergeKLists(ListNode[] lists) {ListNode dumyHead = new ListNode(), tNode = dumyHead;// 利用优先级队列构造小顶堆PriorityQueue<ListNode> queue = new PriorityQueue<ListNode>((n1, n2) -> n1.val - n2.val);// 初始化小顶堆for(ListNode node : lists) {if(node != null) {queue.offer(node);}}while(!queue.isEmpty()) {ListNode node = queue.poll();tNode.next = node;tNode = node;if(node.next != null) {queue.offer(node.next);}}return dumyHead.next;}}
347.前K个高频元素(中等) (例题)
优先级队列 - 大顶堆
解题思路:
- 自定类记录数组中不重复元素的值以及其出现的次数。
- 使用 HashMap 计算数组中不重复元素的出现次数。
利用优先级队列构造大顶堆,通过大顶堆的特性,获取前 k 个高频元素。
class Solution {class Node {private int val;private int count;public Node(int val, int count) {this.val = val;this.count = count;}}public int[] topKFrequent(int[] nums, int k) {// 利用优先级构造大顶堆PriorityQueue<Node> queue = new PriorityQueue<>((n1, n2) -> n2.count - n1.count);// 计算频率HashMap<Integer, Integer> map = new HashMap<>();for(int num : nums) {if(map.containsKey(num)) {map.put(num, map.get(num) + 1);} else {map.put(num, 1);}}// 初始化大顶堆for(Map.Entry<Integer, Integer> entry : map.entrySet()) {queue.offer(new Node(entry.getKey(), entry.getValue()));}int[] res = new int[k];for(int i = 0; i < k; i++) {res[i] = queue.poll().val;}return res;}}
295.数据流的中位数(困难)(例题)
class MedianFinder {// 最小堆private PriorityQueue<Integer> minHeap;// 最大堆private PriorityQueue<Integer> maxHeap;/** initialize your data structure here. */public MedianFinder() {this.minHeap = new PriorityQueue<>((o1, o2) -> o1 - o2);this.maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);}public void addNum(int num) {if(maxHeap.isEmpty() || num <= maxHeap.peek()) {maxHeap.add(num);} else {minHeap.add(num);}// 维持最大堆内元素个数永远只比最小堆内元素个数最多多一个元素。while(maxHeap.size() < minHeap.size()) {maxHeap.add(minHeap.poll());}while(minHeap.size() < maxHeap.size() - 1) {minHeap.add(maxHeap.poll());}}public double findMedian() {if(maxHeap.isEmpty()) {return 0;}if(maxHeap.size() > minHeap.size()) {return maxHeap.peek();} else {return (maxHeap.peek() + minHeap.peek()) / 2f;}}}
973.最接近原点的K个点(中等)
优先级队列-小顶堆
解题思路:
- 自定义一个类,存储数组 point 和到原点的距离 sqrt
- 使用优先级队列,构建小顶堆 minHeap,将数组 points 值全部加入到 minHeap 中
利用小顶堆的特性,poll 出 k 个元素,即为最接近原点的 k 个点
class Solution {class Closest {private int[] point;private int sqrt;public Closest(int[] point, int sqrt) {this.point = point;this.sqrt = sqrt;}}public int[][] kClosest(int[][] points, int k) {// 小顶堆PriorityQueue<Closest> queue = new PriorityQueue<>((c1, c2) -> c1.sqrt - c2.sqrt);// 初始化小顶堆for(int i = 0; i < points.length; i++) {int[] point = points[i];int x = point[0];int y = point[1];int sqrt = x * x + y * y;queue.offer(new Closest(point, sqrt));}int[][] res = new int[k][2];for(int i = 0; i < k; i++) {Closest c = queue.poll();res[i] = c.point;}return res;}}
优先级队列-大顶堆
解题思路:
- 自定义一个类,存储数组 point 和到原点的距离 sqrt 。
- 使用优先级队列,构建大顶堆 maxHeap,先将数组 points 中 k 个元素加入到 maxHeap 中。
然后遍历数组 points 中剩余的元素,比 maxHeap 堆顶元素小的,加入 maxHeap,并 poll 出堆顶元素,遍历完毕后,堆内元素即为最接近原点的 k 个点。
class Solution {class Closest {private int[] point;private int sqrt;public Closest(int[] point, int sqrt) {this.point = point;this.sqrt = sqrt;}}public int[][] kClosest(int[][] points, int k) {// 构建大顶堆PriorityQueue<Closest> maxHeap = new PriorityQueue<>((c1, c2) -> c2.sqrt - c1.sqrt);// 初始化大顶堆for(int i = 0; i < k; i++) {int[] point = points[i];int sqrt = sqrt(point);maxHeap.offer(new Closest(point, sqrt));}for(int i = k; i < points.length; i++) {int[] point = points[i];int sqrt = sqrt(point);if(sqrt < maxHeap.peek().sqrt) {maxHeap.offer(new Closest(point, sqrt));maxHeap.poll();}}int[][] res = new int[k][2];for(int i = 0; i < k; i++) {Closest c = maxHeap.poll();res[i] = c.point;}return res;}private int sqrt(int[] point) {int x = point[0];int y = point[1];return x * x + y * y;}}
313.超级丑数(中等)
优先级队-最小堆
解题思路:
class Solution {public int nthSuperUglyNumber(int n, int[] primes) {PriorityQueue<Long> minHeap = new PriorityQueue<>();// 初始化最小堆long res = 1;for(int i = 1; i < n; i++) {for(int prime : primes) {minHeap.add(prime * res);}res = minHeap.poll();while(!minHeap.isEmpty() && res == minHeap.peek()) {minHeap.poll();}}return (int) res;}}
Trie
208.实现Trie (前缀树)(中等) 标准Trie树
class Trie {class TreeNode {private TreeNode[] children;private boolean isEnd;public TreeNode() {this.children = new TreeNode[26];this.isEnd = false;}}private TreeNode root;/** Initialize your data structure here. */public Trie() {root = new TreeNode();}/** Inserts a word into the trie. */public void insert(String word) {char[] array = word.toCharArray();TreeNode p = root;for(char c : array) {int index = hash(c);if(p.children[index] == null) {p.children[index] = new TreeNode();}p = p.children[index];}p.isEnd = true;}/** Returns if the word is in the trie. */public boolean search(String word) {TreeNode p = searchPrefix(word);return p != null && p.isEnd;}/** Returns if there is any word in the trie that starts with the given prefix. */public boolean startsWith(String prefix) {return searchPrefix(prefix) != null;}private TreeNode searchPrefix(String word) {char[] array = word.toCharArray();TreeNode p = root;for(char c : array) {int index = hash(c);if(p.children[index] == null) {return null;}p = p.children[index];}return p;}private int hash(char c) {return c - 'a';}}/*** Your Trie object will be instantiated and called as such:* Trie obj = new Trie();* obj.insert(word);* boolean param_2 = obj.search(word);* boolean param_3 = obj.startsWith(prefix);*/
