树与二叉树

目录

一、树型结构

1.1 树的概念

1.2 概念

1.树的表示形式

二、二叉树

2.1 概念

2.2 两种特殊的二叉树

2.3 二叉树的性质

2.4 二叉树性质相关习题

三、二叉树的基本操作

 3.1 二叉树的前中后序遍历

3.2 获取树中结点的个数

3.3 获取叶子结点的个数

3.4 获取第k层结点的个数

3.5 树的高度

3.6 检查值为value的元素是否存在

3.7 二叉树的层序遍历

3.8 判断一棵树是不是完全二叉树


一、树型结构

1.1 树的概念

        树是一种非线性的数据结构,它是由n(n >= 0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一颗倒挂的树,它是跟朝上,叶子朝下。

特点:

        1、有一个特殊的结点,成为根结点,根结点没有前驱节点。

        2、除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1T2......Tm,其中每一个集合Ti (1 <= i <= m) 又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。

        3、树是递归定义的。

注意:树型结构中,子树不能有交集,否则就不是树型结构。

1.2 概念

结点的度:一个结点含有子树的个数称为该结点的度;如上图:A的度为6

树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为6

叶子结点或终端结点:度为0的结点称为叶结点; 如上图:BCHI...等节点为叶结点

双亲结点或父结点 :若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图: A B 的父结点
孩子结点或子结点 :一个结点含有的子树的根结点称为该结点的子结点; 如上图: B A 的孩子结点
根结点 :一棵树中,没有双亲结点的结点;如上图: A
结点的层次 :从根开始定义起,根为第 1 层,根的子结点为第 2 层,以此类推
树的高度或深度 :树中结点的最大层次; 如上图:树的高度为 4
森林 :由 m m>=0 )棵互不相交的树组成的集合称为森林

1.树的表示形式

        树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法 孩子表示法 孩子双亲表示法 孩子兄弟表示法 等等。我们这里就简单的了解其中最常用的 孩子兄弟表示法
class Node {
        int value;        //树中存储的数据
        Node left;       //左孩子的引用
        Node right;     //右孩子的引用
}       

二、二叉树

2.1 概念

        一棵二叉树是结点的一个有限集合,该集合:
                1. 或者为空
                2. 或者是由一个根节 点加上两棵别称为 左子树 右子树 的二叉树组成。

 注意:

        1、二叉树不存在度大于2的结点

        2、二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

2.2 两种特殊的二叉树

        1、满二叉树:一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一棵二叉树的层数为K,且结点总数是2^k - 1,则它就是满二叉树

        2、完全二叉树: 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n 个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从0n-1的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

2.3 二叉树的性质

1、若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有 2^i - 1(i>0)个结点

2、若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 2^k - 1(k>=0)

3、对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0n21

4、具有n个结点的完全二叉树的深度k为log2(n + 1)向上取整

5、对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i 的结点有

        若i>0 双亲序号: (i-1)/2 i=0 i 为根结点编号 ,无双亲结点
        若2i+1<n ,左孩子序号: 2i+1 ,否则无左孩子
        若2i+2<n ,右孩子序号: 2i+2 ,否则无右孩子

2.4 二叉树性质相关习题

1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )

A.不存在这样的二叉树    B.200    C.198    D.199

题解:任意一颗二叉树中,度为0的结点比度为2的结点个数多一个,所以度为0的结点(叶子结点)个数为200,本题选B


2.在具有 2n 个结点的完全二叉树中,叶子结点个数为( )

A.n    B.n+1    C.n-1    D.n/2

题解:因为二叉树不存在大于2的结点,因此设度为0的结点为n0,度为1的结点为n1,度为2的结点为n2,得2n = n0 + n1 + n2,n2 = n0  + 1,对于一个偶数结点的二叉树来说,只会有一个度为1的结点,所以n1 = 1

 故本题选A

3.一个具有767个节点的完全二叉树,其叶子节点个数为()

A.383    B.384    C.385    D.386 

题解:度为 0 的节点为 n0,度为 1 的节点为 n1,度为 2 的节点为 n2,因为是奇数个结点,所以度为1的结点个数为0,n1 = 0

故本题选B


4.一棵完全二叉树的节点数为531个,那么这棵树的高度为( )

A.11    B.10    C.8    D.12 

题解:运用上述二叉树的性质2,即531 = 2^k - 1,得532 = 2^k

当k等于9时,2^9 = 512 < 531,2^10 = 1024 > 531,满足题意,所以树的高度为10,故选B


三、二叉树的基本操作

先手动创建一个二叉树

public class MyBinaryTree {
    static class TreeNode {
        public char val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(char val) {
            this.val = val;
        }
    }

    public TreeNode root;

    public TreeNode createTree() {
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');
        A.left = B;
        B.left = D;
        B.right = E;
        E.right = H;
        A.right = C;
        C.left = F;
        C.right = G;
        root = A;
        return root;
    }
}

注意:上述代码不是创建二叉树的方式,创建二叉树后面会介绍

2. 二叉树的前中后序遍历 
遍历就是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问,访问结点所做的操作依赖于具体的应用问题(比如:打印结点内容),遍历是二叉树最重要的操作之一,是二叉树上进行其他运算的基础。

N代表根结点,L代表根结点的左子树,R代表根节点的右子树,则根据遍历根节点的先后次序有以下几种遍历方式:(三种遍历方式的区别在于打印/访问根节点的时机不同)

NLR:前序遍历:根节点---根的左子树---根的右子树 (每次访问直接打印根节点)

LNR:中序遍历:根的左子树---根结点---根的右子树 (左子树走完返回时才打印根节点)

LRN:后序遍历:根的左子树---根的右子树---根结点 (左右子树都走完返回时才打印根节点)
 

 3.1 二叉树的前中后序遍历

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

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

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

前序遍历画图演示:(打印结果ABDECF)

由这个递归展开图可以看出,这里我只画了根节点左边的遍历

3.2 获取树中结点的个数

思想:二叉树结点的个数 = 左子树结点的个数+ 右子树结点的个数 + 1

public int size(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftSize = size(root.left);
        int rightSize = size(root.right);
        return leftSize + rightSize + 1;
    }

3.3 获取叶子结点的个数

思想:叶子节点就是遍历到该节点,若该节点的左子树为空,右子树为空,则返回1

public int getLeafNodeCount(TreeNode root) {
        if(root == null) {
            return 0;
        }
        if(root.left == null && root.right == null) {
            return 1;
        }
        int leftTree = getLeafNodeCount(root.left);
        int rightTree = getLeafNodeCount(root.right);
        return leftTree + rightTree;
    }

3.4 获取第k层结点的个数

思想:求第k层节点的个数,我们可以用子问题的思想去求,分别去求它左右子树k-1层的节点个数即可

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

3.5 树的高度

思想:将此二叉树的左子树的高度与右子树的高度进行比较,较大的高度+1就是此二叉树的高度

public int getHeight(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

3.6 检查值为value的元素是否存在

思想:先递归在左子树中找,再递归在右子树中找

public TreeNode find(TreeNode root, char val) {
        if(root == null) {
            return null;
        }
        if(root.val == val) {
            return root;
        }
        TreeNode leftTree = find(root.left, val);
        if(leftTree != null) {
            return leftTree;
        }
        TreeNode rightTree = find(root.right, val);
        if(rightTree != null) {
            return rightTree;
        }
        return null;
    }

3.7 二叉树的层序遍历

思想:采用非递归的方式,思路是这样的,定义一个队列,先将根节点入队,如果队列不为空,弹出一个队头元素并打印,接着再去看看它左树和右树的根节点是否为空,如果不会空都入队,重复上述操作,当队列为空时,层序遍历结束。

public void levelOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            System.out.print(cur.val + " ");
            if(cur.left != null) {
                queue.offer(cur.left);
            }
            if(cur.right != null) {
                queue.offer(cur.right);
            }
        }
    }

3.8 判断一棵树是不是完全二叉树

利用完全二叉树的性质, 如果中间下标位置的结点有空缺, 说明不是完全二叉树

所以对这棵树进行层序遍历, 利用队列这种数据结构, 根节点入队, 当队列不为空时进入循环, 出队一次, 并让它的左右孩子结点入队(队列可以offer(null), 优先级队列不可以), 出队时遇见 null, 退出循环

退出循环后检查队列中剩余数据是否还有非空数据, 如果存在非空数据说明不是完全二叉树
 

public boolean isCompleteTree(TreeNode root) {
        if(root == null) {
            return true;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            if(cur == null) {
                break;
            }
            queue.offer(cur.left);
            queue.offer(cur.right);
        }
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            if(cur != null) {
                return false;
            }
        }
        return true;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值