阅读量:0
树论基础&二叉树
1 什么是树
概念
数据结构中的“树”(Tree)是一种非常重要的非线性数据结构,它模拟了具有树状结构特点的数据集合。在树形结构中,每个元素被称为一个节点(Node),节点之间通过特定的关系连接,这些关系通常被描述为“父子”关系。
树是由n(n≥0)个有限节点组成一个具有层次关系的集合。它有一个特殊的节点,称为根节点(Root Node),其余节点分成m(m≥0)个互不相交的有限集T1, T2, …, Tm,其中每一个子集Ti(1≤i≤m)又是一棵符合本定义的树,称为根的子树(Subtree)。
特性
- 层次性:树中的节点按照层次组织,根节点位于最顶层,叶子节点位于最底层。
- 无环性:在树中,除了根节点外,每个节点有且仅有一个父节点;没有节点是其自身的祖先或后代。
- 子树不相交性:任意两个子树的节点之间没有交集。
2 重要术语
- 节点(Node)
- 树的每个元素都是一个节点。
- 节点可能包含一个值或一组值,以及指向其子节点的链接。
- 根节点(Root Node)
- 树中唯一没有父节点的节点。
- 它通常是树的入口点。
- 子节点(Child Node)
- 一个节点可以有零个或多个子节点。
- 子节点通过边(Edge)与父节点相连。
- 父节点(Parent Node)
- 子节点的上一级节点。
- 除了根节点外,每个节点都有一个父节点。
- 兄弟节点(Sibling Nodes)
- 具有相同父节点的节点。
- 叶子节点(Leaf Node)
- 没有子节点的节点。
- 它们位于树的最低层。
- 度(Degree)
- 一个节点的子节点数。
- 叶子节点的度为0。
- 示例:在二叉树中,每个节点最多有两个子节点(一个左子节点和一个右子节点),因此二叉树中节点的度最大为2。但在一般的树(也称为N叉树)中,节点的度可以大于2,具体取决于树的定义和上下文。
- 树的度(Degree of a Tree)
- 树中所有节点的度的最大值。
- 层次(Level)
- 从根节点开始,根节点为第1层,它的子节点为第2层,依此类推。
- 深度(Depth)或高度(Height)
- 树中节点的最大层次数。
- 根节点的深度为0,其直接子节点的深度为1,依此类推。
- 路径(Path)
- 从一个节点到另一个节点所经过的节点序列。
- 路径长度是路径上的节点数减一。
- 森林(Forest)
- 一个或多个互不相交的树的集合。
- 结点的高度:结点到叶子结点的最长路径
- 结点的深度:根结点到该结点的边个数
- 结点的层数:结点的深度加1
- 树的高度:根结点的高度
3 不同的树
- 二叉树:每个节点最多有两个子节点的树。
- 每个节点最多有两个子节点,通常称为左子节点和右子节点。
- 子树之间不能相交。
- 除了根节点外,每个节点有且仅有一个父节点。
- 一颗N个结点的树有N-1条边。
- 一个节点的子树的个数称为节点的度,普通二叉树中节点的度最大为2。
- 二叉搜索树(BST):二叉树的一种特殊形式,其中对于树中的每个节点,其左子树上所有节点的值都小于它的值,而右子树上所有节点的值都大于它的值。
- 平衡二叉树:一种特殊的二叉搜索树,其中任意节点的两个子树的高度差最多为1。常见的平衡二叉树有AVL树和红黑树。
- 完全二叉树:除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干节点。
- 编号性质:对于深度为h的完全二叉树,它的节点从1到2^h-1依次编号。其中,对于任意节点i,如果它的编号为i,则它的左子节点编号为2i,右子节点编号为2i+1。
- 父子关系:对于任意节点i,如果它的编号为i,则它的父节点编号为i/2(向下取整)。
- 高度:完全二叉树的高度为log(n+1)(以2为底),其中n为节点数。
- 存储方式:完全二叉树可以使用数组来存储,从而实现快速的访问和修改。
- 满二叉树:除叶子结点外,每个结点都有左右两个子结点。
- 每一层的节点数都达到了最大值。
- 节点要么是叶子节点(没有子节点),要么是度为2的节点(有两个子节点)。
- N叉树:每个节点可以有N个子节点的树,其中N是一个大于1的整数。
- B树和B+树:用于数据库和文件系统的平衡多路搜索树,它们允许每个节点有多个子节点和关键字,用于高效地插入、删除和搜索数据。
- 堆(Heap):一种特殊的树形数据结构,通常用于实现优先队列。堆是一种完全二叉树,它满足堆的性质:父节点的值总是大于或等于(大顶堆)或小于或等于(小顶堆)其子节点的值。
4 二叉树的遍历
4.1 前序遍历
前序遍历顺序:根节点、左节点、右节点
/** * 前序遍历 * @param node */ public void preOrder(MyBinaryTreeNode<T> node){ print(node); if (node.left != null){ preOrder(node.left); } if (node.right != null){ preOrder(node.right); } }
4.2 中序遍历
中序遍历顺序:左节点、跟节点、右节点
/** * 中序遍历 */ public void middleOrder(MyBinaryTreeNode<T> node){ if (node == null){ return; } if (node.left != null){ middleOrder(node.left); } print(node); if (node.right != null){ middleOrder(node.right); } }
4.3 后续遍历
后续遍历顺序:左节点、右节点、根节点
/** * 后续遍历 */ public void postOrder(MyBinaryTreeNode<T> node){ if (node == null){ return; } if (node.left != null){ postOrder(node.left); } if (node.right != null){ postOrder(node.right); } print(node); }
4.4 层次遍历
层次遍历:也叫横向遍历
/** * 层级遍历 */ public void transverseSort(MyBinaryTreeNode<T> node){ if (node == null){ return; } print(node); ArrayQueue<MyBinaryTreeNode<T>> queue = new ArrayQueue<>(10); queue.add(node); while (!queue.isEmpty()){ MyBinaryTreeNode<T> remove = queue.remove(0); if (remove.left != null){ print(remove.left); queue.add(remove.left); } if (remove.right != null){ print(remove.right); queue.add(remove.right); } } }
4.5 完整代码
package cn.zxc.demo.leetcode_demo.advanced_data_structure; import com.sun.jmx.remote.internal.ArrayQueue; /** * 二叉树 * A * / \ * B E * \ \ * C F * / / * D G * / \ * H K * 前序遍历: * A B C D E F G H K * 中序遍历: * B D C A E H G K F * 后序遍历: * D C B H K G F E A * 层级遍历: * A B E C F D G H K */ public class MyBinaryTreeDemo<T> { /** * 前序遍历 * @param node */ public void preOrder(MyBinaryTreeNode<T> node){ print(node); if (node.left != null){ preOrder(node.left); } if (node.right != null){ preOrder(node.right); } } /** * 中序遍历 */ public void middleOrder(MyBinaryTreeNode<T> node){ if (node == null){ return; } if (node.left != null){ middleOrder(node.left); } print(node); if (node.right != null){ middleOrder(node.right); } } /** * 后续遍历 */ public void postOrder(MyBinaryTreeNode<T> node){ if (node == null){ return; } if (node.left != null){ postOrder(node.left); } if (node.right != null){ postOrder(node.right); } print(node); } /** * 层级遍历 */ public void transverseSort(MyBinaryTreeNode<T> node){ if (node == null){ return; } print(node); ArrayQueue<MyBinaryTreeNode<T>> queue = new ArrayQueue<>(10); queue.add(node); while (!queue.isEmpty()){ MyBinaryTreeNode<T> remove = queue.remove(0); if (remove.left != null){ print(remove.left); queue.add(remove.left); } if (remove.right != null){ print(remove.right); queue.add(remove.right); } } } /** * 打印 * @param node */ public void print(MyBinaryTreeNode<T> node){ if(node == null){ return; } System.out.print(node.data + " "); } public static void main(String[] args) { MyBinaryTreeNode<String> D = new MyBinaryTreeNode<String>("D", null, null); MyBinaryTreeNode<String> H = new MyBinaryTreeNode<String>("H", null, null); MyBinaryTreeNode<String> K = new MyBinaryTreeNode<String>("K", null, null); MyBinaryTreeNode<String> C = new MyBinaryTreeNode<String>("C", D, null); MyBinaryTreeNode<String> G = new MyBinaryTreeNode<String>("G", H, K); MyBinaryTreeNode<String> B = new MyBinaryTreeNode<String>("B", null, C); MyBinaryTreeNode<String> F = new MyBinaryTreeNode<String>("F", G, null); MyBinaryTreeNode<String> E = new MyBinaryTreeNode<String>("E", null, F); MyBinaryTreeNode<String> A = new MyBinaryTreeNode<String>("A", B, E); MyBinaryTreeDemo<String> myBinaryTreeDemo = new MyBinaryTreeDemo<>(); System.out.println("前序遍历:"); myBinaryTreeDemo.preOrder(A); // A B C D E F G H K System.out.println(); System.out.println("中序遍历:"); // B D C A E H G K F myBinaryTreeDemo.middleOrder(A); System.out.println(); System.out.println("后序遍历:"); // D C B H K G F E A myBinaryTreeDemo.postOrder(A); System.out.println(); System.out.println("层级遍历:"); myBinaryTreeDemo.transverseSort(A); System.out.println(); } } class MyBinaryTreeNode<T> { T data; MyBinaryTreeNode<T> left; MyBinaryTreeNode<T> right; public MyBinaryTreeNode(T data) { this.data = data; } public MyBinaryTreeNode(T data, MyBinaryTreeNode<T> left, MyBinaryTreeNode<T> right) { this.data = data; this.left = left; this.right = right; } public T getData() { return data; } public void setData(T data) { this.data = data; } public MyBinaryTreeNode<T> getLeft() { return left; } public void setLeft(MyBinaryTreeNode<T> left) { this.left = left; } public MyBinaryTreeNode<T> getRight() { return right; } public void setRight(MyBinaryTreeNode<T> right) { this.right = right; } }