当前位置:   article > 正文

Java实现数据结构——二叉树_满二叉树按第一层为1,二层2,3,第三层4,5,6,7用java实现

满二叉树按第一层为1,二层2,3,第三层4,5,6,7用java实现

目录

引言

关于树的基础概念

二叉树

二叉树的性质

关于完全二叉树的编号问题

二分搜索树 (BST)及其他二叉树

二叉树的遍历问题

二叉树的基本操作

二叉树的建立,前、中、后序遍历

统计二叉树节点个数

统计叶子结点的个数

统计第k层节点个数

求二叉树的高度

判断二叉树中是否包含指定值val

借助队列,实现二叉树的层序遍历


引言

为什么会存在树结构?

树:高效的查找与搜索的语义。

企业中员工的分类就是一个“树”结构,若是一个线性结构,所有人都在一个“目录”里,比如当前公司中一共有300号员工,要找到一个特定的员工最坏情况下得找300次,O(n)。现在是个树结构,按照员工的职级进行分类,当前300个员工一共分为四级,搜索一个特定员工只需要4次即可找到,O(logn)——树的深度


操作系统OS的文件系统就是基于树结构进行文件管理的,若当前OS中所有文件都放在一个“目录”下,若当前操作系统有1亿个文件,最坏情况遍历1亿次才能找到特定元素。OS分为多级目录,我们只需要logN级别的查找次数。

关于树的基础概念

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

特点:
1. 有一个特殊的节点,称为根节点,根节点没有前驱节点。
2. 除根节点外,其余节点被分成M(M > 0)个互不相交的子集,彼此之间不能相交,若相交就不是树。每棵子树的根节点有且只有一个前驱,可以有0个或多个后继。

3. 树中,边的个数x,节点的个数n,x = n - 1(每个节点都有一个父节点,只有根节点没有父节点)。
4. 树是递归定义的。

5. 节点和树的"度”。
节点的度:该节点中包含的子树个数称为该节点的度。

树的度:树中最大的节点的度就称为树的度。

上图中

5. 叶子节点:度为0的节点,M、J、K都没有子树,都是叶子节点。

非叶子节点:度不为0的节点。还存在子树,D、I都是非叶子节点。
6. 根节点:没有父节点的结点,树中有且只有一个,D就是根节点

7. 节点的层次(高度):从根节点开始计算,根节点为第一层,l、J、K就是第二层,M就是第三层。
树的高度:当前树中节点层次最大的即为树的高度,3。


树与非树

二叉树

树中节点最大的度为2的树,称之为二叉树。

二叉树中,一个节点最多有两棵子树,二叉树节点的度<=2;子树有左右之分,左右的顺序不能颠倒。

二叉树的性质

1. 在深度为k的二叉树中,最多存在2^k-1个节点。
2. 在第k层,该层最多有2^(k-1)个节点。
3. 由于任意二叉树都满足节点个数n和边长x具备:x = n - 1的关系。
可以推出——>在二叉树中 度为0的结点 和 度为2的结点 有 n0 = n2 + 1 的关系。

推导:


满二叉树:在该二叉树中,每一层的节点个数都是最大值,每一层节点个数都是满的。满二叉树就是一颗特殊的完全二叉树

完全二叉树:

1. 完全二叉树的结点编号和满二叉树完全一致

2. 完全二叉树只可能最深的一层结点没有满,没有满的这一层,结点都是靠左排列,其余层都是满的。

3. 在完全二叉树中不存在只有右子树而没有左子树的结点。

4. 在完全二叉树中,若存在度为1的节点,这个节点必然只有左树没有右树而且这个节点有且只有个1个

关于完全二叉树的编号问题

1. 若根节点从1开始编号

若任意—个节点x,既存在子树也有父节点,则该节点的父节点编号为x / 2,
左子树的编号为2x,右子树的编号为2x + 1。

2. 若根节点从1开始编号

若任意—个节点x,既存在子树也有父节点,则该节点的父节点编号为(x - 1) / 2,
左子树的编号为2x + 1,右子树的编号为2x + 2。

二分搜索(BST)及其他二叉树

二分搜索树(BST):节点的值之间有一个大小关系,左子树节点值 < 根节点值 < 右子树节点值。

平衡二叉树:该树中任意一个节点的左右子树高度差<= 1AVL,严格平衡BST。
RBTree(红黑树):"黑节点"严格平衡的BST。

二叉树的遍历问题

二叉树的遍历问题:所有二叉树的基础操作,所有二叉树问题的解决思路都是遍历问题的衍生。

遍历:按照一定的顺序访问这个集合的所有元素,做到不重复,不遗漏。


4种遍历方式:前序遍历、中序遍历、后序遍历(深度优先遍历DFS),层序遍历(广度优先遍历BFS)。

前序遍历:先访问根节点,然后递归访问左子树,再递归访问右子树。

中序遍历:先递归访问左子树,然后访问根节点,最后递归访问右子树。

后序遍历:先递归访问左子树,然后递归访问右子树,最后访问根节点。

第3次走到根节点才能“访问”。

将后序遍历的结果集reverse得到一个先序遍历的镜像结果集——“根右左”。

层序遍历:将二叉树一层层的从上到下、从左到右访问。

二叉树的基本操作

二叉树的建立,前、中、后序遍历

  1. package bintree;
  2. /**
  3. * 二叉树的基本操作
  4. */
  5. class TreeNode<E> {
  6. //当前节点的值
  7. E val;
  8. //左子树的根
  9. TreeNode<E> left;
  10. //右子树的根
  11. TreeNode<E> right;
  12. public TreeNode(E val) {
  13. this.val = val;
  14. }
  15. }
  16. public class MyBinTree<E> {
  17. public TreeNode<Character> root;
  18. //建立一个测试二叉树
  19. public void build() {
  20. TreeNode<Character> node = new TreeNode<>('A');
  21. TreeNode<Character> node1 = new TreeNode<>('B');
  22. TreeNode<Character> node2 = new TreeNode<>('C');
  23. TreeNode<Character> node3 = new TreeNode<>('D');
  24. TreeNode<Character> node4 = new TreeNode<>('E');
  25. TreeNode<Character> node5 = new TreeNode<>('F');
  26. TreeNode<Character> node6 = new TreeNode<>('G');
  27. TreeNode<Character> node7 = new TreeNode<>('H');
  28. node.left = node1;
  29. node.right = node2;
  30. node1.left = node3;
  31. node1.right = node4;
  32. node4.left = node6;
  33. node6.right = node7;
  34. node2.right = node5;
  35. root = node;
  36. }
  37. /**
  38. * 传入一棵二叉树的根节点root,就能按照前序遍历 根左右的方式进行输出
  39. */
  40. public void preOrder(TreeNode root) {
  41. if (root == null) {
  42. return;
  43. }
  44. //根
  45. System.out.print(root.val + " ");
  46. //左子树的输出交给子函数
  47. preOrder(root.left);
  48. //右子树的输出交给子函数
  49. preOrder(root.right);
  50. }
  51. /**
  52. * 传入一棵二叉树的根节点root,就能按照中序遍历 左根右的方式进行输出
  53. */
  54. public void inOrder(TreeNode root) {
  55. if (root == null) {
  56. return;
  57. }
  58. //左子树的输出交给子函数
  59. inOrder(root.left);
  60. //打印根
  61. System.out.print(root.val + " ");
  62. //打印右子树
  63. inOrder(root.right);
  64. }
  65. /**
  66. * 传入一棵二叉树的根节点root,就能按照后序遍历 左右根的方式进行输出
  67. */
  68. public void postOrder(TreeNode root) {
  69. if (root == null) {
  70. return;
  71. }
  72. //左子树的输出交给子函数
  73. postOrder(root.left);
  74. //右子树的输出交给子函数
  75. postOrder(root.right);
  76. //根
  77. System.out.print(root.val + " ");
  78. }
  79. }
  1. package bintree;
  2. /**
  3. * 测试类
  4. */
  5. public class TreeTest {
  6. public static void main(String[] args) {
  7. MyBinTree binTree = new MyBinTree();
  8. binTree.build();
  9. System.out.println("前序遍历结果为:");
  10. binTree.preOrder(binTree.root);
  11. System.out.println();
  12. System.out.println("中序遍历结果为:");
  13. binTree.inOrder(binTree.root);
  14. System.out.println();
  15. System.out.println("后序遍历结果为:");
  16. binTree.postOrder(binTree.root);
  17. }
  18. }

统计二叉树节点个数

  1. /**
  2. * 统计二叉树节点个数
  3. * 传入一棵以root为根的二叉树,就能求出节点个数为多少
  4. */
  5. public int getNodes(TreeNode root) {
  6. //第一种方法
  7. // if (root == null) {
  8. // return 0;
  9. // }
  10. // //根节点至少存在1
  11. // //总节点数 = 根 + 左子树 +右子树
  12. // return 1 + getNodes(root.left) + getNodes(root.right);
  13. //第二种方法
  14. return root == null ? 0 : 1 + getNodes(root.left) + getNodes(root.right);
  15. }

统计叶子结点的个数

1. 递归写法

  1. /**
  2. * 传入一颗以root为根的树,就能求出叶子结点的个数
  3. */
  4. public int getLeafNodes(TreeNode root) {
  5. // 1.边界
  6. if (root == null) {
  7. return 0;
  8. }
  9. // if (root.left == null && root.right == null) {
  10. if ((root.left = root.right) == null) {
  11. //只有叶子节点
  12. return 1;
  13. }
  14. // 此时root不为空,也有子树,总叶子结点个数 = 左树中的叶子结点个数 + 右树中的叶子结点个数
  15. return getLeafNodes(root.left) + getLeafNodes(root.right);
  16. }

2. 层序遍历统计节点

  1. /**
  2. * 使用层序遍历统计一颗二叉树的结点个数
  3. */
  4. public int getNodesNonRecursion(TreeNode<E> root) {
  5. if (root == null) {
  6. return 0;
  7. }
  8. int size = 0;
  9. Deque<TreeNode<E>> queue = new LinkedList<>();
  10. queue.offer(root);
  11. while (!queue.isEmpty()) {
  12. TreeNode<E> node = queue.poll();
  13. size++;
  14. if (node.left != null) {
  15. queue.offer(node.left);
  16. }
  17. if (node.right != null) {
  18. queue.offer(node.right);
  19. }
  20. }
  21. return size;
  22. }

统计第k层节点个数

  1. /**
  2. * 传入一颗以root为根的二叉树,就能求出第k层的节点个数 k <= height(root)
  3. */
  4. public int getKLevelNodes(TreeNode<E> root, int k) {
  5. // 1.边界条件
  6. if (root == null || k <= 0) {
  7. return 0;
  8. }
  9. if (k == 1) {
  10. return 1;
  11. }
  12. // 求以root为根的第k层结点个数 = 以root.left为根的第k - 1层结点个数 + 以root.right为根的第k - 1层结点个数
  13. return getKLevelNodes(root.left, k - 1) + getKLevelNodes(root.right, k - 1);
  14. }

求二叉树的高度

  1. /**
  2. * 传入一颗以root为根的二叉树,就能求出该树的高度是多少
  3. */
  4. public int height(TreeNode<E> root) {
  5. // if (root == null) {
  6. // return 0;
  7. // }
  8. // return 1 + Math.max(height(root.left), height(root.right));
  9. return root == null ? 0 : 1 + Math.max(height(root.left), height(root.right));
  10. }

判断二叉树中是否包含指定值val

  1. /**
  2. * 判断以root为根的二叉树中是否包含指定值val
  3. */
  4. public boolean contains(TreeNode root,E val) {
  5. if (root == null) {
  6. return false;
  7. }
  8. if (root.val.equals(val)) {
  9. return true;
  10. }
  11. return contains(root.left,val)||contains(root.right,val);
  12. }

借助队列,实现二叉树的层序遍历

  1. /**
  2. * 借助队列,实现二叉树的层序遍历
  3. */
  4. public void levelOrder(TreeNode<E> root) {
  5. Deque<TreeNode<E>> queue = new LinkedList<>();
  6. queue.offer(root);
  7. //循环终止条件 队列为空
  8. while (!queue.isEmpty()) {
  9. //
  10. int n = queue.size();
  11. for (int i = 0; i < n; i++) {
  12. TreeNode<E> node = queue.poll();
  13. System.out.print(node.val + " ");
  14. if (node.left != null) {
  15. queue.offer(node.left);
  16. }
  17. if (node.right != null) {
  18. queue.offer(node.right);
  19. }
  20. }
  21. }
  22. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/513149
推荐阅读
相关标签
  

闽ICP备14008679号