总结:二叉树的概念性质与实现

1.树当中的概念

(1)节点的度:一个节点含有的子树的个数称为该节点的度.
(2)树的度:一棵树中,最大的节点的度称为树的度.
(3)叶子节点或终端节点:度为0的节点称为叶节点.
(4)双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点.
(5)孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点.
(6)根结点:一棵树中,没有双亲结点的结点.
(7)节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推.
(8)树的高度或深度:树中节点的最大层次.

2.二叉树

(1)定义:一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成.
(2)特性:
a)每个结点最多有两棵子树,即二叉树不存在度大于 2 的结点.
b)二叉树的子树有左右之分,其子树的次序不能颠倒.
(3)完全二叉树:完全二叉树是效率很高的数据结构。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。满二叉树是一种特殊的完全二叉树。
(4)一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。

在这里插入图片描述

3.二叉树的性质

在这里插入图片描述
例题:一个完全二叉树中有1000个节点,请问:有 ( 500 ) 个叶子节点,( 499 )个非叶子节点,( 1 )个节点只有左孩子,( 0 )个节点只有右孩子。

4.二叉树的遍历方式

(1)先序遍历(前序)==先根遍历:先访问根节点,再递归遍历左子树,再递归遍历右子树.
(2)中序遍历:先递归遍历左子树,再访问根节点,再递归遍历右子树.
(3)后序遍历:先递归遍历左子树,再递归遍历右子树,最后访问根节点.
(4)层序遍历:按照每一层元素从左到右遍历即可.

5.用代码实现二叉树的遍历方式

(1)递归前序遍历图解:
在这里插入图片描述
(2)递归中序遍历图解:
在这里插入图片描述
(3)递归实现后序遍历图解:
在这里插入图片描述
(4)非递归实现前序遍历:
在这里插入图片描述
(5)非递归实现中序遍历:
在这里插入图片描述
(6)非递归实现后序遍历:
在这里插入图片描述
(7)层序遍历:
在这里插入图片描述
(8)判断完全二叉树:
在这里插入图片描述

import java.util.*;

/**
 * Description:二叉树的实现:核心思路要明白每次在递归的时候传入的root是不同的。
 */
class Node {
    char value;
    Node left;
    Node right;

    public Node(char value) {
        this.value = value;
    }
}

public class BinaryTree {
    public Node build() {
        Node A = new Node('A');
        Node B = new Node('B');
        Node C = new Node('C');
        Node D = new Node('D');
        Node E = new Node('E');
        Node F = new Node('F');
        Node G = new Node('G');
        Node H = new Node('H');
        A.left = B;
        B.left = D;
        B.right = E;
        E.right = H;
        A.right = C;
        C.left = F;
        C.right = G;
        return A;
    }

    // 递归实现前序遍历
    void preOrderTraversal(Node root) {
        if (root == null) {
            return;
        }
        System.out.print(root.value + " ");
        preOrderTraversal(root.left);//需要理解每一次传入的root是哪个
        preOrderTraversal(root.right);
    }
    //非递归实现前序遍历(难点)
    void preOrderTraversal2(Node root) {
        List<Character> list = new ArrayList<>();//oj
        if (root == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        //1.让cur指向root
        Node cur = root;
        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                //2.
                stack.push(cur);
                System.out.print(cur.value + " ");
                list.add(cur.value);
                cur = cur.left;
            }
            cur = stack.pop();
            cur = cur.right;
        }
//        return list;
    }

    // 中序遍历
    void inOrderTraversal(Node root) {
        if (root == null) {
            return;
        }
        inOrderTraversal(root.left);
        System.out.print(root.value + " ");
        inOrderTraversal(root.right);
    }

    //非递归实现中序遍历(难点)
    void inOrderTraversal2(Node root) {
        List<Character> list = new ArrayList<>();//oj当中写需要使用
        if (root == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        Node cur = root;
        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                //先将cur入栈,但不打印,然后cur一直往左走,直到cur.left为空的时候,
                //当前的栈顶元素就是cur,然后将cur出栈再打印,然后cur开始走right
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            System.out.print(cur.value + " ");
            cur = cur.right;
        }
    }

    // 后序遍历
    void postOrderTraversal(Node root) {
        if (root == null) {
            return;
        }
        postOrderTraversal(root.left);
        postOrderTraversal(root.right);
        System.out.print(root.value + " ");
    }

    // 非递归实现后序遍历(难点)
    void postOrderTraversal2(Node root) {
        if (root == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        Node cur = root;
        Node flg = null;//flg标记的是打印的元素
        while (cur != null || !stack.isEmpty()) {
            //节点不为空一直压栈
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            //cur==null,cur需要拿到栈顶元素看是否有右子树
            cur = stack.peek();
            if (cur.right == null || cur.right == flg) {
                System.out.print(cur.value + " ");
                stack.pop();
                flg = cur;
                cur = null;
            } else {
                cur = cur.right;
            }
        }
    }

    // 遍历思路-求结点个数
    static int size = 0;

    int getSize1(Node root) {
        if (root == null) {
            return 0;
        }
        size++;
        getSize1(root.left);
        getSize1(root.right);
        return size;
    }

    // 子问题思路-求结点个数---面试
    int getSize2(Node root) {
        if (root == null) {
            return 0;
        }
        return getSize2(root.left) + getSize2(root.right) + 1;
    }

    // 子问题思路-求叶子结点个数
    int getLeafSize2(Node root) {
        if (root == null) {
            return 0;
        }
        if (root.left == null && root.right == null) {
            return 1;
        }
        return getLeafSize2(root.left) + getLeafSize2(root.right);
    }

    // 子问题思路-求第 k 层结点个数
    int getKLevelSize(Node root, int k) {
        if (root == null || k < 1) {
            return 0;
        }
        if (k == 1) {
            // k 为 1 的时候, 就一个根节点
            return 1;
        }
        // 求第 k 层节点的个数,求左子树的第 k - 1 层节点的个数 + 右子树的 k - 1 层
        return getKLevelSize(root.left, k - 1) + getKLevelSize(root.right, k - 1);
    }

    // 查找 val 所在结点,没有找到返回 null,按照根 -> 左子树 -> 右子树的顺序进行查找
    // 一旦找到,立即返回,不需要继续在其他位置查找
    Node find(Node root, int val) {
        if (root == null) {
            return null;
        }
        if (root.value == val) {
            return root;
        }
       /* if (root.left.value == val) {
            return find(root.left, val);
        }
        if (root.right.value == val) {
            return find(root.right, val);
        }*/
        Node result = find(root.left, val);
        if (result != null) {
            return result;
        }
        Node result2 = find(root.left, val);
        if (result2 != null) {
            return result2;
        }
        return null;
    }

    // 层序遍历:核心方法是借助队列来实现
    void levelOrderTraversal(Node root) {
        // 创建一个队列辅助进行遍历
        Queue<Node> queue = new LinkedList<>();
        //1.当根节点不为空的时候,就把根节点入队。
        if (root != null) {
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            //2.当队列不为空的时候,循环取队首元素. 访问这个元素.
            Node cur = queue.peek();
            // 3. 把当前这个队首元素左子树和右子树都插入队列中.
            if (cur.left != null) {
                queue.offer(cur.left);
            }
            if (cur.right != null) {
                queue.offer(cur.right);
            }
            Node output = queue.poll();
            if (output == null) {
                return;
            }
            System.out.print(output.value + " ");
        }
    }

    // 判断一棵树是不是完全二叉树(重点)
    //核心思路:借助队列(队列先进先出)列来完成,使用层序遍历的方式.
    boolean isCompleteTree(Node root) {
        Queue<Node> queue = new LinkedList<>();
        //1.当根节点不为空的时候,就把根节点入队。
        if (root != null) {
            queue.offer(root);
        }
        //2.当队列不为空的时候,让出队的元素为cur.
        while (!queue.isEmpty()) {
            Node cur = queue.poll();
            //3.如果cur不为空,就把cur的左子树和右子树也入队
            if (cur != null) {
                queue.offer(cur.left);
                queue.offer(cur.right);
            } else {
                break;
            }
        }
       //4.遍历队列中的元素,进行判断;
       // 完全二叉树--cur在出队的时候若遇到null,即把二叉树遍历完了,就认为是完全二叉树;
       // 如果在cur遇到null之后,而null之后还有其他元素,就不是二叉树.
        while (!queue.isEmpty()) {
            Node cur = queue.poll();
            if (cur != null) {
                return false;
            }
        }
        return true;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值