LeetCode 二叉树/n叉树的解题思路

二叉树

  • 二叉树特点是每个节点最多只能有两棵子树,且有左右之分
  • 二叉树的数据结构如下:
public class TreeNode {
   //节点的值
   int val;
   //左子树
   TreeNode left;
   //右子树
   TreeNode right;
   TreeNode(int x) { val = x; }
}
  • 树节点的初始化:
    int val=1;
    TreeNode node = new TreeNode(val);
  • 获取树的节点node的值:
   int val = node.val;
  • 二叉树的节点为node,求左右子树:
   TreeNode right =node.right;
   TreeNode left= node.left;
  • N叉树的节点结构如下:
class Node {
    public int val;
    public List<Node> children;
}

树的遍历

树的遍历方式有:前序遍历(又叫先序遍历)、中序遍历、后序遍历
前序遍历:根节点,左节点,右节点。
中序遍历:左节点,根节点,右节点。
后序遍历:左节点,右节点,根节点。

递归法

树经常会用到递归。
树在访问左子树或者右子树的时候,按照同样的方式遍历,直到遍历完整棵树。
使用递归的条件:子问题需与原问题为同样的事,且规模更小;程序有停止条件。
因此整个遍历过程天然具有递归的性质。

  • 比如二叉树的中序遍历(LeetCode94)。
    先序遍历、后序遍历也类似,只是调换了节点的顺序而已。
public class LeetCode94 {
    //list设置为成员变量,如果是方法内变量,无法一直添加元素
    private List<Integer> list = new ArrayList<>();

    public List<Integer> inorderTraversal(TreeNode root) {
        if (root == null) {
            return list;
        }
        //中序遍历:左节点,根节点,右节点。
        inorderTraversal(root.left);
        list.add(root.val);
        inorderTraversal(root.right);
        
        return list;
    }
}
  • 对称二叉树:
    public boolean isSymmetric(TreeNode root) {
        if (root ==null) {
            return true;
        }
        return checkSym(root.left, root.right);
    }

    public boolean checkSym(TreeNode left, TreeNode right) {
        if (left == null && right == null) {
            return true;
        }
        if (left == null || right == null) {
            return false;
        }
        //比较左右节点的值,并继续递归判断。下一层的子树,是否对称,如果对称,左节点的左子节点值等于右节点的右子节点
        return left.val == right.val && checkSym(left.left, right.right) && checkSym(left.right ,right.left );
    }
  • 翻转二叉树:
    从根节点开始,递归地对树进行遍历,并从叶子节点先开始翻转。
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        //获取左节点
        TreeNode left = invertTree(root.left);
        //获取右节点
        TreeNode right = invertTree(root.right);
        //翻转左右节点
        root.left = right;
        root.right = left;

        return root;

    }
  • 比如N叉树的前序遍历(LeetCode589)。
class LeetCode589{
    //list设置为成员变量,如果是方法内变量,无法一直添加元素
    private List<Integer> list = new LinkedList<>();

    public List<Integer> preorder(Node root) {
	if(root==null) {
            return list;
        }
        list.add(root.val);
        for (Node node : root.children) {
            preorder(node);
        }
	return list;
    }
}

迭代法

  • 树还可以用迭代法。利用栈的先进后出解题。
    迭代法是DFS和BFS的基础,可以多学习一下。

BFS(广度优先搜索算法)。

广度优先算法,一般会用到 队列。

BFS的操作步骤如下:
1、使用 Queue的 offer()方法(或者是add()方法)把树的根节点放入 Queue;
2、重复以下步骤,直到 Queue为空为止(也就是while循环条件为 !queue.isEmpty()):
(1)获取 Queue的size, 因为Queue中存放的其实就是每一层中所有的节点, size就相当于每一层的数量,也就是宽度
(2)遍历队列,直到当前这一层所有的节点都遍历完(也就是while循环条件为 size-- > 0 )
(3)在遍历过程中,使用 Queue的 offer()方法得到队列中的节点,根据节点查出它的左节点和右节点,并用offer()方法放入队列中。

题目:LeetCode104、LeetCode102

  • leetCode102:二叉树的层序遍历
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> resultList = new ArrayList<>();
        if (root==null) {
            return resultList;
        }
        //队列的泛型用TreeNode
        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.add(root);
        //遍历队列
        while (!queue.isEmpty()) {
            //此处有坑,一定要先把每一层的数量记录下来,不然队列的长度发生变化,遍历次数不一样
            int n=queue.size();
            //层序遍历,从最上层到最下层,可以用BFS,把每一层的节点放到list里面。
            //每一层都有一个list
            List<Integer> list = new ArrayList<>();
            for (int i=0;i<n;i++) {
                //用poll拿出队列的节点
                TreeNode node = queue.poll();
                list.add(node.val);
                //把当前节点的左子节点、右子节点,放入到队列中。
                if (node.left!=null) {
                    queue.add(node.left);
                }
                if (node.right!=null) {
                    queue.add(node.right);
                }
            }
            resultList.add(list);
        }
        return resultList;

    }
}
  • LeetCode104:求二叉树的最大深度。
public class LeetCode104BFS {
	public int maxDepth(TreeNode root) {
		if (root == null){
                      return 0;
		}
		int depth = 0;
		//队列的实例化,可以通过链表进行实例化。
		Queue<TreeNode> queue= new LinkedList<>();
		//队列使用offer和poll不会抛异常
         //首先,要将根节点放入队列中。这个细节,不要遗漏了,不然 队列为空的循环判断就没法开始。
		nodes.offer(root);
		while (!queue.isEmpty()) {
              //队列中存放的其实就是每一层中所有的节点
              //size就相当于每一层的数量
              //此处有坑,一定要先把每一层的数量记录下来,不然队列的长度发生变化,遍历次数不一样
			int size = queue.size();
			//遍历一次,深度就加一
			depth++;
			//遍历队列中的数据,直到当前这一层所有的节点都遍历完
			while (size-- > 0) {
               //取出队列中的树节点
                TreeNode node = queue.poll();
                //将当前节点的左右子树,放入队列中。
                if (node!=null && node.left != null){
                      queue.offer(node.left);
                }
                if (node!=null && node.right != null){
                      queue.offer(node.right);
                }
			}
		}
		return depth;
	}
}

DFS(深度优先搜索算法)

以深度优先为策略,从根节点开始一直遍历到某个叶子节点。
深度优先算法,一般会用到 栈。

DFS的实现方式相比于BFS应该说大同小异,只是把 queue 换成了stack而已,stack具有后进先出LIFO(Last Input First Output)的特性,DFS的操作步骤如下:
1、把起始点放入stack;
2、重复下述3步骤,直到stack为空为止:
(1)从stack中访问栈顶的点;
(2)找出与此点邻接的且尚未遍历的点(也就是子节点),进行标记,然后全部放入stack中;
(3)如果此点没有尚未遍历的邻接点,则将此点从stack中弹出。

  • LeetCode589:N叉树的前序遍历。
class Solution {
    public List<Integer> preorder(Node root) {
        List<Integer> list = new ArrayList<>();
        if (root == null) return list;
        //将根节点数据添加到栈中
        Stack<Node> stack = new Stack<>();
        stack.add(root);
        
        while (!stack.empty()) {
            //栈顶的数据,出栈
            root = stack.pop();
            //在list中添加栈顶数据
            list.add(root.val);
            //将子节点全部放入栈里面,由于栈是后进先出,所以后面的子节点先放入
            for (int i = root.children.size() - 1; i >= 0; i--)
                stack.add(root.children.get(i));
        }
        
        return list;
    }
}

参考资料

leetCode

posted on   乐之者v  阅读(72)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2021-03-28 Dubbo上传文件之RestEasy
2018-03-28 SpringBoot中Mybatis打印sql
2018-03-28 classpath 和 classpath* 的区别:
2018-03-28 Spring的Cache注解
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示