数据结构和算法----树
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;
}
public boolean equals(Object obj) {
if (!(obj instanceof TreeNode)) {
return false;
}
return this.data.equals(((TreeNode<?>) obj).data);
}
public int hashCode() {
int h = this.data.hashCode();
return h ^ (h >>> 16);
}
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数据结构的使用场景和代码实例