数据结构 二叉树(Java)

目录

获得一些二叉树的信息

获得节点数量

获得叶子节点的数量

获得第k层节点的个数

获得高度

找某个值

判断一颗二叉树是不是完全二叉树

二叉树的前中后序遍历(递归实现)

二叉树的前中后序遍历(非递归实现)

层序遍历


树是一种非线性的数据结构

二叉树的节点中储存值,左节点和右节点,通过节点来构成一颗完整的二叉树

示例:

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

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

获得一些二叉树的信息

获得节点数量

左子树的节点+右子树的节点+本身

获得叶子节点的数量

左右子树都为null时,叶子节点加1

获得第k层节点的个数

(假设从第0层开始)

public int getLevelNodeCount(TreeNode root,int k){
    if(root == null) return 0;
    if (k == 0) return 1;
    return getLevelNodeCount(root.left, k - 1) + getLevelNodeCount(root.right, k - 1);

}

获得高度

获得左右子树的max+1即可

找某个值

    TreeNode find(TreeNode root, int val) {
        if(root == null)return null;
        if(root.val == val)return root;
        if(find(root.left,val) != null)
            return find(root.left,val);
        if(find(root.right,val) != null)
            return find(root.right,val);

        return null;
    }

判断一颗二叉树是不是完全二叉树

方法一:通过前序遍历把每个叶子节点的高度存起来,比较高度是否合理

    int myfalse;
    public void preOrderX(TreeNode root) {
        if(root == null) {
            return;
        }
        height ++;
        //找叶子节点
        if(root.left == null && root.right == null){
            leafnodeheight.add(height);
        }
        if(root.left == null && root.right != null){
            myfalse++;
        }
        preOrderX(root.left);
        preOrderX(root.right);
        height --;

    }
    boolean isCompleteTree(TreeNode root) {
        if(root == null)return true;
        preOrderX(root);
        if(myfalse != 0)return false;
        int t1,t2;
        if(!leafnodeheight.isEmpty())
            t1 = leafnodeheight.poll();
        else return true;
        while(!leafnodeheight.isEmpty()){
            t2 = leafnodeheight.poll();
            if(t2 > t1)return false;
            if(t1 - t2 > 1)return false;
            if(t1 - t2 == 1){
                t1 = t2;
                while(!leafnodeheight.isEmpty()){
                    t2 = leafnodeheight.poll();
                    if(t1 != t2) return false;
                }
            }
        }
        return true;
    }

方法二(推荐):通过层序遍历思路,若前面有空节点,后面又有非空节点,就为false,否则为true

    public boolean isCompleteTree2(TreeNode root) {
        if (root == null) return true;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        boolean end = false; // 标记是否遇到了空节点

        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();

            if (node == null) {
                end = true; // 遇到空节点
            } else {
                if (end) return false; // 前面有空节点,后面又出现非空节点
                queue.offer(node.left);
                queue.offer(node.right);
            }
        }
        return true;
    }

二叉树的前中后序遍历(递归实现)

这里用打印的方式示例来模拟遍历

前序遍历

void preOrder(TreeNode root) {
        if(root == null) return;
        System.out.print(root.val + " ");
        preOrder(root.left);
        preOrder(root.right);
}

中序遍历

void inOrder(TreeNode root) {
        if(root == null) return;
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }

后序遍历

void inOrder(TreeNode root) {
        if(root == null) return;
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }

可见,前中后序遍历只是改变了方法调用的顺序,实现思路比较类似,都是先判断是否为空,如果为空,就返回,否则进行相应的取值和调用操作

二叉树的前中后序遍历(非递归实现)

前序遍历

我们需要一个栈来帮助我们实现遍历,先定义一个顺序表来储存遍历的结果

先判断有没有根节点,没有就返回,否则把根节点加入栈,若栈中没有元素,就向里面添加元素,若有,就弹出此元素,并将它的右节点和左节点加入栈,这样,下次就能弹出依次左节点和右节点,若左节点有孩子节点,则继续向下进行前序遍历,如图所示,先把a加入栈,弹出a,加入c和b,弹出b,加入e和d,弹出d,弹出e,弹出c,加入f,弹出f,就完成了前序遍历,示例代码如下

    ArrayList<Integer> preResult = new ArrayList<>();
    ArrayList<Integer> preOrder(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        if(root == null) return preResult;
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode cur = stack.pop();
            preResult.add(cur.val);
            if(cur.right != null)
                stack.push(cur.right);
            if(cur.left != null)
                stack.push(cur.left);

        }
        return preResult;
    }

后序遍历

由于前序遍历是根左右,我们可以调整为根右左,再逆转序列就是左右根,也就完成了后序遍历,相较于前序遍历,只需改变左右的顺序并添加加一个逆转的环节。

    ArrayList<Integer> postOrder(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        if(root == null) return postResult;
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode cur = stack.pop();
            postResult.add(cur.val);
            if(cur.left != null)
                stack.push(cur.left);
            if(cur.right != null)
                stack.push(cur.right);
        }
        Collections.reverse(postResult);
        return postResult;
    }

中序遍历

中序遍历迭代实现和前序后序逻辑不同,因此不能简单的通过更换部分代码顺序来实现。

我们用栈来实现这个过程

令当前值cur为根节点a,循环条件为cur不为空或者stack不为空(这表示该二叉树没有遍历完),如果当前值不为空,就加入到stack中,所以加入a,然后让cur赋值它的左节点,所以可以继续加入b和d。cur赋值为d的左节点时就为空了,这时候令cur为栈顶元素d,弹出d,将栈顶元素d加入到结果里面,然后让cur赋值它的右节点,也为空,再次令cur为栈顶元素b,弹出b,将b加入到结果里面,然后让cur赋值它的右节点e,将e入栈,e的左为空,弹出e,cur赋值为e的右,也为空,弹出a,cur赋值为a的右,为c,c入栈,cur赋值为c的左f,f入栈,cur赋值为f的左,为空,弹出f,cur赋值为f的右,为空,弹出c,此时栈为空且cur为空,结束循环,弹出顺序就是中序遍历,代码如下:

ArrayList<Integer> inOrder(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        if(root == null)return inResult;
        TreeNode cur = root;
        while(!stack.isEmpty() || cur != null){
            if(cur != null){
                stack.push(cur);
                cur = cur.left;
            }else{
                cur = stack.pop();
                inResult.add(cur.val);
                cur = cur.right;
            }
        }
        return inResult;
    }

层序遍历

先将根加入队列,然后出队,并将出队的元素输出,加入其左右节点(若不为空),此时输出的就是层序遍历的顺序,例:

    Queue<TreeNode> queue = new LinkedList<>();

    void levelOrder(TreeNode root) {
        if(root == null) return;
        queue.add(root);
        while(!queue.isEmpty()){


            TreeNode t = queue.poll();
            System.out.print(t.val + " ");
            if(t.left != null)
                queue.add(t.left);
            if(t.right != null)
                queue.add(t.right);
        }

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值