二叉树的遍历
递归及树的遍历
递归
递归三要素:
- 确定递归函数的参数和返回值:确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数,并且还要明确每次递归的返回值是什么,进而确定递归函数的返回类型。
- 确定终止条件:写完了递归算法,运行的时候经常会遇到栈溢出的错误,如果没写终止条件或者终止条件写的不对,操作系统的内存栈必然就会溢出。
- 确定单层递归的逻辑:确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程
以前序遍历为例:
-
确定递归函数的参数和返回值:因为要打印出前序遍历节点的数值,所以参数里需要传入放在节点的数值,除了这一点就不需要再处理什么数据了也不需要有返回值,所以递归函数返回类型就是void:
void traversal(TreeNode cur,ArrayList value) -
确定终止条件:在递归过程中,如何判断递归结束,就是当前遍历的节点为空,那么结束本层递归
if(cur == null) return; -
确定单层递归的逻辑:前序遍历为中左右的顺序
value.add(cur.val); traversal(cur.left,value); traversal(cur.right,value);
二叉树的遍历
前序遍历:
-
递归实现
public void preorderTraversal(TreeNode root){ List<Integer> result = new ArrayList<>(); preorder(root,result); return result; } public void preorder(TreeNode root,List<Integer> result){ if(root == null) return; result.add(root.val); preorder(root.left,result); preorder(root.right,result); } -
迭代实现
前序遍历是中左右,每次先处理中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。这样保证出栈顺序为中左右。
注意:空节点不入栈
class Iterator1{ public List<Integer> preorderTraversal(TreeNode root){ List<Integer> result = new ArrayList<>(); if(root == null){ return result; } Stack<TreeNode> stack = new Stack<>(); stack.push(root); while(!stack.isEmpty()){ TreeNode node = stack.pop(); result.add(node.val); if(node.right != null){ stack.push(node.right); } if(node.left != null){ stack.push(node.left); } } return result; } }
中序遍历:
-
递归实现
public void inorderTraversal(TreeNode root){ List<Integer> result = new ArrayList<>(); preorder(root,result); return result; } public void inorder(TreeNode root,List<Integer> result){ if(root == null) return; preorder(root.left,result); result.add(root.val); preorder(root.right,result); } -
迭代实现
中序遍历为左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(存放节点数值),这就是的处理顺序和访问顺序不一致。
class Iterator2{ public List<Integer> inorderTraversal(TreeNode root){ List<Integer> result = new ArrayList<>(); if(root == null){ return result; } Stack<TreeNode> stack = new Stack<>(); TreeNode cur = root; while(cur != null || !stack.isEmpty()){ if(cur != null){ stack.push(cur); cur = cur.left; }else{ cur = stack.pop(); result.add(cur.val); cur = cur.right; } } return result; } }
后序遍历:
-
递归实现
public void postorderTraversal(TreeNode root){ List<Integer> result = new ArrayList<>(); preorder(root,result); return result; } public void postorder(TreeNode root,List<Integer> result){ if(root == null) return; preorder(root.left,result); preorder(root.right,result); result.add(root.val); } -
迭代实现
前序遍历是中左右,后序遍历是左右中,只需要调整前序遍历的顺序然后再翻转结果数组,输出的结果就是左右中了。
class Iterator3{ public List<Integer> preorderTraversal(TreeNode root){ List<Integer> result = new ArrayList<>(); if(root == null){ return result; } Stack<TreeNode> stack = new Stack<>(); stack.push(root); while(!stack.isEmpty()){ TreeNode node = stack.pop(); result.add(node.val); if(node.left != null){ stack.push(node.left); } if(node.right != null){ stack.push(node.right); } } Collections.reverse(result); return result; } }
层序遍历:
-
递归实现
class Recursion{ public List<List<Integer>> reList = new ArrayList<List<Integer>>(); public List<List<Integer>> levelOrder(TreeNode root){ checkFun01(root); return reList; } public void checkFun01(TreeNode node, Integer deep){ if(node == null) return; deep++; if(reList.size() < deep){ //当层级增加时,list的Item也增加,利用list的索引值进行层级界定 List<Integer> item = new ArrayList<Integer>(); reList.add(item); } resList.get(deep - 1).add(node.val); checkFun01(node.left,deep); checkFun02(node.right,deep); } } -
迭代实现
//借助队列 class Recursion{ public List<List<Integer>> reList = new ArrayList<List<Integer>>(); public List<List<Integer>> levelOrder(TreeNode root){ checkFun02(root); return reList; } public void checkFun02(TreeNode node){ if(node == null) return; Queue<TreeNode> que = new LinkedList<TreeNode>(); que.offer(node); while(!que.isEmpty()){ List<Integer> itemList = new ArrayList<Integer>(); int len = que.size(); while(len > 0){ TreeNode tmpNode = que.poll(); itemList.add(tmpNode.val); if(tmpNode.left != null) que.offer(tmpNode.left); if(tmpNode.right != null) que.offer(tmpNode.right); len--; } reList.add(itemList); } } }
根据遍历序列构造二叉树
从中序与后序遍历序列构造二叉树
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
return buildTree1(inorder, 0, inorder.length, postorder, 0, postorder.length);
}
public TreeNode buildTree1(int[] inorder, int inLeft, int inRight,
int[] postorder, int postLeft, int postRight) {
// 没有元素了
if (inRight - inLeft < 1) {
return null;
}
// 只有一个元素了
if (inRight - inLeft == 1) {
return new TreeNode(inorder[inLeft]);
}
// 后序数组postorder里最后一个即为根结点
int rootVal = postorder[postRight - 1];
TreeNode root = new TreeNode(rootVal);
int rootIndex = 0;
// 根据根结点的值找到该值在中序数组inorder里的位置
for (int i = inLeft; i < inRight; i++) {
if (inorder[i] == rootVal) {
rootIndex = i;
break;
}
}
// 根据rootIndex划分左右子树
root.left = buildTree1(inorder, inLeft, rootIndex,
postorder, postLeft, postLeft + (rootIndex - inLeft));
root.right = buildTree1(inorder, rootIndex + 1, inRight,
postorder, postLeft + (rootIndex - inLeft), postRight - 1);
return root;
}
}
从前序与中序遍历序列构造二叉树
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return helper(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
}
public TreeNode helper(int[] preorder, int preLeft, int preRight,
int[] inorder, int inLeft, int inRight) {
// 递归终止条件
if (inLeft > inRight || preLeft > preRight) return null;
// val 为前序遍历第一个的值,也即是根节点的值
// idx 为根据根节点的值来找中序遍历的下标
int idx = inLeft, val = preorder[preLeft];
TreeNode root = new TreeNode(val);
for (int i = inLeft; i <= inRight; i++) {
if (inorder[i] == val) {
idx = i;
break;
}
}
// 根据 idx 来递归找左右子树
root.left = helper(preorder, preLeft + 1, preLeft + (idx - inLeft),
inorder, inLeft, idx - 1);
root.right = helper(preorder, preLeft + (idx - inLeft) + 1, preRight,
inorder, idx + 1, inRight);
return root;
}
}
注意:前序和后序遍历序列无法唯一确定一棵二叉树,因为没有中序遍历无法确定左右部分,也就无法进行分割。
浙公网安备 33010602011771号