文章目录
🚀 一、树形结构
🌟 1.1 什么是树?
树形结构是一种非线性的数据结构,它有以下特点:
- 有一个特殊的节点:根节点,根节点没有前驱节点。
- 除根节点外,其余节点被分成 M(M > 0)个互不相交的集合,其中每一个集合又是一颗与树类似的子树。
- 树是递归定义的。
🌟 1. 2 重要概念
- 节点的度:一个节点含有的子树的个数。
- 树的度:一棵树中,最大的节点的度称为树的度。
- 叶子节点:度为 0 的节点称为叶子节点。
- 双亲节点或父节点:若一个节点含有字节点,则这个节点称为它的子节点的父节点。
- 子节点:一个节点含有的子树的根节点为该节点的子节点。
- 节点的层次:从根节点开始定义,根节点为第一层,以此类推。
- 树的深度:树中节点的最大层次。
🚀 二、二叉树
🌟 2.1 概念
一棵二叉树是节点的有限集合,该集合或为空,或为一个根节点加上两颗分别称为左子树和右子树的二叉树组成。
二叉树的特点:
- 每个节点最多有两颗子树,二叉树不存在度大于 2 的节点。
- 二叉树的子树有左右之分,不能颠倒,因为二叉树是有序树。
🌟 2.2 两种特殊二叉树
- 满二叉树:一个二叉树,每层的节点数都达到最大值,则这个树就是满二叉树。即如果一个二叉树的层数为 K ,且节点总数为 2^K - 1 ,则它就是满二叉树。
- 完全二叉树:完全二叉树是一种效率很高的数据结构。对于深度为 K 的,有 n 个节点的二叉树,当且仅当其每一个节点都与深度为 K 的满二叉树中编号 1 到 n 的节点的位置一一对应时才称为完全二叉树。完全二叉树的节点按下标依次存放且连续。满二叉树是特殊的完全二叉树。
🌟 2.3 二叉树的性质(重要)
- 若规定根节点层数为 1,则一棵非空二叉树的第 i 层上最多有 2^(i-1) 个节点。
- 若规定只有根节点的二叉树的深度为 1,则深度为 K 的二叉树的最大节点数是 2^K - 1。
- 对任一棵二叉树,如果其叶节点个数为 n0 ,度为 2 的非叶节点个数为 n2 ,则有 n0 = n2 + 1。
- 具有 n 个节点的二叉树的深度 K 为 log( n+1 ) (以 2 为底)向上取整。
- 对于 n 个节点的完全二叉树,按照从上至下从左至右的顺序对所有节点从 0 开始编号,则对于序号为 i 的节点有:
- 若 i > 0 ,则父节点为 ( i-1 ) / 2。否则无父节点。
- 若 2i +1 < n ,则左孩子为 2i + 1。
- 若 2i + 2 < n,则右孩子为 2i + 2。
🌟 2.4 二叉树的存储
二叉树的存储结构分为顺序存储和链式存储。
- 堆主要采用顺序结构存储。
- 二叉树主要采用链式结构存储。
二叉树的链式存储是通过一个个的节点引用起来的,常见的有 孩子兄弟表示法 和 孩子双亲表示法。
// 链式结构下二叉树节点
// 孩子表示法
class Node {
int val; // 存储的数据
Node left; // 左孩子的引用
Node right; // 右孩子的引用
}
// 孩子双亲表示法
// AVL树、B-树、红黑树等树形结构会用到这种方法。
class Node{
int val; // 存储的数据
Node left; // 左孩子的引用
Node right; // 右孩子的引用
Node parent;// 当前节点的根节点
}
🚀 三、二叉树的基本操作
🌟 3.1 构建二叉树说明
在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。先在下面手动快速创建一棵简单二叉树。
/**
* 先创建一棵二叉树
*/
public TreeNode createTree(){
TreeNode root = new TreeNode('A');
TreeNode B = new TreeNode('B');
TreeNode C = new TreeNode('C');
TreeNode D = new TreeNode('D');
TreeNode E = new TreeNode('E');
TreeNode F = new TreeNode('F');
TreeNode G = new TreeNode('G');
TreeNode H = new TreeNode('H');
root.left = B;
root.right = C;
B.left = D;
B.right = E;
C.left = F;
C.right = G;
E.right = H;
return root;
}
上述代码并不是创建二叉树的真正方式,只是为了方便学习,快速搭建出来的。
🌟 3.2 二叉树的遍历
3.2.1 前中后序遍历
二叉树的遍历指沿着某条搜索路线,依次对树中每个节点做且仅做一次访问。遍历是二叉树最重要的操作之一。 二叉树的遍历主要有以下几种:
- 前序遍历:根节点 => 根的左子树 => 根的右子树
- 中序遍历:根的左子树 => 根节点 => 根的右子树
- 后序遍历:根的左子树 => 根的右子树 => 根节点
/**
* 前序遍历
*/
public void preOrder(TreeNode root){
if(root == null){
return ;
}
System.out.print(root.val + " ");
preOrder(root.left);
preOrder(root.right);
}
/**
* 中序遍历
* @param root
*/
public void inOrder(TreeNode root){
if(root == null){
return ;
}
inOrder(root.left);
System.out.print(root.val + " ");
inOrder(root.right);
}
/**
* 后序遍历
* @param root
*/
public void postOrder(TreeNode root){
if(root == null){
return ;
}
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val + " ");
}
3.2.2 二叉树的层序遍历
设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
/**
* 二叉树的层序遍历
* 需要使用到 队列 的数据结构
*
* @param root
*/
public void levelOrder(TreeNode root){
// 先创建一个队列,并把根节点放入队列
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
// 循环条件是,队列里还有元素,即层序遍历还未完成。
while(!queue.isEmpty()){
// 弹出队列头节点,
TreeNode top = queue.poll();
System.out.print(top.val + " ");
// 如果该节点的左子节点不为空,入队列
if(top.left != null){
queue.offer(top.left);
}
// 如果该节点的右子节点不为空,入队列
if(top.right != null){
queue.offer(top.right);
}
}
}
🌟 3.3 二叉树的基本操作
- 获取节点总数。
- 获取叶子节点总数
- 求第 K 层节点总数
- 获取二叉树高度
- 二叉树查找元素
/**
* 遍历思路,获取节点个数
*/
static int size = 0;
public void getSize1(TreeNode root){
if(root == null){
return;
}
size++;
getSize1(root.left);
getSize1(root.right);
}
/**
* 子问题思路,获取节点个数
* @param root
* @return
*/
public int getSize2(TreeNode root){
if(root == null){
return 0;
}
return getSize2(root.left) + getSize2(root.right) + 1;
}
/**
* 遍历思路,求叶子节点个数
*/
static int leafSize = 0;
public void getLeafSize1(TreeNode root){
if(root == null){
return;
}
if(root.left == null && root.right == null){
leafSize++;
}
getLeafSize1(root.left);
getLeafSize1(root.right);
}
/**
* 子问题思路,获取叶子节点个数
* @param root
* @return
*/
public int getLeafSize2(TreeNode root){
if(root == null){
return 0;
}
if(root.left == null && root.right==null){
return 1;
}
return getLeafSize2(root.left) + getLeafSize2(root.right);
}
/**
* 求第 K 层节点总数,子问题思路
* @param root
* @return
*/
public int getKlevelSize(TreeNode root, int k){
if(root == null){
return 0;
}
if(k == 1){
return 1;
}
return getKlevelSize(root.left,k-1) + getKlevelSize(root.right,k-1);
}
/**
* 获取二叉树高度
*
* @param root
* @return
*/
public int getHeight(TreeNode root){
if(root == null){
return 0;
}
return Math.max(getHeight(root.left),getHeight(root.right))+1;
}
/**
* 查找元素,根 => 左 => 右 的顺序进行查找
* @param root
* @param val
* @return
*/
public TreeNode find(TreeNode root, char val){
if(root == null){
return null;
}
if(root.val == val){
return root;
}
return find(root.left,val) == null ? find(root.right,val) : find(root.left,val);
}