【刷题笔记】二叉树1

1. 什么是二叉树?

        二叉树和链表的形式类似,都分为两个部分,即值value和指向下一个节点的next,只不过二叉树区别于链表,有两个指向:left和right。具体区别见下图:

链表
链表
二叉树

 代码上的区别如下:

(1)链表

public class ListNode{
    int val;
    ListNode next;

    ListNode(){}

    ListNode(int val) {
        this.val = val;
    }

    ListNode (int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

(2)二叉树

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {}

    TreeNode(int val) {
        this.val = val;
    }

    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

2. 二叉树的遍历方式

        二叉树的遍历分为两种,一种是深度优先,一种是广度优先,其中深度优先就是我们常说的前序遍历、中序遍历以及后序遍历。而广度优先,顾名思义就是一层一层的遍历二叉树。

下面举例分别展示一下各种遍历的差别:

二叉树

以这棵树为例:

        首先,前序遍历的顺序为:中左右,就是先把中间节点记录下来,再去按照这个顺序遍历它的左子树,左子树遍历完再去遍历它的右子树。最终顺序为:1245367

        其次,中序遍历的顺序为:左中右,就是遍历左子树在记录中见节点,再遍历右子树。最终顺序为:4251637

        然后,后序遍历的顺序为:左右中,就是先遍历两棵子树,再加上中间节点。最终顺序为:4526731

        最后,层序遍历,顾名思义就是一层一层的遍历,遍历的顺序为:1234567

3. 练手题目

        下面我们就挑几道题来练练手:

3.1 二叉树的前序遍历

144. 二叉树的前序遍历

方法一:递归

        首先是递归的方法,前、中、后序遍历其实比较类似。首先,我们在做题之前要明确一点,就是要把收集结果的那一个步骤作为“中”,向左遍历作为“左”,向右遍历作为“右”。于是有,前序遍历就是:中左右,即收集结果->向左遍历->向右遍历。

        看下面递归代码(reverse方法),首先递归的流程,要有退出条件,就是我们遍历到叶子节点的后面了,已经遍历到null,就直接退出这层。如果不为null,说明它有值,那就收集起来,这就是“中”,中完事了再向左遍历,就是收集这个节点的左子树,这是“左”,然后收集这个节点的右子树,就是“右”。

        于是,这道题解决了,中序和后序也就解决了。

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

方法二:迭代

        迭代法也可以遍历树,要用到我们之前学过的栈。给大家画图来解释:

二叉树

        还是以这棵树为例, 先把1压入栈,然后按照上面的逻辑,“中”->收集结果,“左”->遍历左子树,怎么遍历呢?把1弹出来,把1的左子树的头压入栈。

但是,这样的话当我们把左边遍历完了的时候,怎么遍历右边呢?哦,因为之前把头节点弹掉了,所以找不到右子树了。所以为了解决这个问题,在遍历左子树的时候,先把当前节点的右节点压入栈中,等左子树遍历完了,栈中还有一个右子树的头节点,然后再把它弹出来,遍历右子树。

 

看看这个逻辑,是不是也符合左右中呢?

    public List<Integer> preorderTraversal(TreeNode root) {

        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        stack.push(cur);
        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;

    }

3.2 二叉树的后序遍历 

145. 二叉树的后序遍历

方法一:递归

        与前序类似。

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

方法二:迭代

        与前序类似,但是这个可以动一个小脑筋。就是我们先按“中右左”的顺序压入栈,然后将收集到的结果翻转,是不是就是“左右中”->后序遍历了。

    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        stack.push(cur);
        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);
            }
        }
        reverseRes(res);
        return res;
    }
    
    private void reverseRes(List<Integer> result) {
        for (int i = 0; i < result.size() / 2; i++) {
            Integer temp = result.get(i);
            result.set(i, result.get(result.size() - i - 1));
            result.set(result.size() - i - 1, temp);
        }
    }

3.3 二叉树的中序遍历 

94. 二叉树的中序遍历

方法一:递归

        与前序、后序类似。

    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        reverse(res, root);
        return res;
    }

    public void reverse(List<Integer> res, TreeNode cur) {
        if (cur == null) {
            return;
        }
        reverse(res, cur.left);
        res.add(cur.val);
        reverse(res, cur.right);
    }

方法二:迭代

        中序的迭代遍历和前序后序还是有区别的。中序的遍历顺序是:左中右,那我们要怎么压栈、弹栈呢?首先,中序遍历的第一个元素是最左边的元素,所以只要左边不为空,就得一直向左遍历,于是就有了,如果每次向左遍历前,都把当前节点先压入栈,直到为null,说明当前节点的左边已经没有节点了,然后按照左中右的逻辑把中收集起来,再遍历右节点,如果右节点不为bull就继续按照这个逻辑遍历下去,如果为null,说明到头了,怎么办?再次进入下一轮循环,把栈里面存的节点弹出来,存起来,看看他的右边是不是null,如此循环。

        图解:

 

    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        Deque<TreeNode> deque = new LinkedList<>();
        while (root != null || !deque.isEmpty()) {
            if (root != null) {
                deque.push(root);
                root = root.left;
            } else {
                TreeNode node = deque.pop();
                res.add(node.val);
                root = node.right;
            }
        }
        return res;
    }
  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值