举例让抽象问题具体化
- 包含 min 函数的栈
- 栈的压入、弹出序列
- 从上到下打印二叉树
- 二叉搜索树的后序遍历序列
- 二叉树中和为某一值的路径
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。
思路:
- 递归:在后续遍历得到的序列中,最后一个数字是树的根节点的值。数组中前面的数字可以分为两部分:第一部分是左子树节点的值,它们都比根节点的值小;第二部分是右子树节点的值,它们都比根节点的值大。接下来用同样的方法确定与数组每一部分对应的子树的结构
- 辅助栈:后续遍历倒序(根-右-左),记每个节点为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();
}
}