[LeetCode]-二叉树-1-遍历系列

前言

记录LeetCode刷题中遇到的二叉树相关题目,第一篇。第二篇地址
二叉树是递归性很强的结构,很多问题都能通过递归求解

144.二叉树的前序遍历

递归实现:

class Solution {
    private List<Integer> res = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {
        pre(root);
        return res;
    }
    private void pre(TreeNode node){
        if(node == null) return;
        res.add(node.val);
        pre(node.left);
        pre(node.right);
    }
}

迭代实现思路:借助栈,开始时先把根节点入栈,然后开始循环:取出栈顶元素,先放入这个栈顶结点的右儿子再放入左儿子,然后继续下一次循环取出栈顶元素…直到栈为空,取出的结点顺序就是前序遍历的顺序

94.二叉树的中序遍历

class Solution {
    private List<Integer> res = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        in(root);
        return res;
    }
    private void in(TreeNode node){
        if(node == null) return;
        in(node.left);
        res.add(node.val);
        in(node.right);
    }
}

非递归实现

class Solution {
    private ArrayList<Integer> res = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        TreeNode t = root;
        if(t != null){
            LinkedList<TreeNode> stack = new LinkedList<>();
            while(t != null || !stack.isEmpty()){
            	//把左儿子的左儿子的左儿子.....所有左儿子先入栈
                while(t != null){
                    stack.push(t);
                    t = t.left;
                }
                if(!stack.isEmpty()){
                    t = stack.poll();
                    res.add(t.val);
                    t = t.right;
                }
            }
        }
        return res;
    }
}

145.二叉树的后序遍历

class Solution {
    private List<Integer> res = new ArrayList<>();
    public List<Integer> postorderTraversal(TreeNode root) {
        post(root);
        return res;
    }
    private void post(TreeNode node){
        if(node == null) return;
        post(node.left);
        post(node.right);
        res.add(node.val);
    }
}

非递归实现

class Solution {
    private List<Integer> res = new ArrayList<>();
    public List<Integer> postorderTraversal(TreeNode root) {
        TreeNode t = root,tem = null;
        LinkedList<TreeNode> stack = new LinkedList<>();
		while(t != null || !stack.isEmpty()){
			if(t != null){
				stack.push(t);
				t= t.left;
			}
			else{
				t = stack.peek();
				//"t.right != tem"的作用在于防止把当前节点已经遍历过的右儿子再次遍历
				if(t.right != null && t.right != tem)
					t = t.right;
				else{
					stack.poll();
                    res.add(t.val);
					tem = t;
					t = null;
				}
			}
		}
		return res;
    }
}

102.二叉树的层序遍历

借助队列,开始先把根节点放入队列,然后开始循环:访问队列头元素,然后将其左右儿子分别入队。重复循环直到队列为空即可。在具体实现时,由于答案要求每一层的结点作为一个list,所以要用第二个临时队列存放左右儿子

也可以不用临时队列:每开始遍历新一层的结点时,先获取当前队列中的元素个数i,那么这就是当前遍历的这一层的节点总数,接下来只要从队列中取i元素就行,然后就可以把遍历到的结点的左右儿子直接放入队列,因为只会从队列中取i个元素,后面加入的结点不会再在这次遍历中取到的(具体代码请看下面的429题)

class Solution {
    private ArrayList<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        if(root != null){
            TreeNode t = root;
            LinkedList<TreeNode> queue = new LinkedList<>();
            LinkedList<TreeNode> queue1 = new LinkedList<>();
            queue.offer(t);
            while(!queue.isEmpty()){
                ArrayList<Integer> list = new ArrayList<>();
                //每次queue中放的都是同一层的结点,所以当queue每次为空时,当层的结点就被访问完了
                while(!queue.isEmpty()){
                    t = queue.poll();
                    list.add(t.val);
                    //先将访问到的结点的左右儿子(即下一层的结点)放入第二个队列待第一个队列为空后
                    //再放入第一个队列,直接放入第一个队列的话会影响结点的层次关系
                    /*===不过更好的方式是,每遍历一层前先用queue.size()获取当前队列
                    的大小,也就是当前这一层的节点总数,然后这次循环只取队列前面这几个节点就行,
                    这样就可以直接把新的节点放到队列中而不怕在这次循环就被取出*/
                    if(t.left != null) queue1.offer(t.left);
                    if(t.right != null) queue1.offer(t.right);
                }
                res.add(list);
                while(!queue1.isEmpty()){
                    queue.offer(queue1.poll());
                }
            }
        }
        return res;
    }
}

107.二叉树的层次遍历2

只需把102题的res改为LinkedList,作为栈来使用,将每一层的遍历结果压栈,就可以实现从底向上的效果了

class Solution {
    private LinkedList<List<Integer>> res = new LinkedList<>();
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        if(root != null){
            TreeNode t = root;
            LinkedList<TreeNode> queue = new LinkedList<>();
            LinkedList<TreeNode> queue1 = new LinkedList<>();
            queue.offer(t);
            while(!queue.isEmpty()){
                ArrayList<Integer> list = new ArrayList<>();
                while(!queue.isEmpty()){
                    t = queue.poll();
                    list.add(t.val);
                    if(t.left != null) queue1.offer(t.left);
                    if(t.right != null) queue1.offer(t.right);
                }
                //采用push(),压栈
                res.push(list);
                while(!queue1.isEmpty()){
                    queue.offer(queue1.poll());
                }
            }
        }
        return res;
    }
}

103. 二叉树的锯齿形层序遍历

在层次遍历的基础上,加一个标志变量 flag,当 flag 为 true,表示当前层次要从左到右遍历;当 flag 为 false,表示当前层次要从右到左遍历
当然不用真的从右往左遍历,只需从左往右遍历,但遍历到的节点不是放到队列尾,而是放到队列头

public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    if(root == null) return new LinkedList<List<Integer>>();
    List<List<Integer>> res = new ArrayList<>();
    LinkedList<TreeNode> list = new LinkedList<>();
    list.offer(root);
    boolean flag = true;
    while(!list.isEmpty()){
        int s = list.size();
        LinkedList<Integer> level = new LinkedList<>();
        for(int i = 0;i < s;i++){
            TreeNode t = list.poll();
            if(flag){
                level.offerLast(t.val);
            }else{
                level.offerFirst(t.val);
            }
            if(t.left != null) list.offer(t.left);
            if(t.right != null) list.offer(t.right);
        }
        res.add(level);
        flag = !flag;
    }
    return res;
}

429.N叉树的层序遍历

层次遍历的基础上修改一下对儿子节点的判断就可以了

class Solution {
    private ArrayList<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> levelOrder(Node root) {
        if(root != null){
            Node t = root;
            LinkedList<Node> queue = new LinkedList<>();
            queue.offer(t);
            while(!queue.isEmpty()){
                ArrayList<Integer> list = new ArrayList<>();
                //当前队列中的元素个数就是当前这一层的节点个数,所以只用遍历count个队列元素就可以了
                int count = queue.size();
                for(int i = 1;i <= count;i++){
                    t = queue.poll();
                    list.add(t.val);
                    int num = t.children.size();
                    //所有不为空的儿子入队
                    for(int j = 0;j < num;j++){
                        Node chi = t.children.get(j);
                        if(chi != null) queue.offer(chi);
                    }
                }
                res.add(list);
            }
        }
        return res;
    }
}

105.从前序与中序遍历序列构造二叉树(每日一题)

前序遍历的访问顺序是先访问根,再访问左子树,再访问右子树
中序遍历的访问顺序是先访问左子树,再访问根,再访问右子树
根据访问顺序的规律我们可以把当前这棵树分为根,左子树,右子树三个部分,如:
前序遍历:1 24365 87
中序遍历:42635 1 87
可以找出当前这棵树的根为1,24365是左子树的部分,87是右子树的部分,怎么知道要这么分?在中序遍历中找到根,也就是1,也就是前序遍历序列的第一个值,的位置就可以了。所以我们可以构造一个根据前序跟中序遍历序列找出根的方法,然后分治,递归,对左右子树的遍历序列调用这个方法就可以

public TreeNode buildTree(int[] preorder, int[] inorder) {
	//结束:遍历序列为空时返回空
    if(preorder.length == 0) return null;
    TreeNode root = new TreeNode(preorder[0]);
    for(int i = 0;i < preorder.length;i++){
    	//在中序遍历序列中找根的位置
        if(inorder[i] == preorder[0]){
        	//找到根的位置后就可以得出左,右子树的遍历序列了
            int[] newPre1 = Arrays.copyOfRange(preorder,1,i + 1);
            int[] newIn1 = Arrays.copyOfRange(inorder,0,i);
            //分解,对左子树部分的遍历序列调用方法的返回结果就是左子树的根
            //     对右子树部分的遍历序列调用方法的返回结果就是右子树的根
            root.left = buildTree(newPre1,newIn1);
            int[] newPre2 = Arrays.copyOfRange(preorder,i + 1,preorder.length);
            int[] newIn2 = Arrays.copyOfRange(inorder,i + 1,inorder.length);
            root.right = buildTree(newPre2,newIn2);
            break;
        }
    }
    return root;
}

106.从中序与后序遍历序列构造二叉树

思路跟105差不多。不同的是,这里根变成了后序遍历的最后一个值;截取左右子树的遍历序列的方式也有一些不同

public TreeNode buildTree(int[] inorder, int[] postorder) {
    if(postorder.length == 0) return null;
    int l = postorder.length;
    TreeNode root = new TreeNode(postorder[l - 1]);
    for(int i = 0;i < l;i++){
        if(inorder[i] == postorder[l - 1]){
            int[] newPost1 = Arrays.copyOfRange(postorder,0,i);
            int[] newIn1 = Arrays.copyOfRange(inorder,0,i);
            root.left = buildTree(newIn1,newPost1);
            int[] newPost2 = Arrays.copyOfRange(postorder,i,l - 1);
            int[] newIn2 = Arrays.copyOfRange(inorder,i + 1,l);
            root.right = buildTree(newIn2,newPost2);
            break;
        }
    }
    return root;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值