《剑指Offer》笔记4.3

举例让抽象问题具体化

  1. 包含 min 函数的栈
  2. 栈的压入、弹出序列
  3. 从上到下打印二叉树
  4. 二叉搜索树的后序遍历序列
  5. 二叉树中和为某一值的路径

30:包含 min 函数的栈

题目:
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数。在该栈中,调用min、push 及 pop 的时间复杂度都是 O(1)。
思路:
• 数据栈 A : 栈 A 用于存储所有元素,保证入栈 push() 函数、出栈 pop() 函数、获取栈顶 top() 函数的正常逻辑。
• 辅助栈 B : 栈 B 中存储栈 A 中所有 非严格降序 的元素,则栈 A 中的最小元素始终对应栈 B 的栈顶元素,即 min() 函数只需返回栈 B 的栈顶元素即可。

import java.util.Stack;
public class Solution {
    private Stack<Integer> stack = new Stack<>();
    private Stack<Integer> auxStack = new Stack<>();
    public void push(int node) {
        stack.push(node);
        if(auxStack.isEmpty() || min() >= node){
            auxStack.push(node);
        }
    }
    
    public void pop() {
        if(stack.pop().equals(min()))
            auxStack.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return auxStack.peek();
    }
}

31:栈的压入、弹出序列

题目:
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列
思路:
模拟,如果下一个弹出的数字刚好是栈顶数字,那么直接弹出;如果下一个弹出的数字不在栈顶,则把压栈序列中还没入栈的数字压力栈中,知道把下一个需要弹出的数字压入栈顶为止;如果所有数字都压入栈后仍然没有找到下一个弹出的数字,那么该序列不可能是一个弹出序列。

import java.util.Stack;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
      Stack<Integer> stack = new Stack<>();
        int i = 0;
        for(int j=0;j<pushA.length;j++) {
            stack.push(pushA[j]);
            while(!stack.isEmpty() && stack.peek().equals(popA[i])) {
                stack.pop();
                i++;
            }
        }
        return stack.isEmpty();
    }
}

32:从上到下打印二叉树

题目一:
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
思路:
按层遍历,队列

import java.util.ArrayList;
import java.util.Queue;
import java.util.LinkedList;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        if(root == null) return new ArrayList<Integer>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        ArrayList<Integer> ans = new ArrayList<>();
        while (!queue.isEmpty()){
            TreeNode node = queue.poll();
            ans.add(node.val);
            if(node.left != null) queue.add(node.left);
            if(node.right != null) queue.add(node.right);
        }
        return ans;
    }
}

题目二:
分行从上到下打印二叉树,从上到下按层打印二叉树,同一层的节点按照从左到右的顺序打印,每一层打印到一行。
思路:
• 当队列 queue 为空时跳出;
• 新建一个临时列表 tmp ,用于存储当前层打印结果;
• 当前层打印循环: 循环次数为当前层节点数(即队列 queue 长度);

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> res = new ArrayList<>();
        if(root != null) queue.add(root);
        while(!queue.isEmpty()){
            List<Integer> temp = new ArrayList<>();
            for(int i=queue.size(); i > 0;i--){
                TreeNode node = queue.poll();
                temp.add(node.val);
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            res.add(temp);
        }
        return res;
    }
}

题目三:
之字形打印二叉树。请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
思路:
方法同上,但是打印时, 若为奇数层,将 node.val 添加至 tmp 尾部;否则,添加至 tmp 头部

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> res = new ArrayList<>();
        if(root!=null) queue.add(root);
        while(!queue.isEmpty()){
            LinkedList<Integer> tmp = new LinkedList<>();
            for(int i=queue.size();i>0;i--){
                TreeNode node = queue.poll();
                if(res.size() % 2 == 0) tmp.addLast(node.val);
                else tmp.addFirst(node.val);
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            res.add(tmp);
        }
        return res;
    }
}

33:二叉搜索树的后续遍历序列

题目:
输入一个整数数组,判断该数组是不是某二叉搜索树的后续遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。例如,输入数组{5, 7, 6, 9, 11, 10, 8},则返回 true,因为这个整数序列时图 4.9 二叉搜索树的后序遍历结果。如果输入的数组是{7, 4, 6, 5},则由于没有哪棵二叉搜索树的后序遍历结果是这个序列,因此返回 false。
思路:

  1. 递归:在后续遍历得到的序列中,最后一个数字是树的根节点的值。数组中前面的数字可以分为两部分:第一部分是左子树节点的值,它们都比根节点的值小;第二部分是右子树节点的值,它们都比根节点的值大。接下来用同样的方法确定与数组每一部分对应的子树的结构
  2. 辅助栈:后续遍历倒序(根-右-左),记每个节点为ri,若 ri > root,则此后续遍历序列不满足二叉搜索树定义,返回false;当栈不为空且 ri < 栈顶值时,循环弹出栈,并将值赋给root;将 ri 压入栈;若遍历完成,则说明后续遍历满足二叉搜索树定义
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        Stack<Integer> stack = new Stack<>();
        int root = Integer.MAX_VALUE;
        for(int i=postorder.length-1;i>=0;i--) {
            if(postorder[i]>root) return false;
            while(!stack.isEmpty() && stack.peek()>postorder[i]){
                root = stack.pop();
            }
            stack.push(postorder[i]);
        }
        return true;
    }
}

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

题目:
输入一颗二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
思路:
• 先序遍历: 按照 “根、左、右” 的顺序,遍历树的所有节点
• 路径记录: 在先序遍历中,记录从根节点到当前节点的路径。当路径为根节点到叶节点形成的路径且各节点值的和等于目标值 sum时,将此路径加入结果列表

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    LinkedList<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        recur(root, sum);
        return res;
    }
    private void recur(TreeNode root, int target){
        if(root==null) return;
        path.add(root.val);
        target -= root.val;
        if(target == 0 && root.left == null && root.right == null)
            res.add(new LinkedList(path));
        recur(root.left, target);
        recur(root.right, target);
        path.removeLast();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值