前序
昨天我们分析了树形结构和特点,特别是二叉树结构和特点,手动创建了一个二叉树,并实现了先序,中序,后序三种遍历方式,那么今天就先来一个动态创建二叉树,并且是一个二叉查找树。
什么是二叉查找树?
二叉查找树是一种特殊的二叉树
特点:
- 左侧树节点全部比根节点小,右侧树节点全部比根节点大。
- 任意一个节点左儿子节点小,右儿子节点大。
通过这个结构,查找一个数将会非常方便。
来一个动态创建的二叉查找树
定义一个数组,用于创建二叉树的数据
int[] arr = { 3, 1, 5, 7, 9, 4 };
创建二叉树步骤解析:
- 以数组arr第一个索引【3】为二叉树根节点(根节点root)
- 以数组arr第二个索引【1】,和根节点比较,小于设置为根节点左侧儿子节点(根节点-左儿子)
- 以数组arr第三个索引【5】,和根节点比较,大于设置为根节点右侧儿子节点(根节点-右儿子)
- 以数组arr第四个索引【7】,和根节点比较大于,在和根节点右儿子比较大于,设置为右侧节点(根节点-右儿子-右孙子)
- 以数组arr第五个索引【9】,和根节点比较大于,在和根节点右儿子比较大于,在和根右孙子节点比较大于,设置为右侧节点(根节点-右儿子-右孙子-右曾孙子)
- 以数组arr第五个索引【4】,和根节点比较大于,在和根右儿子比较小于,设置为右儿子左侧节点(根节点,右儿子,左孙子)
代码逻辑
根据上述二叉树步骤解析,通过自定义一个包装类,完成二叉树的创建
public class TreeRoot {
private TreeNode treeNode;
public TreeNode getTreeRoot() {
return treeNode;
}
public void setTreeRoot(TreeNode treeNode) {
this.treeNode = treeNode;
}
代码逻辑说明:如果比当前根节点要小,那么放到当前根节点左边,如果比当前根节点要大,那么放到当前根节点右边。
/**
* 创建二叉树
*
* @author 张江丰
* @param tr
* 二叉树包装对象
* @param value
* 节点值
*/
public static void createTree(TreeRoot tr, int value) {
// 首先判断根节点数据为空
if (tr.getTreeRoot() == null) {
// 直接把value设置给root
tr.setTreeRoot(new TreeNode(value));
} else {
// 根不为空的
// value大于根节点设置到右侧
if (value > tr.getTreeRoot().getValue()) {
// 根右侧节点为空,直接设置
if (tr.getTreeRoot().getRightNode() == null) {
tr.getTreeRoot().setRightNode(new TreeNode(value));
} else {
// 右侧节点不为空
TreeNode trr = comparTree(tr.getTreeRoot().getRightNode(), value);
// tr.setTreeRoot(trr);
}
} else {
// value小于根节点设置到左侧
if (tr.getTreeRoot().getLeftNode() == null) {
tr.getTreeRoot().setLeftNode(new TreeNode(value));
// tr.setTreeRoot(new TreeNode(value));
} else {
// 左侧节点不为空
TreeNode trl = comparTree(tr.getTreeRoot().getLeftNode(), value);
// tr.setTreeRoot(trl);
}
}
}
}
如果是孙子节点,通过递归判断定位存放位置
/**
* 递归判断--判断数据存放节点位置
*
* @author 张江丰
* @param tn
* 左侧/右侧节点对象
* @param value
* 节点值
* @return
*/
public static TreeNode comparTree(TreeNode tn, int value) {
// 判断value比节点数据大
if (value > tn.getValue()) {
// 右侧节点为空,直接设置value
if (tn.getRightNode() == null) {
tn.setRightNode(new TreeNode(value));
return tn;
} else {
// 递归判断下一级节点
comparTree(tn.getRightNode(), value);
}
} else {
// value比节点数据小
if (tn.getLeftNode() == null) {
// 左侧节点为空,直接设置value
tn.setLeftNode(new TreeNode(value));
return tn;
} else {
// 左侧节点有数据,递归判断下一级节点
comparTree(tn.getLeftNode(), value);
}
}
return tn;
}
数据测试
public static void main(String[] args) {
int[] arr = { 3, 1, 5, 7, 9, 4 };
TreeRoot tr = new TreeRoot();
for (int i : arr) {
createTree(tr, i);
}
// TreeNode.befoTree(tr.getTreeRoot());
TreeNode.betTree(tr.getTreeRoot());
// TreeNode.afterTree(treeNode);
}
验证:
这里使用中序遍历方式,中序遍历的顺序(左->根->右),输出结果应该为1-3-4-5-7-9,遍历代码具体请看【来玩二叉树NO.1】–之遍历算法
控制台输出
逻辑debug
二叉查找树深度查询
查询树的深度我们可以这样想:根节点下左边的子树和右边的字数比,谁大就返回谁(层级多),然后再接上根节点+1就可以了。
public static int getDeep(TreeNode tn) {
if (tn == null) {
return 0;
} else {
// 查询左侧子树深度
int left = getDeep(tn.getLeftNode());
// 查询右侧子树深度
int right = getDeep(tn.getRightNode());
// 以左侧子树为锚点比较深度
int max = left;
// 判断左,右子树深度大小,如果左右两侧深度相等跳过直接加上根节点返回
if (right > max) {
max = right;
}
// 加上根节点返回计算深度结果
return max + 1;
}
充分利用递归算法的优势,通过递归层层计算节点深度,最后返回根节点,经过比较找出二叉查找树深度。
二叉树的最大值
在上一篇博客二叉树的遍历算法,中序遍历(左-根-右)的结果已经是排好序,那么如果不是二叉查找树,又如何查询到最大值?
其实可以通过一个根节点和左右子节点比较,谁大返回谁,在递归下返回最大的值
public static int getMaxValue(TreeNode rootTreeNode) {
if (rootTreeNode == null) {
return -1;
} else {
// 找出左边的最大值
int left = getMaxValue(rootTreeNode.getLeftNode());
// 找出右边的最大值
int right = getMaxValue(rootTreeNode.getRightNode());
// 与当前根节点比较
int currentRootValue = rootTreeNode.getValue();
// 以左侧子树为锚点
int max = left;
//先比较左右两侧子节点,谁大返回谁
if (right > max) {
max = right;
}
//父节点和最大的子节点比较,谁大返回谁
if (currentRootValue > max) {
max = currentRootValue;
}
//返回父节点-左儿子-右儿子的最大值
return max;
}
}