算法回顾--二叉树基础知识

二叉树的各种遍历

递归遍历

中序遍历:中序遍历的顺序是左根右,递归写法比较简单。直接贴上模板

public void inorder(TreeNode root){
    
    
    if(root == null)	return;
    if(root.left != null) inorder(root.left);
    //当前位置即为操作,可以根据题意进行添加
    if(root.right != null)	inorder(root.right);
}

例如,lettcode94题遍历二叉树使用中序遍历的方法,但是需要放回一个list,那么可以在上面代码注释处添加这部分的逻辑

public List<Integer> inorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<Integer>();
    inorder(root, res);
    return res;
}
public void inorder(TreeNode root, List<Integer> res){
    
    
    if(root == null)    return;
    if(root.left != null)   inorder(root.left, res);
    res.add(root.val);
    if(root.right != null)  inorder(root.right, res);
}

那么,直接举一返二,写出先序遍历和后序遍历的写法,只需要将操作的逻辑代码放置在不同位置即可

//先序遍历
public List<Integer> preorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<Integer>();
    inorder(root, res);
    return res;
}
public void preorder(TreeNode root, List<Integer> res){
    
    
    if(root == null)    return;
    res.add(root.val);
    if(root.left != null)   inorder(root.left, res);
    if(root.right != null)  inorder(root.right, res);
}
//后序遍历
public List<Integer> postorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<Integer>();
    inorder(root, res);
    return res;
}
public void postorder(TreeNode root, List<Integer> res){
    
    
    if(root == null)    return;
    if(root.left != null)   inorder(root.left, res);
    if(root.right != null)  inorder(root.right, res);
    res.add(root.val);
}

非递归遍历

递归遍历的优点是代码简洁,但是在二叉树的节点比较多的时候,容易出现爆栈StackOverFlow,所以我们一般也会写上二叉树的非递归写法,一般来说递归写法都就可以更改为非递归写法,这就需要借助一种数据结构栈,栈的特点是先进后出,对于中序遍历来说是先遍历左节点的,那么可以先将所有左节点加入至栈中,然后将最后一个左节点拿出来得到值,然后取出右节点,重复即可

public List<Integer> inorder(TreeNode root){
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<>();
    while(root != null && !stack.isEmpty()){
    
    
		while(root != null){
    
    
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        res.add(root.val);
        root = root.right;
    }
    return res;
}

有了中序遍历的基础,写先序遍历就可以直接写出来了

public List<Integer> perorder(TreeNode root){
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<>();
    while(root != null || !stack.isEmpty()){
    
    
        while(root != null){
    
    
            res.add(root.val);
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        root = root.right;
    }
    return res;
}

那么后序遍历呢?进行后序遍历的时候,需要判断当前节点是否具有左右节点,比较麻烦,这里使用一种比较灵活的方法,因为先序遍历是根左右,而后序遍历是左右根,可以将原二叉树的左右子树进行颠倒,然后将先序遍历的结果反过来既可以得到后序遍历的结果,例如

原树:

      1
    /    \
   2      3
  /  \    /  \
 7   6   4	  5

后序遍历: 7 6 2 4 5 3 1

反转左右节点

      1
    /    \
   3      2
  /  \   /  \ 
 5    4 6	 7

先序遍历: 1 3 5 4 2 6 7

将先序遍历反转得到:7 6 2 4 5 3 1就是原树的后序遍历,而反转左右子树比较简单可以做到,只需要在先序遍历的时候先遍历右子树,然后遍历左子树即可。

public List<Integer> postorder(TreeNode root){
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<>();
    while(root != null || !stack.isEmpty()){
    
    
        while(root != null){
    
    
            res.add(root.val);
            stack.push(root);
            root = root.right;
        }
        root = stack.pop();
        root = root.left;
    }
    Collections.reverse(res);
    return res;
}

层次遍历

层次遍历是指按照从上到下,从左到右的顺序,那么一般可以使用BFD,广度优先算法,而这个算法的实现需要借助一个数据结构队列,队列的优点是先进先出

为什么使用队列?

    3
   / \
  9  20
    /  \
   15   7

返回:

[3,9,20,15,7]

根据上面的例子,可知我们需要将每一个节点保存下来,以便获取到该节点可能的左右子树节点,如果而打印是从左到右打印的,使用队列先进先出恰好满足这个特点

public int[] levelOrder(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> q = new LinkedList<>();
    if(root == null)    return new int[]{
    
    };
    q.add(root);
    while(!q.isEmpty()){
    
    
        TreeNode temp = q.poll();                
        res.add(temp.val);
        if(temp.left != null) q.add(temp.left);
        if(temp.right != null) q.add(temp.right);
    }
    int[] r = new int[res.size()];
    for(int i = 0;i < res.size();i++){
    
    
        r[i] = res.get(i);
    }
    return r;
}

这里也简单的复习一下Deque作为栈和队列的一些常用方法

作为栈

方法 作用
push(e) 弹入
pop() 弹出
peek() 取栈顶元素

作为队列

方法 作用
offer(e) 向队首加入
poll() 从队尾排除
peek() 从队尾取

练习

剑指offer32题 1

从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

例如:
给定二叉树: [3,9,20,null,null,15,7],
	3	
   / \
  9  20
    /  \
   15   7
返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

与层次遍历不同的是,这个题目还需要将每一层的数据打印成一行,这就需要将因为队列里面存储的是每一行的数据,这就需要每循环的时候对当前的队列进行操作,可以先将队列的size记住,然后把子节点加入

public List<List<Integer>> levelOrder(TreeNode root) {
    
    
    List<List<Integer>> res = new ArrayList<>();
    Deque<TreeNode> q = new LinkedList<>();

    if(root == null)    return new ArrayList<List<Integer>>();
    q.offer(root);
    while(!q.isEmpty()){
    
    
        List<Integer> tempA = new ArrayList<>();
        //这行代码很重要,需要记住当前层的队列数量
        // for(int i = queue.size(); i > 0; i--)
        int size = q.size();
        for(int i = 0;i < size;i++){
    
    
            TreeNode temp = q.poll();
            if(temp.left != null) q.offer(temp.left);
            if(temp.right != null) q.offer(temp.right);
            tempA.add(temp.val);
        }
        res.add(tempA);
    }
    return res;
}

剑指offer32题 2

请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

    3
   / \
  9  20
    /  \
   15   7
返回其层次遍历结果:

[
  [3],
  [20,9],
  [15,7]
]

这是上面题目的变种,只需要遇到偶数行将数据进行反转即可

public List<List<Integer>> levelOrder(TreeNode root) {
    
    
        List<List<Integer>> res = new ArrayList<>();
        Deque<TreeNode> q = new LinkedList<>();
        if(root == null) return new ArrayList<>();
        int flag = 1;
        q.offer(root);
        while(!q.isEmpty()){
    
    
            List<Integer> temp = new ArrayList<>();
            for(int i = q.size();i >0 ;i--){
    
    
                TreeNode node = q.poll();
                temp.add(node.val);
                if(node.left != null)   q.offer(node.left);
                if(node.right != null)  q.offer(node.right);
            }
            if(flag % 2 == 0){
    
    
                Collections.reverse(temp);
            }
            res.add(temp);
            flag++;
        } 
        return res;
    }

猜你喜欢

转载自blog.csdn.net/weixin_44706647/article/details/115190793