点击查看代码
// 二叉树的递归遍历
public class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<>();
preorder(root, res);
return res;
}
public void preorder(TreeNode root, ArrayList<Integer> res) {
if (root == null) {
return;
}
res.add(root.val);
preorder(root.left, res);
preorder(root.right, res);
}
}
// 二叉树的非递归遍历
public class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
// 借助栈实现非递归遍历
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
res.add(node.val);
// 栈先进后出,所以先压右子树,再压左子树
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return res;
}
}
点击查看代码
// 二叉树的中序非递归遍历
public class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode p = root;
while (p != null || !stack.isEmpty()) {
while (p != null) {
stack.push(p);
p = p.left;
}
p = stack.pop();
res.add(p.val);
p = p.right;
}
return res;
}
}
点击查看代码
// 二叉树的后序非递归遍历
public class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
// 压栈顺序是从上到下,从左到右
Stack<TreeNode> inStack = new Stack<>();
// 出栈顺序则是从下到上,从右到左。出栈顺序就是后序遍历的结果
Stack<Integer> outStack = new Stack<>();
inStack.push(root);
while (!inStack.isEmpty()) {
TreeNode node = inStack.pop();
outStack.push(node.val);
if (node.left != null) {
inStack.push(node.left);
}
if (node.right != null) {
inStack.push(node.right);
}
}
while (!outStack.isEmpty()) {
res.add(outStack.pop());
}
return res;
}
}
点击查看代码
// 二叉树的蛇形遍历(对层序遍历结果进行变换)
public class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
// 当前层的节点个数
int size = queue.size();
ArrayList<Integer> tmp = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
tmp.add(node.val);
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
if (res.size() % 2 == 1) {
// 奇数层取值顺序需要反转
Collections.reverse(tmp);
}
res.add(tmp);
}
return res;
}
}
- 从前序与中序遍历序列构造二叉树:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
点击查看代码
// 基于已有的先序和中序遍历结果重建二叉树
public class Solution {
// 先序中的节点作为root,必然将中序中的节点划分为两部分,分别归属于左右子树
HashMap<Integer, Integer> map = new HashMap<>();
int[] preorder;
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
int len = inorder.length;
for (int i = 0; i < len; i++) {
map.put(inorder[i], i);
}
return build(0, len - 1, 0, len - 1);
}
public TreeNode build(int preL, int preR, int inL, int inR) {
if (preL > preR || inL > inR) {
return null;
}
int pivot = preorder[preL];
TreeNode root = new TreeNode(pivot);
// 找到pivot在中序序列中的下标
// 先序序列中,pivot后面紧跟的是左子树节点序列 -> 右边子树节点序列
int pivotIdx = map.get(pivot);
// 构建root的左子树,左子树中的元素个数为pivotIdx - inL
root.left = build(preL + 1, preL + (pivotIdx - inL), inL, pivotIdx - 1);
// 构建root的右子树,注意先序和中序数组大小必然相同
root.right = build(preL + (pivotIdx - inL) + 1, preR, pivotIdx + 1, inR);
return root;
}
}
- 从中序与后序遍历序列构造二叉树:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
点击查看代码
// 从中序和后续遍历序列构造二叉树
public class Solution {
HashMap<Integer, Integer> map = new HashMap<>();
int[] postorder;
public TreeNode buildTree(int[] inorder, int[] postorder) {
int len = inorder.length;
this.postorder = postorder;
for (int i = 0; i < len; i++) {
map.put(inorder[i], i);
}
return build(0, len - 1, 0, len - 1);
}
public TreeNode build(int postL, int postR, int inL, int inR) {
if (postL > postR || inL > inR) {
return null;
}
// 后续遍历的最后一个节点作为root,将中序遍历结果分为左右子树的两部分
// 后续序列中pivot前面紧跟的是右子树节点序列,左子树节点序列
int pivot = postorder[postR];
TreeNode root = new TreeNode(pivot);
int pivotIndex = map.get(pivot);
// 右子树节点个数为inR - pivotIndex
root.left = build(postL, postR - (inR - pivotIndex) - 1, inL, pivotIndex - 1);
root.right = build(postR - (inR - pivotIndex), postR - 1, pivotIndex + 1, inR);
return root;
}
}
剑指 Offer 54. 二叉搜索树的第k大节点:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof/
点击查看代码
// 中序非递归遍历,同时调整顺序:先右子树,再左子树,可实现降序排序
public class Solution {
public int kthLargest(TreeNode root, int k) {
Stack<TreeNode> stack = new Stack<>();
int cnt = 0;
TreeNode p = root;
while (p != null || !stack.isEmpty()) {
while (p != null) {
stack.push(p);
p = p.right;
}
p = stack.pop();
cnt++;
if (cnt == k) {
return p.val;
}
p = p.left;
}
return 0;
}
}
点击查看代码
// 完全二叉树节点个数 = 左子树结点数 + 1(root节点) + 右子树结点数,核心是“完全二叉树”特征 + 递归.
public class Solution {
public int countNodes(TreeNode root) {
if (root == null) {
return 0;
}
int left = countLevel(root.left);
int right = countLevel(root.right);
if (left == right) {
// 左子树必为满二叉树
return countNodes(root.right) + (1 << left);
} else {
// 最后一层未满,右子树必为满二叉树
return countNodes(root.left) + (1 << right);
}
}
public int countLevel(TreeNode root) {
int res = 0;
TreeNode p = root;
while (p != null) {
res++;
// 完全二叉树的层数由左子树的高度决定
p = p.left;
}
return res;
}
}
点击查看代码
// 总结:二叉树递归处理思想 = 当前节点 + 左子树处理 + 右子树处理
public class Solution {
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
}
点击查看代码
public class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}
if (root == p || root == q) {
return root;
}
// 判断p、q是否存在于左子树或右子树
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 不存在于左子树,则直接返回右子树的情况
if (left == null) {
return right;
}
// 不存在于右子树,则直接返回左子树的情况
if (right == null) {
return left;
}
// p、q分别位于左子树和右子树,则root必为其最近公共祖先
if (left != null && right != null) {
return root;
}
return null;
}
}
点击查看代码
public class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null) {
return false;
}
// 已经抵达叶子节点
if (root.left == null && root.right == null) {
return root.val == targetSum;
}
return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
}
}
点击查看代码
public class Solution {
private List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
searchPath(root, new ArrayList<>(), 0, targetSum);
return res;
}
public void searchPath(TreeNode root, List<Integer> curPath, int curSum, int targetSum) {
if (root == null) {
return;
}
// 添加root到路径信息中
curPath.add(root.val);
curSum += root.val;
if (root.left == null && root.right == null) {
// 抵达叶子节点,判断路径值是否为目标和
if (curSum == targetSum) {
// 通过深拷贝方式避免curPath的修改影响res中的结果
res.add(new ArrayList<>(curPath));
}
}
// 搜索左子树中的路径
searchPath(root.left, curPath, curSum, targetSum);
// 搜索右子树树中的路径
searchPath(root.right, curPath, curSum, targetSum);
// 从路径信息中移除root
curPath.remove(curPath.size() - 1);
}
}
点击查看代码
// 二叉树中的最大路径和 = root记录的单边最大和 || 左子树+root+右子树
// 每个节点记录单边最大路径为: root || 左子树+root || 右子树+root
public class Solution {
private int ans = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
searchMaxPathSum(root);
return ans;
}
public int searchMaxPathSum(TreeNode root) {
if (root == null) {
return 0;
}
// 获取左子树记录的单边最大路径
int lMaxSum = searchMaxPathSum(root.left);
// 获取右子树记录的单边最大路径
int rMaxSum = searchMaxPathSum(root.right);
// 获取以root为根的二叉树的单边最大路径
int curMaxSum = Math.max(root.val, Math.max(root.val + rMaxSum, root.val + lMaxSum));
// 更新总的最大和
ans = Math.max(ans, Math.max(curMaxSum, lMaxSum + root.val + rMaxSum));
// 返回单边最大路径
return curMaxSum;
}
}
点击查看代码
class MyStack {
// LinkList用做队列,成员方法为add/poll/peek
// queue承担stack角色,helper用作辅助,将元素倒腾次数减少为1 =》 需要进行指针交换
private LinkedList<Integer> queue;
private LinkedList<Integer> helper;
public MyStack() {
queue = new LinkedList<>();
helper = new LinkedList<>();
}
public void push(int x) {
queue.add(x);
}
public int pop() {
// 题目已做保证,pop和top时栈不为空
while (queue.size() > 1) {
helper.add(queue.poll());
}
int res = queue.poll();
// 指针交换
swap();
return res;
}
public int top() {
int res = pop();
queue.add(res);
return res;
}
public boolean empty() {
return queue.isEmpty();
}
private void swap() {
LinkedList<Integer> tmp = queue;
queue = helper;
helper = tmp;
}
}
点击查看代码
class MyQueue {
// 栈1负责push,栈2负责pop/peek,pop或peek时如果栈2为空则将栈1元素pop到栈2
// 栈的成员方法:push/pop/peek
private Stack<Integer> pushStack;
private Stack<Integer> popStack;
public MyQueue() {
pushStack = new Stack<>();
popStack = new Stack<>();
}
public void push(int x) {
pushStack.push(x);
}
public int pop() {
// 题目已经保证,不会在没有元素的情况下pop/peek
if (popStack.isEmpty()) {
while (!pushStack.isEmpty()) {
popStack.push(pushStack.pop());
}
}
return popStack.pop();
}
public int peek() {
if (popStack.isEmpty()) {
while (!pushStack.isEmpty()) {
popStack.push(pushStack.pop());
}
}
return popStack.peek();
}
public boolean empty() {
return pushStack.isEmpty() && popStack.isEmpty();
}
}
点击查看代码
class FreqStack {
// 该map用于统计元素出现的频次
private HashMap<Integer, Integer> kfMap;
// 该map用于映射:频次 -- 该频次下的元素列表
// LinkList充当stack作用,push、pop含义相同
private HashMap<Integer, LinkedList<Integer>> fvsMap;
// 用于统计当前已出现的最大频次
int maxFreq;
public FreqStack() {
kfMap = new HashMap<>();
fvsMap = new HashMap<>();
maxFreq = 0;
}
public void push(int val) {
// 元素val当前已出现的频次
int freq = kfMap.getOrDefault(val, 0) + 1;
// 更新kfMap和fvsMap,maxFreq
kfMap.put(val, freq);
fvsMap.putIfAbsent(freq, new LinkedList<>());
fvsMap.get(freq).push(val);
// maxFreq取值必然是连续的
maxFreq = Math.max(maxFreq, freq);
}
public int pop() {
// 弹出频次最高中的最新添加的元素, 当前记录的最高频次为maxFreq
int res = fvsMap.get(maxFreq).pop();
// 更新kfMap和fvsMap,maxFreq
if (fvsMap.get(maxFreq).isEmpty()) {
fvsMap.remove(maxFreq);
maxFreq--;
}
kfMap.put(res, kfMap.get(res) - 1);
return res;
}
}
点击查看代码
// LRU缓存:基于LinkedHashMap实现,需要重写removeEldestEntry(默认返回false,不生效)
public class LRUCache {
Cache<Integer, Integer> cache = null;
public LRUCache(int capacity) {
cache = new Cache<>(capacity);
}
public int get(int key) {
return cache.getOrDefault(key, -1);
}
public void put(int key, int val) {
cache.put(key, val);
}
// 实现一个内部类继承LinkedHashMap, 设置双向链表有序性按照访问顺序维护
static class Cache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
public Cache(int capacity) {
super(capacity, 0.75f, true);
this.capacity = capacity;
}
protected boolean removeEldestEntry(Map.Entry entry) {
return size() > capacity;
}
}
}
面试题 02.05. 链表求和:https://leetcode-cn.com/problems/sum-lists-lcci/
点击查看代码
public class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = null;
ListNode preNode = null;
// 用于保存进位
int tmp = 0;
// 用于保存两数相加和的个位
int val = 0;
while (l1 != null || l2 != null) {
int l1val = l1 == null ? 0 : l1.val;
int l2val = l2 == null ? 0 : l2.val;
val = (l1val + l2val + tmp) % 10;
tmp = (l1val + l2val + tmp) / 10;
preNode = createNode(preNode, val);
head = head == null? preNode : head;
l1 = l1 == null? l1 : l1.next;
l2 = l2 == null? l2 : l2.next;
}
if (tmp > 0) {
preNode = createNode(preNode, tmp);
preNode.next = null;
}
return head;
}
public ListNode createNode(ListNode preNode, int val) {
ListNode curNode = new ListNode(val);
if (preNode == null) {
return curNode;
}
preNode.next = curNode;
return curNode;
}
}
点击查看代码
public class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
// 先反转前k个元素。题目确保链表中至少存在一个节点
// nodes[0]是当前k个节点反转后的链表头节点,nodes[1]是当前k个节点反转后的最后一个节点,
// nodes[2]是剩下的待返转的链表节点的头节点,
ListNode[] nodes1 = reverseK(head, k);
head = nodes1[0];
while (nodes1[2] != null) {
ListNode[] nodes2 = reverseK(nodes1[2], k);
// 处理两个以反转的链表
nodes1[1].next = nodes2[0];
nodes1 = nodes2;
}
return head;
}
private ListNode[] reverseK(ListNode node, int k) {
ListNode preNode = null;
ListNode curNode = node;
ListNode nextNode;
// 先判断node节点开始是否有k个节点,不足则保持原有顺序
int cnt = 0;
nextNode = node;
while (nextNode != null) {
cnt++;
nextNode = nextNode.next;
}
if (cnt < k) {
return new ListNode[]{node, null, null};
}
for (int i = 0; i < k; i++) {
nextNode = curNode.next;
curNode.next = preNode;
preNode = curNode;
curNode = nextNode;
}
// 返回preNode、node、CurNode
return new ListNode[]{preNode, node, curNode};
}
}
点击查看代码
// 相交链表,指针A先遍历链表A再遍历链表B,指针B先遍历链表B再遍历链表A
// 如果相交,相遇的第一个点必然时相交的起始节点,否则均为null.
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode pA = headA;
ListNode pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
艾子的世界艾子主宰
分类:
Leetcode专题
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具