剑指Offer07、09|8月更文挑战

82 阅读2分钟

剑指 Offer 07. 重建二叉树

题目

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

示例 1:

Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]

示例 2:

Input: preorder = [-1], inorder = [-1]
Output: [-1]

限制:

0 <= 节点个数 <= 5000

方法一

递归:根据先序遍历和中序遍历的对应关系:首先创建一个哈希表,用来记录先序遍历中的每一个元素在中序遍历中的下标;接着根据先序遍历中的第一个元素找到其在中序遍历中的位置,中序遍历的第一个元素到该位置的前一个元素即为左子树,该位置的后一个元素到中序遍历的最后一个元素即为右子树。同理,根据中序遍历中的左右子树连续的元素个数,可以推断出先序遍历中哪些元素属于左子树的先序遍历,和中序遍历;然后递归创建即可。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int[] preorder;
    HashMap<Integer, Integer> hash = new HashMap<Integer,Integer>();
    public TreeNode buildTree(int[] _preorder, int[] inorder) {
        preorder = _preorder;
        int n = inorder.length;
        for (int i = 0; i < n; i ++ ) {
            hash.put(inorder[i], i);
        }
        return dfs(0, n - 1, 0, n - 1);
    }
​
    public TreeNode dfs(int pl, int pr, int il, int ir) {
        if (pl > pr) return null;
        int index = hash.get(preorder[pl]);
        TreeNode left = dfs(pl + 1, pl + index - il, il, index - 1);
        TreeNode right = dfs(pl + index - il + 1, pr, index + 1, ir);
        TreeNode root = new TreeNode(preorder[pl]);
        root.left = left;
        root.right = right;
        return root;
    }
}

时间复杂度: O(n)

空间复杂度: O(n)

剑指 Offer 09. 用两个栈实现队列

题目

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTaildeleteHead,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

示例 1:

输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]

示例 2:

输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]

提示:

  • 1 <= values <= 10000
  • 最多会对 appendTail、deleteHead 进行 10000 次调用

方法一

模拟:一个栈用来入队,另一个栈用来辅助出队。入队时往stk1添加,出队时,判断stk2中是否有元素,有则直接将栈顶元素弹出。没有则将stk1 中的元素全部弹到stk2中之后,stk2再弹出栈顶元素。若stk1中也没有,则说明队列为空,返回-1。

class CQueue {
​
    LinkedList<Integer> stk1;
    LinkedList<Integer> stk2;
    public CQueue() {
        stk1 = new LinkedList<>();
        stk2 = new LinkedList<>();
    }
    
    public void appendTail(int value) {
        stk1.addLast(value);
    }
    
    public int deleteHead() {
        if (stk2.isEmpty()) {
            if (stk1.isEmpty()) return -1;
            else {
                while (!stk1.isEmpty()) {
                    stk2.addLast(stk1.removeLast());
                }
            }
        }
        return stk2.removeLast();
    }
}
​
/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */

时间复杂度: O(1)

空间复杂度: O(n)

注意: 使用Stack的方式来做这道题,会造成速度较慢; 原因的话是Stack继承了Vector接口,而Vector底层是一个Object数组,那么就要考虑空间扩容和移位的问题了。 可以使用LinkedList来做Stack的容器,因为LinkedList实现了Deque接口,所以Stack能做的事LinkedList都能做,其本身结构是个双向链表,扩容消耗少。