记录树的前中后序和层次遍历的写法。
一、前序遍历
1.递归写法
public void preOrder(){ preOrder(root); } private void preOrder(TreeNode node){ if(node == null) return; System.out.println(node.val); //先打印的是该节点,在访问左节点和右节点所以是前序 preOrder(node.left); preOrder(node.right); }
2.非递归写法
public void preOrder1(TreeNode root){ if(root == null) return ; Stack<Node> stack = new Stack<>(); stack.push(root); while(!stack.isEmpty()) { Node temp = stack.pop(); System.out.println(temp.val); if(temp.right != null) stack.push(temp.right); if(temp.left != null) stack.push(temp.left); } return ; }
二、中序遍历
1.递归写法
// 二分搜索树的中序遍历 public void inOrder(){ inOrder(root); } // 中序遍历以node为根的二分搜索树, 递归算法 private void inOrder(TreeNode node){ if(node == null) return; inOrder(node.left); System.out.println(node.val); inOrder(node.right); }
2.非递归写法
写法相比于前序的非递归要稍微复杂些,因为中序需要一直压节点,因此需要反复判断。本写法的遍历结果用List存放。
写法1:只要当前节点不为空,每次都压入当前节点。当前节点为空,说明该节点的父节点就是叶子节点,且此时栈顶节点就是该空节点的父节点,因此弹出后就可加入到队列中(打印),然后去遍历该父节点的右子节点。
public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); Stack<TreeNode> stack = new Stack<>(); while (root != null || !stack.isEmpty()) { if (root != null) { stack.push(root); root = root.left; } else { TreeNode node = stack.pop(); res.add(node.val);//这里打印 root = node.right; } } return res; }
第2个写法,压入当前节点的左子节点或者右子节点(这里我的idea上写法方法有误,要补上最后一个else)
public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); if(root==null) return res; Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode temp = root;//辅助节点 stack.push(temp); while(stack.isEmpty() == false) { //只要你有左孩子,就将左孩子压入栈中 if(temp!=null && temp.left!=null) { stack.add(temp.left); temp = temp.left; }else { temp = stack.pop();//弹出栈顶节点 左孩子--->根节点 res.add(temp.val);//访问 if(temp!=null && temp.right!=null) { //如果栈顶元素有右孩子的话,将有节点压入栈中 stack.add(temp.right); temp = temp.right; }else temp=null; //这个else不补上,会导致没有右子节点的节点,会又被访问一次 } } return res; }
三、后序遍历
1.递归写法
public void postOrder(){ postOrder(root); } private void postOrder(Node node){ if(node == null) return; postOrder(node.left); postOrder(node.right); System.out.println(node.val); }
2.非递归写法
主要记录2个写法。
第一个写法:
前序是根左右,中序是左根右,后序是左右根。我们知道前序的根左右很好写,相应的我们写一个根右左也很好写,我们再用一个栈将根右左变成左右根,这样就形成了后序遍历。
public List<Integer> postorderTraversal(TreeNode root) { List<Integer> list = new ArrayList(); if (root == null) return list; Stack<TreeNode> s1 = new Stack<>(); Stack<TreeNode> s2 = new Stack<>(); s1.push(root); while (!s1.isEmpty()) { root = s1.pop(); s2.push(root); if (root.left != null) { s1.push(root.left); } if (root.right != null) { s1.push(root.right); } } while (!s2.isEmpty()) { list.add(s2.pop().val); } return list; }
写法2:
考虑到根节点是最后遍历到的,因此我们需要知道,某个节点访问完后,到底是访问这个节点的父节点,还会这个节点的兄弟节点(右节点),如果不加判断,就不知道下一个节点应该怎么访问,因为,需要一个变量或者数据结构去记录一些遍历的信息。
public List<Integer> postorderTraversal(TreeNode root) { List<Integer> res = new LinkedList<>(); Stack<TreeNode> stack = new Stack<>(); TreeNode curr = root; TreeNode last = null; while (curr != null || !stack.isEmpty()) { while (curr != null) { stack.push(curr); curr = curr.left; } curr = stack.peek(); //如果某个节点没有右节点,或者右边节点访问过了,那么就将该节点添加到结果集,并弹出 if (curr.right == null || curr.right == last) { res.add(curr.val); stack.pop(); last = curr;//用last记录这个弹出的节点(已访问过) curr = null; } else {//如果栈顶元素还有右子节点,就得入栈 curr = curr.right; } } return res; }
四、层序遍历
一般是用队列
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) { ArrayList<Integer> res = new ArrayList<>(); if (root == null) { return res; } Queue<TreeNode> q = new LinkedList<>(); q.add(root); while (!q.isEmpty()) { TreeNode nowNode = q.poll(); res.add(nowNode.val); if (nowNode.left != null) { q.add(nowNode.left); } if (nowNode.right != null) { q.add(nowNode.right); } } return res; }