1. 二叉树的前序遍历:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/
点击查看代码
// 二叉树的递归遍历
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;
    }
}
  1. 二叉树的中序遍历:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
点击查看代码
// 二叉树的中序非递归遍历
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;
    }
}
  1. 二叉树的后序遍历:https://leetcode-cn.com/problems/binary-tree-postorder-traversal/
点击查看代码
// 二叉树的后序非递归遍历
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;
    }
}
  1. 二叉树的锯齿形层序遍历:https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
点击查看代码
// 二叉树的蛇形遍历(对层序遍历结果进行变换)
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;
    }
}
  1. 从前序与中序遍历序列构造二叉树: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;
    }
}
  1. 从中序与后序遍历序列构造二叉树: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. 完全二叉树的节点个数:https://leetcode-cn.com/problems/count-complete-tree-nodes/
点击查看代码
// 完全二叉树节点个数 = 左子树结点数 + 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;
    }
}
  1. 二叉树的最大深度:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
点击查看代码
// 总结:二叉树递归处理思想 = 当前节点 + 左子树处理 + 右子树处理
public class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
    }
}
  1. 二叉树的最近公共祖先:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
点击查看代码
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;
    }
}
  1. 路径总和:https://leetcode-cn.com/problems/path-sum/
点击查看代码
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);
    }
}
  1. 路径总和 II:https://leetcode-cn.com/problems/path-sum-ii/
点击查看代码
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);
    }
}
  1. 二叉树中的最大路径和:https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/
点击查看代码
// 二叉树中的最大路径和 = 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;
    }
}
  1. 用队列实现栈:https://leetcode-cn.com/problems/implement-stack-using-queues/
点击查看代码
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;
    }
}
  1. 用栈实现队列:https://leetcode-cn.com/problems/implement-queue-using-stacks/
点击查看代码
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();
    }
}
  1. 最大频率栈:https://leetcode-cn.com/problems/maximum-frequency-stack/
点击查看代码
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;
    }
}
  1. LRU 缓存:https://leetcode-cn.com/problems/lru-cache/
点击查看代码
// 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;
    }
}
  1. K 个一组翻转链表:https://leetcode-cn.com/problems/reverse-nodes-in-k-group/
点击查看代码
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};
    }
}
  1. 相交链表:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
点击查看代码
// 相交链表,指针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;
    }
}