数据结构和算法----树

Posted 编程是个坑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构和算法----树相关的知识,希望对你有一定的参考价值。

概念


        树(Tree)结构是一种描述非线性层次关系的数据结构,其中重要的是树的概念。树是n个数据节点的集合,在该集合中包含一个根结点,根节点之下分布着一些互不相交的子集合,这些子集合是根结点的子树。树结构的基本特征如下:

        无前驱节点者,根节点;

        除根节点外,其余节点均有且仅有一个前驱节点;

        每个节点可以有任意多个直接后继节点(子节点或兄弟节点);


        二叉树:二叉树是树结构的一种特殊形式,它是n个节点的集合,每个节点最多只能有两个子节点。二叉树的子树仍是二叉树。二叉树的子树分左右树,因此二叉树是有序树。



       满二叉树:在二叉树中除最下一层的叶节点外,每层的节点都有两个子节点。


     

        完全二叉树:与满二叉树拥有相同位置的二叉树,则为完全二叉树。

   



       二叉树的存储方式:

               顺序结构存储(一维结构数组)、

               线性结构存储(链式存储结构)、

               函数结构存储。

 


      

遍历顺序

      二叉树遍历方式:

              先序遍历(DLR)、

              中序遍历(LDR)、

              后序遍历(LRD)




自己实现一个普通的树
package cn.jdk8.test.myself.tree.binary;
/** * @author jaylee * @description: * @author: Mr.JayLee * @create: 2020-04-25 15:05 */public class TreeNode<E> { E data;//数据域 TreeNode<E> left;//左子树 TreeNode<E> right;//右子树 public TreeNode(E data) { this.data = data; left = null; right = null; } public TreeNode() { } /** * 添加左节点 * * @param value */ public TreeNode<E> addLeft(E value) { TreeNode<E> leftChild = new TreeNode<>(value); this.left = leftChild; return this; } /** * 添加右节点 * * @param value * @return */ public TreeNode<E> addRight(E value) { TreeNode<E> rightChild = new TreeNode<>(value); this.right = rightChild; return this; } @Override public boolean equals(Object obj) { if (!(obj instanceof TreeNode)) { return false; } return this.data.equals(((TreeNode<?>) obj).data); } @Override public int hashCode() { int h = this.data.hashCode(); return h ^ (h >>> 16); } @Override public String toString() { return this.data == null ? "" : this.data.toString(); } }


package cn.jdk8.test.myself.tree.binary;
import java.util.ArrayList;import java.util.LinkedList;import java.util.List;import java.util.Queue;
/** * @author jaylee * @description: * @author: Mr.JayLee * @create: 2020-04-27 13:53 */public class TreeTools<E> { /** * 获取树的节点数 * * @param root 根节点 * @param <E> * @return 树的节点数 */ public static <E> int getTreeNums(TreeNode<E> root) { if (null == root) return 0;//空树,节点为0 return getTreeNums(root.left) + getTreeNums(root.right) + 1; } /** * 获取树的深度 * * @param root 根结点 * @param <E> * @return 树的深度 */ public static <E> int getDeepth(TreeNode<E> root) { if (root == null) return 0; int leftDeepth = getDeepth(root.left) + 1; int rightDeepth = getDeepth(root.right) + 1; return Math.max(leftDeepth, rightDeepth); } /** * DLR * 前序遍历 * * @param root * @param <E> */ public static <E> void preOrderTravel(TreeNode<E> root) { if (null == root) return; vistTreeNode(root); preOrderTravel(root.left); preOrderTravel(root.right); } /** * LDR * 中序遍历 * * @param root * @param <E> */ public static <E> void midOrderTravel(TreeNode<E> root) { if (null == root) return; midOrderTravel(root.left); vistTreeNode(root); midOrderTravel(root.right); } /** * LRD * 后序遍历 * * @param root * @param <E> */ public static <E> void postOrderTravel(TreeNode<E> root) { if (null == root) return; postOrderTravel(root.left); postOrderTravel(root.right); vistTreeNode(root); } /** * 访问节点 * * @param root * @param <E> */ private static <E> void vistTreeNode(TreeNode<E> root) { System.out.println(root.data + "\t"); } /** * 层级遍历 * * @param root * @param <E> 层级遍历:从上到下,从左到右依次遍历 */ public static <E> void levelTravel(TreeNode<E> root) { Queue<TreeNode<E>> queue = new LinkedList<>();//新建一个队列 queue.offer(root);//根结点如队列 while (!queue.isEmpty()) {//队列不为空就 访问节点 出队列 TreeNode<E> tmpPoll = queue.poll(); vistTreeNode(tmpPoll); if (null != tmpPoll.left) { queue.offer(tmpPoll.left); } if (null != tmpPoll.right) { queue.offer(tmpPoll.right); } } } /** * 第K层的节点数目 * * @param root 根节点 * @param K K层 * @param <E> * @return */ public static <E> int num4kLevel(TreeNode<E> root, int K) { if (root == null || K < 1) { return 0; } if (K == 1) { return 1; } int leftNum = num4kLevel(root.left, K - 1); int rightNum = num4kLevel(root.right, K - 1); return leftNum + rightNum; } /** * getLeafNum: 求二叉树中叶子节点的个数 * * @param root * @return int 返回类型 */ public static <E> int getLeafNum(TreeNode<E> root) { if (root == null) { return 0; } if (root.left == null && root.right == null) { return 1; } int leftNum = getLeafNum(root.left); int rightNum = getLeafNum(root.right); return leftNum + rightNum; } /** * exchange: 交换根节点的左右子树 * * @param root * @return TreeNode 返回类型 */ public static <E> TreeNode<E> exchange(TreeNode<E> root) { if (root == null) { return null; } TreeNode<E> left = exchange(root.left); TreeNode<E> right = exchange(root.right); root.left = right; root.right = left; return root; } /** * nodeIsChild: 查看node是否是root的子节点 * * @param root * @param node * @return boolean 返回类型 */ public static <T> boolean nodeIsChild(TreeNode<T> root, TreeNode<T> node) { if (root == null || node == null) { return false; } if (root == node) { return true; } boolean isFind = nodeIsChild(root.left, node); if (!isFind) { isFind = nodeIsChild(root.right, node); } return isFind; } /** * findAllFatherNode: 返回两个节点lnode和rnode的以root为根节点的公共父节点 * * @param root 根节点 * @param lNode * @param rNode * @return TreeNode 返回类型 */ public static <T> TreeNode<T> findAllFatherNode(TreeNode<T> root, TreeNode<T> lNode, TreeNode<T> rNode) { if (lNode == root || rNode == root) { return root; } if (root == null || lNode == null || rNode == null) { return null; } // 如果lNode是左子树的节点 if (nodeIsChild(root.left, lNode)) { if (nodeIsChild(root.right, rNode)) { return root; } else { return findAllFatherNode(root.left, lNode, rNode); } } else { if (nodeIsChild(root.left, rNode)) { return root; } else { return findAllFatherNode(root.right, lNode, rNode); } } } /** * getTreeFromPreAndMid: 根据前序和中序构建二叉树 * * @param pre 前序序列 * @param mid 中序序列 * @return TreeNode 返回类型 */ public static <T> TreeNode<T> getTreeFromPreAndMid(List<T> pre, List<T> mid) { if (pre == null || mid == null || pre.size() == 0 || mid.size() == 0) { return null; } if (pre.size() == 1) { return new TreeNode<T>(pre.get(0)); } TreeNode<T> root = new TreeNode<T>(pre.get(0)); // 找出根节点在中序中的位置 int index = 0; while (!mid.get(index++).equals(pre.get(0))) { } // 构建左子树的前序 List<T> preLeft = new ArrayList<T>(index); // 左子树的中序 List<T> midLeft = new ArrayList<T>(index); for (int i = 1; i < index; i++) { preLeft.add(pre.get(i)); } for (int i = 0; i < index - 1; i++) { midLeft.add(mid.get(i)); } // 重建左子树 root.left = getTreeFromPreAndMid(preLeft, midLeft); // 右子树的前序 List<T> preRight = new ArrayList<T>(pre.size() - index - 1); // 右子树的中序 List<T> midRight = new ArrayList<T>(pre.size() - index - 1); for (int i = 0; i <= pre.size() - index - 1; i++) { preRight.add(pre.get(index + i)); } for (int i = 0; i <= pre.size() - index - 1; i++) { midRight.add(mid.get(index + i)); } // 重建→子树 root.right = getTreeFromPreAndMid(preRight, midRight); return root; } /** * equals: 查看node1和node2两棵树是否相等(两棵树所有节点都相等) * * @param node1 node2 两个节点 * @return boolean 返回类型 */ public static <T> boolean equals(TreeNode<T> node1, TreeNode<T> node2) { // TODO Auto-generated method stub if (node1 == null && node2 == null) { return true; } else if (node1 == null || node2 == null) { return false; } boolean isEqual = node1.data.equals(node2.data); boolean isLeftEqual = equals(node1.left, node2.left); boolean isRightEqual = equals(node1.right, node2.right); return isEqual && isLeftEqual && isRightEqual; } public static void main(String[] args) { // TODO Auto-generated method stub TreeNode<Integer> t = new TreeNode<>(1); t.addLeft(2); t.addRight(3); t.left.addLeft(4); t.left.addRight(5); System.out.println("中序遍历测试:"); TreeTools.midOrderTravel(t); System.out.println("\n前序遍历测试:"); TreeTools.preOrderTravel(t); System.out.println("\n后序遍历测试:"); TreeTools.postOrderTravel(t); System.out.println("\n层次遍历测试:"); TreeTools.levelTravel(t); System.out.println("\n树的深度:"+TreeTools.getDeepth(t)); System.out.println("树的叶子个数:"+TreeTools.getLeafNum(t)); System.out.println("树的节点个数:"+TreeTools.getTreeNums(t)); System.out.println("第2层节点个数为:"+TreeTools.num4kLevel(t,2)); List<Integer> pre = new ArrayList<>(); pre.add(1); pre.add(2); pre.add(4); pre.add(5); pre.add(3); List<Integer> mid = new ArrayList<Integer>(); mid.add(4); mid.add(2); mid.add(5); mid.add(1); mid.add(3); TreeNode<Integer> root = TreeTools.getTreeFromPreAndMid(pre, mid); System.out.println("\n通过前序和中序构建树测试:"); TreeTools.levelTravel(root); System.out.println("\n构建的树比较测试:"); System.out.println(TreeTools.equals(t,root)); }}


中序遍历测试:

4

2

5

1

3


前序遍历测试:

1

2

4

5

3


后序遍历测试:

4

5

2

3

1


层次遍历测试:

1

2

3

4

5


树的深度:3

树的叶子个数:3

树的节点个数:5

第2层节点个数为:2


通过前序和中序构建树测试:

1

2

3

4

5


构建的树比较测试:

true


Process finished with exit code 0



二叉树的特性:


1: 在非空二叉树的第n层上,最多有 2^(n-1)个节点;2: 深度为k的二叉树至多有 ( 2^k - 1 ) 个节点;3: 对任何一颗二叉树T,若其终端节点(叶子节点)数为 n0, 度为 2的节点数为n2, 则 n0 = n2 + 1.4: 具有n个节点的完全二叉树深度为 [log2(n+1)], 对以2为底,n+1 对数进行向上取整。


因为树的知识纷繁,后续择时在细化。
















以上是关于数据结构和算法----树的主要内容,如果未能解决你的问题,请参考以下文章

天天数据结构和算法PHP中trie数据结构的使用场景和代码实例

片段(Java) | 机试题+算法思路+考点+代码解析 2023

求数据结构算法平衡二叉树实现代码

通过分析 JDK 源代码研究 TreeMap 红黑树算法实

机器学习——模型树

数据结构与算法:树 堆排序