文章概述
介绍了二叉树的4种遍历算法,每一种都用递归和迭代完成。
题目链接
二叉树的前序遍历
二叉树的中序遍历
二叉树的后序遍历
二叉树的层序遍历
递归遍历
前中后序的递归遍历比较简单,就是按照不同的遍历顺序依次写就可以。
前序遍历:
private static void postTravel(TreeNode root, ArrayList<Integer> list) {
if(root == null){
return;
}
list.add(root.val);
if(root.left!=null) postTravel(root.left,list);
if(root.right!=null) postTravel(root.right,list);
}
中序遍历
private static void postTravel(TreeNode root, ArrayList<Integer> list) {
if(root == null){
return;
}
if(root.left!=null) postTravel(root.left,list);
list.add(root.val);
if(root.right!=null) postTravel(root.right,list);
}
后序遍历
private static void postTravel(TreeNode root, ArrayList<Integer> list) {
if(root == null){
return;
}
if(root.left!=null) postTravel(root.left,list);
if(root.right!=null) postTravel(root.right,list);
list.add(root.val);
}
迭代遍历
迭代遍历是利用栈先进后出的特点,前序遍历是中左右,每次先把中间节点计入结果,然后将右孩子入栈,再将左孩子入栈,得到的结果就是中左。
public List<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<>();
//前序是中左右
Stack<TreeNode> stack = new Stack<>();
if(root == null) return res;
stack.push(root);
while (!stack.isEmpty()){
TreeNode pop = stack.pop();
res.add(pop.val);
if(pop.right!=null){
stack.push(pop.right);
}
if(pop.left!=null){
stack.push(pop.left);
}
}
return res;
}
后序遍历可以参考前序遍历,后序遍历是左右中,可以先得到中右左,再翻转。因为得到中间节点比较简单。
public List<Integer> postorderTraversal(TreeNode root) {
//迭代写法:
//后序遍历是左右中,可以做一个中右左,再倒序
ArrayList<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root == null) return res;
stack.push(root);
while (!stack.isEmpty()){
TreeNode pop = stack.pop();
res.add(pop.val);
if(pop.left!=null){
stack.push(pop.left);
}
if(pop.right!=null){
stack.push(pop.right);
}
}
Collections.reverse(res);
return res;
}
中序遍历的思想就是一直看左边,左边没有了就将节点读取,并将右孩子节点加入,手动模拟也是这样的步骤。
public List<Integer> inorderTraversal(TreeNode root) {
//顺序是左中右
ArrayList<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
// stack.push(root);
TreeNode cur = root;
while (cur!=null || !stack.isEmpty()){
if(cur !=null){
// 先看左边,有的话就加入
stack.push(cur);
cur=cur.left;
}
else {
//左边都没了的情况下,标记并开始看右边。
cur = stack.pop();
res.add(cur.val);
cur=cur.right;
}
}
return res;
}
迭代的统一写法
因为中序遍历有一点不同,所以记起来比较难,所以用一种统一的写法会方便记忆。
也是用的栈的思想,但是不同的是在中间节点用一个null来代替。
统一格式
while(){
if(!=null){}
else{}
}
前序遍历:
//前序遍历的统一写法
public List<Integer> preTraversalAll(TreeNode root){
ArrayList<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root == null){
return res;
}
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.peek();
if(node != null){
//前序
//前序是中左右,入栈就是右左中
stack.pop();//这个点要弹出
if(node.right!=null){
stack.push(node.right);
}
if(node.left !=null){
stack.push(node.left);
}
stack.push(node);
stack.push(null);
}else {
stack.pop();
TreeNode tp = stack.pop();
res.add(tp.val);
}
}
return res;
}
中序遍历:
public List<Integer> inorderTraversalAll(TreeNode root){
ArrayList<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root == null){
return res;
}
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.peek();
if(node != null){
//中序
//左中右,右中左
stack.pop();//这个点要弹出
if(node.right!=null){
stack.push(node.right);
}
stack.push(node);
stack.push(null);
if(node.left !=null) {
stack.push(node.left);
}
}else {
stack.pop();
TreeNode tp = stack.pop();
res.add(tp.val);
}
}
return res;
}
后序遍历:
public List<Integer> postTraversalAll(TreeNode root){
ArrayList<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root == null){
return res;
}
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.peek();
if(node != null){
//后序
//左右中,变成中右左
stack.pop();//这个点要弹出
stack.push(node);
stack.push(null);
if(node.right!=null){
stack.push(node.right);
}
if(node.left !=null) {
stack.push(node.left);
}
}else {
stack.pop();
TreeNode tp = stack.pop();
res.add(tp.val);
}
}
return res;
}
}
层次遍历
1.递归写法
DFS的思想
private static void dfsLevelOrder(TreeNode node,int depth,List<List<Integer>> res){
if (node == null){
return;
}
depth++;
//新的一层,创建一个新的list
if(res.size()<depth){
ArrayList<Integer> list = new ArrayList<>();
res .add(list);
}
//还是该层,添加元素将即可
res.get(depth-1).add(node.val);
//递归调用
dfsLevelOrder(node.left,depth,res);
dfsLevelOrder(node.right,depth,res);
}
- 迭代写法
利用队列先进先出的特性实现,先记录当前队列的长度n,也就是该层有几个元素,然后依次出队,因为队列中的前n个元素就是该层的元素。然后将所有左右孩子入队,再次记录长度。
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue= new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root == null){
return res;
}
queue.offer(root);
while (!queue.isEmpty()){
List<Integer> list = new ArrayList<>();
int len = queue.size()-1;
while (len>=0){
TreeNode poll = queue.poll();
if(poll!=null) {
list.add(poll.val);
if (poll.left != null) {
queue.add(poll.left);
}
if (poll.right != null) {
queue.add(poll.right);
}
}
len--;
}
res.add(list);
}
return res;
}