面试题30:包含min函数的栈

题目:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数。在该栈中,调用min、push及pop的时间复杂度都是O(1)。

  • 算法:
    • 两个栈:
    • 主栈照常出栈入栈
    • 最小栈每次入栈小于等于栈顶的元素,出栈照常
class MinStack {
    Stack<Integer> main;
    Stack<Integer> min;
    public MinStack() {
        main = new Stack<Integer>();
        min = new Stack<Integer>();
    }
    public void push(int x) {
        main.push(x);
        if (min.isEmpty() || x < min.peek()) {
            min.push(x);
        } else {
            min.push(min.peek());
        }
    }
    public void pop() {
        if (!main.empty()) {
            main.pop();
        }
        if (!min.empty()) {
            min.pop();
        }
    }
    public int top() {
        if (!main.empty()) {
            return main.peek();
        } else {
            return -1;
        }
    }
    public int getMin() {
        if (!min.empty()) {
            return min.peek();
        } else {
            return -1;
        }
    }
}
  • 算法:
    • 只用一个栈
    • 入栈元素小于当前min就入栈两次同时更新min(当前min和x)
    • 出栈元素等于当前min就出栈两次同时更新min
class MinStack {
    int min;
    Stack<Integer> stack;
    public MinStack() {
        min = Integer.MAX_VALUE;
        stack = new Stack<Integer>();
    }
    public void push(int x) {
        // first push the last minimum value 
        // when the current minimum value changes after pushing the new value x
        if(x <= min){
            stack.push(min);
            min=x;
        }
        stack.push(x);
    }
    public void pop() {
        // if pop operation could result in the changing of the current minimum value,
        // pop twice and change the current minimum value to the last minimum value.
        if(stack.pop() == min) 
            min = stack.pop();
    }
    public int top() {
        return stack.peek();
    }
    public int getMin() {
        return min;
    }
}

面试题31:栈的压入、弹出序列

题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1、2、3、4、5是某栈的压栈序列,序列4、5、3、2、1是该压栈序列对应的一个弹出序列,但4、3、5、1、2就不可能是该压栈序列的弹出序列。

  • 算法:push序列不断压栈直到遇到pop序列的出栈元素
// 遍历出栈序列
public boolean validateStackSequences(int[] pushed, int[] popped) {
    if (pushed == null || popped == null) {
        throw new RuntimeException("Null");
    }
    if (pushed.length != popped.length) {
        return false;
    }
    if (pushed.length == 0 && popped.length == 0) {
        return true;
    }

    int pushIndex = 0;
    int popIndex = 0;
    Stack<Integer> stack = new Stack<Integer>();
    while (popIndex < popped.length) {
        while ((stack.empty() || stack.peek() != popped[popIndex]) && pushIndex < pushed.length) {
            stack.push(pushed[pushIndex++]);
        }

        if (stack.peek() == popped[popIndex]) {
            stack.pop();
            popIndex++;
        } else {
            return false;
        }
    }
    return true;
}

// 遍历出栈序列
public boolean validateStackSequences(int[] pushed, int[] popped) {
    if (pushed == null || popped == null) {
        throw new RuntimeException("Null");
    }
    if (pushed.length != popped.length) {
        return false;
    }
    if (pushed.length == 0 && popped.length == 0) {
        return true;
    }

    Stack<Integer> stack = new Stack<Integer>();
    int index = 0;
    for (int p : pushed) {
        stack.push(p);
        while (!stack.isEmpty() && stack.peek() == popped[index]){
            stack.pop();
            index++;
        }
    }
    return stack.isEmpty();
}

面试题32:从上到下打印二叉树

题目一:不分行从上到下打印二叉树

题目:从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印。

public int[] levelOrder(TreeNode root) {
    if (root == null) {
        return new int[0];
    }

    List<Integer> result = new ArrayList<Integer>();
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    while (!queue.isEmpty()) {
        TreeNode temp = queue.poll();
        result.add(temp.val);
        if (temp.left != null) {
            queue.offer(temp.left);
        }
        if (temp.right != null) {
            queue.offer(temp.right);
        }
    }

    int[] array = new int[result.size()];
    int i = 0;
    for (Integer x : result) {
        array[i++] = x;
    }
    return array;
}

题目二:分行从上到下打印二叉树

题目:从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印到一行。

  • 算法:分层收集打印
public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    if(root == null)
        return res;

    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    while (!queue.isEmpty()) {
        int levelNum = queue.size();
        List<Integer> levelList = new ArrayList<Integer>();
        for (int i = 0; i < levelNum; i++) {
            TreeNode temp = queue.peek();
            if (temp.left != null) {
                queue.offer(temp.left);
            }
            if (temp.right != null) {
                queue.offer(temp.right);
            }
            levelList.add(queue.poll().val);
        }
        res.add(levelList);
    }
    return res;
}
  • 算法:分层计数打印
public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    if (root == null) {
        return res;
    }

    int nextLevel = 0;
    int currLevel = 1;
    ArrayList<Integer> level = new ArrayList<Integer>();
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    while (!queue.isEmpty()) {
        TreeNode temp = queue.poll();
        level.add(temp.val);
        currLevel--;
        if (temp.left != null) {
            queue.offer(temp.left);
            nextLevel++;
        }
        if (temp.right != null) {
            queue.offer(temp.right);
            nextLevel++;
        }
        if (currLevel == 0) {
            res.add(level);
            level = new ArrayList<Integer>();
            currLevel = nextLevel;
            nextLevel = 0;
        }
    }
    return res;
}

题目三:之字形打印二叉树

题目:请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

  • 算法:
    • 两个栈:从1开始计算层数
    • 栈1收集奇数层先右节点后左子节点,栈2收集偶数层先左子节点后右子节点
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    if (root == null) {
        return res;
    }

    int curr = 0;
    int next = 1;
    ArrayList<Integer> levelList = new ArrayList<Integer>();
    Stack<TreeNode> stack1 = new Stack<TreeNode>();
    Stack<TreeNode> stack2 = new Stack<TreeNode>();
    stack1.push(root);
    while (!stack1.empty() || !stack2.empty()) {
        if (curr == 0) {
            TreeNode temp = stack1.pop();
            levelList.add(temp.val);
            if (temp.left != null) {
                stack2.push(temp.left);
            }
            if (temp.right != null) {
                stack2.push(temp.right);
            }
            if (stack1.empty()) {
                res.add(levelList);
                levelList = new ArrayList<>();
                curr = 1 - curr;
                next = 1 - next;
            }
        } else {
            TreeNode temp = stack2.pop();
            levelList.add(temp.val);
            if (temp.right != null) {
                stack1.push(temp.right);
            }
            if (temp.left != null) {
                stack1.push(temp.left);
            }
            if (stack2.empty()) {
                res.add(levelList);
                levelList = new ArrayList<>();
                curr = 1 - curr;
                next = 1 - next;
            }
        }
    }
    return res;
}
  • 算法:
    • 双端队列:
    • 奇数层和偶数层按规律添加和删除
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if(root == null) {
            return res;
        }

        Deque<TreeNode> deque = new LinkedList<>();
        deque.add(root);
        while(!deque.isEmpty()) {
            // 奇数层:队尾删除,队首先左后右添加,使得下一个偶数层从右向左打印
            List<Integer> levelList = new ArrayList<>();
            for(int i = deque.size(); i > 0; i--) {
                TreeNode node = deque.removeLast();
                levelList.add(node.val);
                if(node.left != null) deque.addFirst(node.left);
                if(node.right != null) deque.addFirst(node.right);
            }
            res.add(levelList);
            if (deque.isEmpty()) break;
            // 偶数层:队首删除,队尾先右后左添加,使得下一个奇数层从左向右打印
            levelList = new ArrayList<>();
            for(int i = deque.size(); i > 0; i--) {
                TreeNode node = deque.removeFirst();
                levelList.add(node.val);
                if(node.right != null) deque.addLast(node.right);
                if(node.left != null) deque.addLast(node.left);
            }
            res.add(levelList);
        }
        return res;
}

面试题33:二叉搜索树的后序遍历序列

题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。

  • 算法:
    • 利用最后一个数组元素是树的根节点来分割这个二叉搜索树,递归处理左右子树
public boolean verifyPostorder(int[] postorder) {
    if (postorder == null) {
        throw new RuntimeException("Array is null");
    }
    if (postorder.length <= 1) {
        return true;
    }

    return verifyPostorderRecursion(postorder, 0, postorder.length - 1);
}
private boolean verifyPostorderRecursion(int[] postorder, int start, int end) {
    if (start == end) {
        return true;
    }

    int root = postorder[end];
    int i = start;
    for (; i < end; i++) {
        if (postorder[i] > root)  {
            break;
        }
    }
    int j = i;
    for (; j < end; j++) {
        if (postorder[j] < root) {
            return false;
        }
    }
    boolean left = true;
    boolean right = true;
    if (i > start) {
        left = verifyPostorderRecursion(postorder, start, i - 1);
    }
    if (i < end) {
        right = verifyPostorderRecursion(postorder, i, end - 1);
    }
    return left && right;
}

面试题34:二叉树中和为某一值的路径

题目:输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

public List<List<Integer>> pathSum(TreeNode root, int sum) {
    List<List<Integer>> result = new LinkedList<List<Integer>>();
    if (root == null) {
        return result;
    }

    LinkedList<Integer> path = new LinkedList<Integer>();
    findPath(root, result, path, sum, 0);
    return result;
}

private void findPath(TreeNode node, List<List<Integer>> result, LinkedList<Integer> path,
                        int pathSum, int currentSum) {
    currentSum += node.val;
    path.offer(node.val);
    if (node.left == null && node.right == null && currentSum == pathSum) {
        LinkedList<Integer> temp = new LinkedList<Integer>();
        temp.addAll(path);
        result.add(temp);
    }
    if (node.left != null) {
        findPath(node.left, result, path, pathSum, currentSum);
    }
    if (node.right != null) {
        findPath(node.right, result, path, pathSum, currentSum);
    }
    path.pollLast();
}