一、树型结构
树形结构是一种重要的数据结构,它类似于现实生活中的树的结构,由结点和边构成。树形结构具有以下特点:
- 树形结构是一种层次化的结构,由根结点、内部结点和叶子结点组成。
- 根结点是树的顶部结点,没有父结点,所有其他结点都是它的后代。
- 内部结点是除根结点外的其他非叶子结点,它们至少有一个子结点。
- 叶子结点是没有子结点的结点,位于树的底部。
- 子树之间不能有交集,否则就不是树形结构
- 结点之间通过边相连,表示结点之间的关系。
树形结构常用于表示层次关系、分类结构、组织结构等。在计算机科学中,树形结构也广泛应用于数据存储、算法设计等方面,例如二叉树、二叉搜索树、堆等都是基于树形结构设计的数据结构。树形结构的灵活性和高效性使其成为计算机科学中一种非常重要的数据结构。
二、二叉树
1、二叉树的定义
二叉树是一种树状数据结构,其中每个节点最多有两个子结点,通常称为左子结点和右子结点。二叉树具有以下特点:
- 每个结点最多有两个子结点,分别为左子结点和右子结点。
- 左子结点和右子结点可以为空,也可以是叶子结点(没有子节点的结点)。
- 二叉树具有递归的定义,即每个结点的左子树和右子树都是二叉树。
二叉树的结点包括一个数据域和指向左子结点和右子结点的指针域。在实际应用中,二叉树常用于表示层次关系、树状结构等。
2、二叉树的特性
- 二叉树不存在子结点数量大于 2 的情况
- 二叉树的子结点有左右之分,次序不能颠倒,因此二叉树是有序树
- 左子树和右子树也是二叉树,它们可以为空。
- 二叉树的结点之间通过边相连,且不存在环路。
- 二叉树的深度(或高度)是从根节点到叶子节点的最长路径上的节点数。
3、两种特殊的二叉树
满二叉树:一棵二叉树,如果每层的结点数都达到最大值(层数为k,结点总数是),那么这棵二叉树就是满二叉树
完全二叉树:完全二叉树是效率很高的数据结构,是由满二叉树而引出来的。
对于深度为 k 的,有 n 个结点的二叉树,当且仅当每一个节点都与深度为 k 的满二叉树中编号从 0 至 n-1 的结点一一对应时称之为完全二叉树
满二叉树是一种特殊的完全二叉树
4、二叉树的存储
孩子表示法
class Node {
int val; //树中存储的数据
Node left; //左子结点,可以代表左子树为根的整棵左子树
Node right; //右子结点,可以代表右子树为根的整棵右子树
}
孩子双亲表达法
class Node {
int val; //树中存储的数据
Node left; //左子结点,可以代表左子树为根的整棵左子树
Node right; //右子结点,可以代表右子树为根的整棵右子树
Node parent; //当前结点的根结点
}
还有许多表达法,这里不再一一介绍
5、创建一棵树
class Node {
public String val; //树中存储的数据
public Node left; //左子结点,可以代表左子树为根的整棵左子树
public Node right; //右子结点,可以代表右子树为根的整棵右子树
public Node(String val){
this.val = val;
this.left = null;
this.right = null;
}
}
//可以通过一个根结点拿到整个树
//还可以通过创建一个新的类,在类中持有上述的 Node root
class Tree{
public Node root;
}
public class text1 {
public static Node creatTree(){
//创建出所有的节点
Node a = new Node("A");
Node b = new Node("B");
Node c = new Node("C");
Node d = new Node("D");
Node e = new Node("E");
Node f = new Node("F");
Node g = new Node("G");
//把这些节点连起来
a.left = b;
a.right = c;
b.left = d;
b.right = e;
c.right = f;
e.left = g;
//返回根结点
return a;
}
public static void main(String[] args) {
//第一种写法,在笔试面试中比较常见
Node root = new Node("A");
//第二钟写法,在工作中比较常见
Tree tree = new Tree();
}
}
6、树的遍历
二叉树的本身就是递归定义的结构,所以二叉树的遍历也是基于递归,有四种的遍历方法,分别是 先序 、中序 、后序 、层序。接下来分别演示对下图二叉树的不同遍历
先序(第一个是根节点)
//先序
public static void preOrder(Node root){
//1.如果为null说明是空树,直接返回
if(root == null){
return;
}
//2.先打印,再进子节点
System.out.print(root.val + " ");
preOrder(root.left);
preOrder(root.right);
}
中序(遍历结果中,根节点左侧的就是左子树,根节点右侧的就是右子树)
//中序
public static void inOrder(Node root){
//1.如果为null说明是空树,直接返回
if(root == null){
return;
}
//2.进左子节点后打印再进右子节点
inOrder(root.left);
System.out.print(root.val + " ");
inOrder(root.right);
}
后序(最后一个元素是根节点)
//后序
public static void postOrder(Node root){
//1.如果为null说明是空树,直接返回
if(root == null){
return;
}
//2.进完两个子节点后再打印
inOrder(root.left);
inOrder(root.right);
System.out.print(root.val + " ");
}
层序(按照层次,把每一层的元素从左到右访问出来)
层序遍历的实现,需要搭配一个队列
//层序
public static void levelOrder(Node root){
if(root == null){
return;
}
//创建一个队列,辅助层序进行遍历
Queue<Node> queue = new LinkedList<>();
//把根节点添加到队列中
queue.offer(root);
//如果队首不为空
while (!queue.isEmpty()){
//取出队首元素
Node cur = queue.poll();
//打印
System.out.print(cur.val + " ");
//把队首的左右节点加入队列
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
}
7、二叉树的基本操作
7.1 求二叉树节点个数
//求二叉树的节点个数
public static int size(Node root){
if(root == null){
return 0;
}
//递归过程中把子树递归的结果返回上层方法中
//1 是当前节点不为空的返回值,
return 1 + size(root.left) + size(root.right);
}
7.2 求二叉树叶子结点个数
//求二叉树叶子结点的个数
public static int getLeafSize(Node root){
if(root == null){
return 0;
}
//两个子节点都为 null 就是叶子结点
if( root.left == null && root.right == null){
return 1;
}
//如果当前节点不是叶子结点就递归的求左、右子节点
return getLeafSize(root.left) + getLeafSize(root.right);
}
7.3 求 k 层节点个数
//求 k 层节点的个数
public static int getKLevelNodeCount(Node root,int k){
if(root == null || k < 1){
return 0;
}
//根节点
if(k==1){
return 1;
}
return getKLevelNodeCount(root.left,k-1) + getKLevelNodeCount(root.right,k-1);
}
7.4 获取二叉树的高度
//求二叉树的最大高度
public static int bestHigh(Node root){
if(root == null){
return 0;
}
return Math.max(bestHigh(root.left),bestHigh(root.right))+1;
}
7.5 查找 val 值是否存在
//查找元素是否存在
public static Node find(Node root,String val){
if(root == null){
return null;
}
if(root.val.equals(val)){
return root;
}
//递归查找左侧子树
Node leftResult = find(root.left,val);
if(leftResult != null){
//找到了直接返回结果
return leftResult;
}
//没找到就到右子树查找
Node rightResult = find(root.right,val);
if(rightResult != null){
return rightResult;
}
//最后没找到直接返回null
return null;
}