您的位置:首页 > 财经 > 金融 > Java-数据结构-二叉树-基础 (o゚▽゚)o

Java-数据结构-二叉树-基础 (o゚▽゚)o

2024/12/23 11:17:17 来源:https://blog.csdn.net/2303_80388948/article/details/142106093  浏览:    关键词:Java-数据结构-二叉树-基础 (o゚▽゚)o

文本目录:

❄️一、树形结构:

   ▶ 1、概念:

▶ 2、特殊的概念:

 ▶ 3、树的表示形式:

❄️二、二叉树:

   ▶ 1、概念:

   ▶ 2、两种特殊的二叉树:

          ➷ 1)、满二叉树:

           ➷ 2)、完全二叉树:

  ▶ 3、二叉树的性质:

       ➷ 1)性质一:

        ➷ 2)性质二:

         ➷ 3)性质三:

 ​编辑

         ➷ 4)性质四:

          ➷ 5)性质五:

   ▶ 4、二叉树的存储:

    ▶ 5、二叉树的基本操作:

        ➷ 1)、二叉树的创建:

 ➷ 2)、二叉树的遍历:

           •(1)、前中后序遍历:

             •(2)、层序遍历:

  ➷ 3)、二叉树的基本操作:

            •(1)、size(Node root):

             •(2)、getLeafNodeCount(Node root):

              •(3)、getKLevelNodeCount(Node root,int k):

               •(4)、getHeight(Node root):

                •(5)、find(Node root,char val):

                 •(6)、isCompleteTree(Node root):

❄️总结:


❄️一、树形结构:

     在我们介绍二叉树之前呢,我们先来了解一下什么是树形结构。

    1、概念:

       树是一种 非线性的数据结构 ,其是由 n 个有限节点组成的一个具有层次关系的集合。其结构呢就像是一个倒挂着的一颗树,所以我们称之为树。

      特点:1、有一个特殊的节点,称为根节点,并且根节点没有前驱节点。

                 2、除了根节点外,其余节点被分为 M 个互不相交的集合,其中呢每一个集合都是一颗 与树相似的子树,每个子树的根节点有且只有一个前驱,其中可以有0个或者多个后继结点。

                 3、 我们的树是递归实现的。

                 4、一颗有N个节点的树,有N - 1 条边。

这里要注意:树形结构中呢,我们的子树之间不能有交集,否则就不是树形结构了。


 2、特殊的概念:

 我们先来看个树形结构的图片: 


☑ 结点的度:一个结点含有子树的个数称之为该结点的度。比如:上图中 A 的度为 6 。

☑ 树的度:一棵树中,所有 结点的度 的最大值称之为 树的度。比如:我们上图的树的度为 6 

☑ 叶子结点或终端结点:度为 0 的结点称之为叶子结点。比如:我们上图的B、C、H、I、K....等

☑ 双亲结点或父结点:若一个结点含有子结点,则这个节点称之为其子结点的父结点。比如:我                                       们上图的 A 是 B 的父结点。

☑ 孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点比如:B是A的孩子结                                      点

☑ 根结点:一棵树中没有双亲结点的结点比如:A结点

☑ 结点的层次:从根结点开始定义起,根为第一层1,根的子结点为第2层,以此类推。

☑ 树的高度或者深度:树中结点的最大层次比如:上图中树的高度为 4


接下来的概念呢,我们只需要了解即可:

☒ 非终端结点或分支结点:度不为 0 的结点比如:A、D、E、F、G....等

☒ 兄弟结点:具有相同父结点的结点互相称之为兄弟结点比如:B、C就是兄弟结点

堂兄弟结点:双亲在同一层的结点互为堂兄弟比如:H、I 就是堂兄弟

结点的祖先:从根到该结点所经分支上的所有结点比如:Q 的祖先就是J、E、A

子孙:以结点为根的子树中任意结点都称之为该结点的子孙比如:所有结点都是A的子孙

森林:由m 棵互不相交的树组成的就是森林


  3、树的表示形式:

        树结构呢,相对于线性结构呢就比较复杂了,我们存储表示起来就比较麻烦了。我们对于树有很多的表示形式,比如:双亲表示法、孩子表示法、孩子双亲表示法、孩子兄弟表示法。

        我们来了解一下我们最常用的表示方法:孩子兄弟表示法

class Node {int val;Node firstChild;//第一个孩子引用Node nextBrother;//下一个兄弟引用
}

 

 这个呢就是我们的孩子兄弟表示法。


❄️二、二叉树:

   ▶ 1、概念:

       一颗二叉树是结点的一个有限集合,这个集合呢:

1、或者为空

2、或者是由一颗根结点加上两个子树,分别为 左子树 和 右子树 所构成的。

我们来看看二叉树的图片: 

从上面的图片可知:1、二叉树不存在结点大于2的情况。

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

 注意:我们二叉树可能出现几种情况:


   2、两种特殊的二叉树:

          1)、满二叉树:

         一颗二叉树,如果每层的结点数都达到最大值,则这颗二叉树就是满二叉树。

就是 如果一颗二叉树的结点为 k,并且结点总数为 2^k - 1 个结点,则其就是满二叉树


            2)、完全二叉树:

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

        这样看文字可能不是很看懂,我们来看图片:
完全二叉树的存放顺序为:从上到下,从左到右,依次存放。


  3、二叉树的性质:

       ➷ 1)性质一:

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


        ➷ 2)性质二:

     若规定只有根结点的二叉树的深度为1,则深度为 k 的二叉树的最大结点数为 2^k - 1


         ➷ 3)性质三:

对于任意的一颗二叉树,如果其叶子结点个数为n0,度为2的非叶子结点数为n2,则有n0 = n2 + 1

 


         ➷ 4)性质四:

   具有 n 个结点的 完全二叉树 的 深度k 为 log(n+1) 向上取整,这里的 log是以2为底的

 


          ➷ 5)性质五:

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

          ☞  若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点

          ☞  若2i+1<n,左孩子序号:2i+1,否则无左孩子

          ☞  若2i+2<n,右孩子序号:2i+2,否则无右孩子


   4、二叉树的存储:

   二叉树的存储解构呢,分为:顺序存储链式存储

我们先来看 链式存储:是通过一个一个结点引用起来的,我们常见的有二叉三叉表示形式。

//二叉
class Node {int val;//数据域Node left;//左孩子引用Node right;//右孩子引用
}

//三叉
class Node {int val;//数据域Node left;//左孩子引用Node right;//右孩子引用Node parent;//当前结点的根结点
}

    5、二叉树的基本操作:

        ➷ 1)、二叉树的创建:

     我们这里呢只是进行简单的创建,手动的进行创建一个二叉树,之后我们再来写其操作,等到后面我们再来进行二叉树的真正的创建方法。

我们来手动创建这个二叉树,我们使用二叉的方式来进行创建。 

public class BinaryTree {static class TreeNode {public char val;public TreeNode right;public TreeNode left;public TreeNode(char val) {this.val = val;}}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');A.left = B;A.right = C;B.left = D;B.right = E;C.left = F;C.right = G;return A;}
}

      这样我们就把这个二叉树创建成功了,注意这里的二叉树创建不是真正的二叉树的创建。我们接下来看看二叉树的操作:


 ➷ 2)、二叉树的遍历:

    对于我们的二叉树操作呢,最简单的就是遍历操作了,所谓的遍历:就是沿着某条搜索路线,依次对树中每个结点均做一次访问且只做一次访问

           •(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 + " ");}

我们来看这些遍历的运行结果,我们遍历的就是我们上面创建的二叉树。


             •(2)、层序遍历:

     我们除了前中后序遍历呢,我们还有层序遍历,就是先访问第一层的根结点,之后从上到下,从左到右依次访问结点,就是层序遍历。

比如我们创建的这个二叉树的层序遍历为:

A->B->C->D->E->F->G。 

这里我们层序遍历,我们需要使用队列来配合实现,那么它是怎么做到的呢?我们来看看:

 

  来看看代码:

//层序遍历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)、二叉树的基本操作:

            •(1)、size(Node root):

         获得树中结点的个数。

我们有两种方法,我们第一种就是遍历二叉树,当 root 不为空的时候呢,我们的 usedSize 就++ 

第二种方法就是我们的 左子树结点+右子树结点+1,这个方法来求结点的个数。

我们来看第一种方法的代码:

//返回总结点的个数public static int usedSize;//我们用于存放结点个数的public int size1(TreeNode root) {if (root == null) {return 0;}usedSize++;size1(root.left);//遍历左子树size1(root.right);//遍历右子树return usedSize;}

    在我们第一种方法中呢,我们的 usedSize 必须在方法的外面定义,因为要是在方法中的话呢,每次我们递归后,我们之前存放的结点的个数就不会保存了,所以我们要定义在外面。


我们来看看第二种方法:

代码:

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

             •(2)、getLeafNodeCount(Node root):

 获得叶子结点的个数。

     我们这个同样可以使用两种方法来进行获得叶子结点的个数。第一种:使用 leafSize 来计数,当遇到左子树和右子树都为空的结点就是叶子结点

    第二种:左子树的叶子+右子树的叶子。

我们的这个方法和上面的方法是差不多的,所以这里我们直接看代码。

第一种:

//获得叶子结点的个数//第一种public static int leafSize;public int getLeafNodeCount1(TreeNode root) {if (root == null) {return 0;}if (root.left == null && root.right == null) {leafSize++;}getLeafNodeCount1(root.left);getLeafNodeCount1(root.right);return leafSize;}

第二种: 

//第二种public int getLeafNodeCount2(TreeNode root) {if (root == null) {return 0;}if (root.left == null && root.right == null) {return 1;}return getLeafNodeCount2(root.left) + getLeafNodeCount2(root.right);}

              •(3)、getKLevelNodeCount(Node root,int k):

获得第k层的结点的个数。

   我们求第k层的结点数呢,我们求  root的左子树的k-1层的结点 + root的右子树的k-1层的结点

这样求出来的就是我们的第k层结点数。我们来看看思路图: 

我们来看代码的实现: 

//获得第k层的结点的个数。public int getKLevelNodeCount(TreeNode root,int k) {if (root == null) {return 0;}if (k == 1) {return 1;}return getKLevelNodeCount(root.left,k-1) + getKLevelNodeCount(root.right,k-1);}

               •(4)、getHeight(Node root):

获得 二叉树的高度。

     我们需要求 左树的高度 和 右子树的高度的最大值 + 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;}

                •(5)、find(Node root,char val):

查看二叉树中有没有val这个值的结点。

这个方法呢,就是我们需要遍历一遍二叉树就可以了。

    我们需要先判断我们的根是不是,如果不是,就去左子树中找,如果还没有,就从右子树中找,如果这里面都没有的话,那么就返回null。

//检测值为value的元素是否存在public TreeNode find(TreeNode root,char val) {if (root == null) {return null;}//先判断根结点if (root.val == val) {return root;}//如果不是就去左子树中寻找TreeNode leftNode = find(root.left,val);if (leftNode != null) {//如果我们在左子树中找到的话,我们在上面根的判断就返回了我们和val相等的结点,//在这里我们只需要判断是否为空就可以了,不为空,就是val的这个结点,反之则没有return leftNode;}//去右子树中寻找TreeNode rightNode = find(root.right,val);if (rightNode != null) {//我们这里同上return rightNode;}return null;}

                 •(6)、isCompleteTree(Node root):

判断该二叉树是否是完全二叉树。

     这个方法呢,我们也需要借助队列来实现,这个步骤和我们的层序遍历是差不多的,但是如果我们队列中两个结点中存在 null 的话,那么就不是完全二叉树。因为完全二叉树是从上到下从左到右依次存放,两个相邻的结点中间不可能会存在 null 。我们利用这个性质就可以判断出是否是完全二叉树了。我们来看思路图:

这里我们要注意的是,当我们的root为空的时候,也是完全二叉树。

我们来看代码:

//判断一棵树是不是完全二叉树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) {queue.offer(cur.left);queue.offer(cur.right);}else {break;}}while(!queue.isEmpty()) {TreeNode peek = queue.peek();if (peek != null) {return false;}queue.poll();}return true;}

❄️总结:

   OK,到这里呢,我们的二叉树基础就到这里就结束了,我们接下来看看关于二叉树的相关的习题,并且在题中我们会看见我们 二叉树的创建方法。让我们期待下次的博客吧!!!拜拜啦~~~

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com