前言
逆水行舟,不进则退!!!
题目
递归
对二叉树这种结构,遍历的话,递归是最合适的解决方式。
前序遍历
前序遍历递归嘛,很好写,先录入根节点,再递归左子树,然后再递归右子树。没有什么需要注意的。
public void preorderTraversal(TreeNode root, List<Integer> list) {
if(root == null ) {
return ;
}else {
list.add(root.val);
preorderTraversal(root.left, list);
preorderTraversal(root.right, list);
}
}
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<>();
preorderTraversal(root, list);
return list;
}
中序遍历
和前序遍历没什么区别,调整一下顺序即可。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<Integer>();
if(root == null) {
return list;
}
inorderTraversal1(list, root);
return list;
}
public void inorderTraversal1(List<Integer> list, TreeNode root) {
if(root == null) {
return;
}
inorderTraversal1(list, root.left);
list.add(root.val);
inorderTraversal1(list, root.right);
}
后序遍历
后序遍历递归也好写,先将左右孩子录入队列,然后再返回将根节点录入队列。
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<Integer>();
if(root == null) {
return list;
}
postorderTraversal1(list, root);
list.add(root.val);
return list;
}
public void postorderTraversal1(List<Integer> list, TreeNode root) {
if(root == null) {
return;
}
postorderTraversal1(list, root.left);
if(root.left != null) {
list.add(root.left.val);
}
postorderTraversal1(list, root.right);
if(root.right != null) {
list.add(root.right.val);
}
}
非递归
非递归实现二叉树的前中后序遍历的关键是:用压栈和出栈的方式来控制访问的次序,用循环来遍历每一个结点。
前序遍历
定义一个cur变量来遍历整个二叉树,cur每指向一个结点,都将其压入栈中,并且录入链表。先让cur遍历左子树,当cur == null时,弹出栈顶结点,并将cur指向所弹出结点的右子树,然后循环上述步骤。
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<>();
if(root == null) {
return list;
}
Deque<TreeNode> stack = new LinkedList<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()) {
while(cur != null) {
stack.push(cur);
list.add(cur.val); //先入栈,然后录入结点,非递归实现先序遍历的精髓
cur = cur.left;
}
cur = stack.pop();
cur = cur.right;
}
return list;
}
中序遍历
与前序遍历的入栈并录入不同, 中序是出栈便录入到链表当中。先让cur一直向左遍历,每指向一个结点,便压入栈中,cur为空了就弹出栈顶结点并将弹出结点录入到链表中,然后cur指向该弹出结点的右子树,重复上述步骤。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<Integer>();
if(root == null) {
return list;
}
Deque<TreeNode> stack = new LinkedList<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()) {
while(cur != null) {
stack.push(cur);
//list.add(cur.val);
cur = cur.left; //先不停地入栈,直到第一次cur==null
}
cur = stack.pop(); //cur==null 就相当于已经遍历了栈顶元素的左子树,该录入根节点了,所以要弹出该结点
list.add(cur.val);
cur = cur.right; //根结点录入了,就该右子树了,然后判断右子树的左子树(不断循环)
}
return list;
}
后序遍历
非递归的后序遍历用代码实现还是有一点难度的。主要是在右子树的处理上,具体原因我已经注释在代码中了。
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<>();
if(root == null) {
return list;
}
Deque<TreeNode> stack = new LinkedList<>();
TreeNode cur = root;
TreeNode top = null;
while(!stack.isEmpty() || cur != null) {
while(cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.peek();
cur = cur.right;
if(cur == null || cur ==top) { // 我觉得最难解决的是对右子树的处理
//因为在右子树 X 不为空时,程序会继续判断X的左右子树,
//这时左右都为null,弹出栈顶结点X 并录入,
//然后cur指向了当前的栈顶元素:X的父结点,
// 然后,cur又指向了父结点的右子树X ,陷入了死循环。
//所以需要一个top引用,来记录上一个弹出的结点,
// 如果top记录的结点和cur指向的结点相同,就继续弹出。
//如此 便破解了死循环。
top = stack.pop();
list.add(top.val);
cur = null;
}
}
return list;
}
我是专注学习的章鱼哥~