今天偶遇一到数据结构的题目,发现自己对二叉树的相关知识变得很模糊,花了点时间又重新温习了一遍,写下这篇文章做个记录。
二叉树:二叉树是树型数据结构的一种,每个节点最多有两个子树,有左右之分 如图
二叉树的相关术语
- 节点:包含一个数据元素及若干指向子树分支的信息 。
- 节点的度:一个节点拥有分支的数目称为节点的度
- 叶子节点:也称为终端节点,没有子树的节点或者度为零的节点 。
- 分支节点:也称为非终端节点,度不为零的节点称为非终端节点 。
- 树的度:树中所有节点的度的最大值 。
- 节点的层次:从根节点开始,假设根节点为第1层,根节点的子节点为第2层,依此类推,如果某一个节点位于第L层,则其子节点位于第L+1层 。
- 树的深度:也称为树的高度,树中所有节点的层次最大值称为树的深度 。
- 有序树:如果树中各棵子树的次序是有先后次序,则称该树为有序树 。
- 无序树:如果树中各棵子树的次序没有先后次序,则称该树为无序树 。
- 森林:由m(m≥0)棵互不相交的树构成一片森林,如果把一棵非空的树的根节点删除,则该树就变成了一片森林,森林中的树由原来根节点的各棵子树构成 。
二叉树的数据结构
/**
* 二叉树数据结构
*/
static class Node {
//节点值
char value;
//左子树
Node lChild;
//左子树
Node rChild;
public Node(char value) {
this.value = value;
}
}
二叉树的常见操作
创建二叉树
/**
* 定义一个数组
* 下标0的元素为根节点
* 左子节点为本节点下标*2 +1
* 右子节点为本节点下标*2 +2
* 当节点值为0时,标识节点不存在
*/
public static final char[] chars = {'F', 'C', 'E', 'A', 'D', 'H', 'G', '0', '0', 'B', '0', '0', '0', 'M'};
/**
* 递归创建二叉树
*
* @param data 用来构造树的原始数据
* @param index 下标
* @return
*/
public static Node createTree(char[] data, int index) {
//下标长度超出长度,或者内容值等于0是 节点不存在,返回null
if (index >= data.length || data[index] == '0') {
return null;
}
//创建节点
Node node = new Node(data[index]);
//左子树下标
int lIndex = 2 * index + 1;
//右子树下标
int rIndex = 2 * index + 2;
//递归创建左子树
node.lChild = createTree(data, lIndex);
//递归创建右子树
node.rChild = createTree(data, rIndex);
return node;
}
public static void main(String[] args) {
Node tree = createTree(chars, 0);
}
遍历二叉树
/**
* 层先法遍历tree
* 层法从做到右遍历
*/
public static void showTree(Node tree) {
//定义队列
Queue<Node> queue = new LinkedList<Node>();
//根节点入队
queue.offer(tree);
//循环,直到队列为空
while (!queue.isEmpty()) {
//出队
Node temp = queue.poll();
System.out.println(temp.value);
Node l = temp.lChild;
Node r = temp.rChild;
//左孩子不为空入队
if (l != null) queue.offer(l);
//右孩子不为空入队
if (r != null) queue.offer(r);
}
}
求树高
/**
* 求树高
*/
public static int getHeight(Node tree) {
if (tree == null)
return 0;
//递归求左子树高度
int lHeight = getHeight(tree.lChild);
//递归求右子树高度
int rHeight = getHeight(tree.rChild);
//返回当前树高
return lHeight > rHeight ? lHeight + 1 : rHeight + 1;
}
查找数的层级
/**
* 递归查找层级
* 先看当前层级是否存在,存在返回层级数
* 不存在层级 +1 往左子树传递,
* 当左子树不存在,再查找右子树
*/
public static int getLevel(Node root, char nodeValue, int level) {
//未找到
if (root == null) {
return -1;
}
//找到了返回
if (root.value == nodeValue) {
return level;
}
//递归查找左子树
int lLevel = getLevel(root.lChild, nodeValue, level + 1);
if (lLevel == -1) {
//当左子树不存在,递归查找右子树
return getLevel(root.rChild, nodeValue, level + 1);
} else {
//左子树存在,返回层级
return lLevel;
}
}
/**
* 从根节点查找层级(调用上面方法,使用了重载,跟结点为1)
*/
public static int getLevel(Node root, char nodeValue) {
return getLevel(root, nodeValue, 1);
}
二叉数进阶操作
求两节点的公共节点
/**
* 求公共节点
* 从根目录开始往下找,看左右子树下是否能找到a或者b
* 当从一个子树下能找到a或者b,另一个子树下也能找到,则说明此节点是公共节点
* 当一个子树能找到,另一个子树不能找到,说明这个节点不是他们的公共节点,再递归能找到的子树继续查找
*/
public static Node getPublicNode(Node root, char a, char b) {
//当找到空节点时,表示未找到,返回nul
if (root == null) {
return null;
}
//当前node为a 或者 为 b,返回
if (root.value == a || root.value == b) {
return root;
}
//递归从孩子节点查找
Node l = getPublicNode(root.lChild, a, b);
Node r = getPublicNode(root.rChild, a, b);
//俩孩子节点都未找到,则公共节点不存在(输入有误)
if (l == null && r == null) return null;
//在左子树中找到,右子树未找到
if (l != null && r == null) return l;
//在右子树找到,左子树未找到
if (l == null) return r;
//左右子树都找到了。则公共节点为当前节点
return root;
}
求两节点的距离
要使用求层级的方法,前面介绍有讲到
/**
* 求两节点的距离
* a到根的距离(层级-1) + b到根的距离(层级-1) - (2 * 公共节到根距离)
*/
public static int getDistance(Node root, Node a, Node b) {
//求公共节点
Node publicNode = getPublicNode(root, a.value, b.value);
int levelA = getLevel(root, a.value);
int levelB = getLevel(root, b.value);
int levelp = getLevel(root, publicNode.value);
//节点到根的距离 = 节点的层级 - 1
//a到根的距离 + b到根的距离 - (2 * 公共节到根距离)
return (levelA - 1) + (levelB - 1) - (2 * (levelp - 1));
}