二叉树的完全理解
1.概念:
1.每个节点(除了根节点)都有父节点
2.每个节点最多有两个节点
2.优点:
1.索引:提高查询效率,就是B树索引(Binary Tree)
2.比如全国十几亿个人的姓名和身份证,有同名的,想找一个人的信息,如果从头到尾逐条就太慢了.按正规来讲,在数据库中每个记录都有uid,给名字就找到对应的uid,1234...然后就可以找到同名字的各个身份证号;
3.因为二叉树是排列好的,所以可以直接确定在哪个位置.
3.二叉树的排列方式
无论哪一种,左节点肯定在右节点前面,”某”序根据的是”根”节点所在位置:
1.前序:(根节点在”前”)
根->左->右
7->3->1->5->9->11->21
2.中序:(根节点在”中”)
左->根->右
1->3->5->7->11->9->21
3.后序:(根节点在”后”)
左->右->根
1->5->3->11->21->9->7
4.层序(从上到下按层级)
最简单的分层
7->3->9->1->5->11->21
现在我们要实现一个标准的二叉树:1.有序排列的(中序),2.无重复元素的
一棵大树就一个根:root
一个节点的构成有:4部分
1.parent:父节点
2.Object:数据
3.left:左节点
4.right:右节点
节点属性图:
节点连接图:
添加数据方法add(Object data):
1.如果已经包含了,就不加入了,所以①写一个判断是否的方法.
接着:
1.创建节点;
2.给节点放入数据数据;
3.节点放入二叉树
4.如果根节点为空,根节点就等于这个节点;
如果不存在,就看应该放在哪个位置. 找它的父节点,所以②写一个找父节点的方法.
1.从根节点开始找,用传入的数据和根节点的数据比较
2.比根节点数据大就继续右边查找,小就是左边查找
3.比较"大小"注意点:是否实现了比较器 data instantsof Compale
实现了就强转为Comparable c1
没实现: c1 = data.toString();对比他们的字符方法,因为toString实现了;
最后:根据 compareTo得出那个大.
以上是完成数据的添加方法的逻辑.
接下来是打印的方法:(重点讲一下,因为涉及到递归)
打印的方法,因为是中序所以左->中->右打印
see(Note root){
note!=null;
//是个递归的过程,重要,相当于打完”每一个”点的左边才到中间才到右边
see(root.left);
system.out.println(root);
see(root.right);
}
相当于: 根的左->根的左->根的左然后才递上来,递归过程挺清晰的;
因为是中序遍历,所以左中右
接下来讲一下移除的方法,也是该算法的重点和难点
只要掌握以下几点要素即可完全理解
移除的点我们分为两种:
1.该点是根节点
1.根节点没左儿子,也没右儿子
结论:那么根就为空了,因为就一个点
2.根节点只有左儿子
结论:左儿子就继位了,根赋值为左儿子
3.根节点只有右儿子
结论:右儿子继位了,根赋值为右儿子
4.根节点同时有左右儿子
(可以选择左儿子继位,也可以选右儿子,我们随便选个就选左儿子吧)
结论:左儿子继位,那么根据中序的原理:右儿子就变成了左儿子的最右边的节点:
2.该点是非根节点
1.此节点没左儿子,也没右儿子
2.此节点只有左儿子
3.此节点只有右儿子
4.此节点同时有左右儿子
存在左右儿子,选左儿子继位图:
下面给出详细代码
public class MyTree {
//节点的属性
private class Node {
Node parent;//父节点
Object data;//数据
Node left; //左节点
Node right; //右
}
//根
private Node root;
public void add(Object data) {
//存在就不添加
if (contians(data)) {
return;
}
//建一个点
Node node = new Node();
node.data = data;
if (root == null) {//根是空,该点就是根,说成根就是该点更合适
root = node;
} else {
//判断是否存在
//有节点,那就:1.找到该节点的应该的父节点,2.找到了,比较判断该值和父节点的值,应该放左还是右
Node parent = findPrent(data, root);
node.parent = parent;//赋值再双向,确定父节点地址
if (compare(data, parent.data)) {
//大于就放在右边
//node.parent = parent.right;//赋值再双向,这个是错误的,等于parent 的地址,不用加上right
parent.right = node;
} else {
parent.left = node;
}
}
}
/**
* 包含就不添加
*/
private boolean contians(Object data) {
//查找到点就说明包含
Node node = findNode(data);
return node != null;
}
//找到该点的父节点
private Node findPrent(Object data, Node node) {
//从以该点为根开始找
Node parent = null;
Node temp = node;
while (temp != null) {
//先等于当前点,然后当左右都为空的时候就找到了
parent = temp;
if (compare(data, temp.data)) {
//比父节点大,继续右边找
temp = temp.right;
} else {
temp = temp.left;
}
}
return parent;
}
/**
* compare只比大小,不比相等,这里相等就是当小于
* 要能比较字符串和数字
* 相等就不要添加
* 然后看ture还是false放左右
*/
private boolean compare(Object data1, Object data2) {
Comparable c1;
Comparable c2;
if (data1 instanceof Comparable) {//看看是否传的是实现比较器的,是就转换
c1 = (Comparable) data1;
c2 = (Comparable) data2;
} else {
c1 = data1.toString();//否则就获取他们的toString();因为toString已经实现了Comparable比如数字,字符串都能比
c2 = data1.toString();
}
int i = c1.compareTo(c2);
return i > 0;
}
@Override
public String toString() {
return print();
}
/**
* 打印方法,要采用递归,同汉诺塔原理
* 中序
*/
public String print() {
StringBuffer sb = new StringBuffer();
see(root, sb);
String s = sb.toString();
if (s.length() <= 1) {
return "[" + s + "]";
}
s = s.substring(0, s.length() - 1);
return "[" + s + "]";
}
//把根点放入进去
private void see(Node root, StringBuffer sb) {
if (root != null) {
//先打印左
see(root.left, sb);
//再打印中
//System.out.println(root.data);
sb.append(root.data + ",");
//再打印右边
see(root.right, sb);
}
}
/**
* 移除方法
* 判断删除的是根节点还是非根节点
* 然后分别判断该点有无节点;只有左,还是只有右,还是都有,再双向判断
*/
public void remove(Object data) {
Node node = findNode(data);
if (node != null) {//存在才去做
//删的是根节点
if (node.parent == null) {//等同node==root;
//没有子节点
if (node.left == null && node.right == null) {
root = null;//根就就空了
}
//该根节点只有左节点无右
else if (node.right == null) {
node.parent = null;
root = node.left;//根就变左节点
}
//该根节点有右节点无左
else if (node.left == null) {
node.parent = null;
root = node.right;
}
//该根节点有左也有右
else {
//找最左儿子的最后边的节点,以此时的左儿子为根然后找到 此时右儿子应该的父节点,画图
//右儿子变成左儿子的"最右边"节点
//双向指向
//把一个点分裂,让左儿子继承即可
Node left = split(node);
//新的父节点,变成左儿子
root = left;
root.parent = null;
}
//非根节点,同样的判断
} else {
//该点无左无右
if (node.left == null && node.right == null) {
//判断自己是左还是右,通过对比父节点可知
if (compare(data, node.parent.data)) {
//该点是右
node.parent.right = null;
} else {
//是左
node.parent.left = null;
}
}
//该点有左无右
else if (node.right == null) {
//判断该点是左右
if (compare(data, node.parent.data)) {
//是右
node.parent.right = node.left;
} else {
node.parent.left = node.left;
}
}
//该点有右节点
else if (node.left == null) {
//判断左右
if (compare(data, node.parent)) {
//右边
node.parent.right = node.right;
} else {
node.parent.left = node.right;
}
//该点有左右
} else {
//分裂该节点
Node left = split(node);
//判断该点是左是右边
if (compare(data, node.parent.data)) {
//右
node.parent.right = left;//删除的位置由左儿子继承
} else {
//左
node.parent.left = left;
}
//双向
left.parent = node.parent;
}
}
}
}
private Node split(Node node) {
Node parentL = findPrent(node.right.data, node.left);//以右边数据为参考,从该点左侧开始找
parentL.right = node.right;//双向;
node.right.parent = parentL;//双向
return node.left;//返回的是继承的左儿子
}
//寻找该数据所在的的点
private Node findNode(Object data) {
//从根节点遍历,先默认为根,不等于的话就接着改变
Node node = root;
while (node != null) {
//相等就获取到了
if (data.equals(node.data) && data.hashCode() == node.data.hashCode()) {
return node;//等同break;
} else {
if (compare(data, node.data)) {
node = node.right;
} else {
node = node.left;
}
}
}
return node;
}
//更新方法
public void update(int oldData, int newData) {
//包含才操作
if (contians(oldData)) {
//删除旧的,放新的
remove(oldData);
add(newData);
}
}
}
新建测试类进行测试
public class MyTreeTest {
public static void main(String[] args) {
MyTree tree = new MyTree();
System.out.println("--------------------add----------------------");
tree.add(3);
tree.add(4);
tree.add(2);
tree.add(2);//重复
tree.add(21);
tree.add(11);
tree.add(12);
tree.add(7);
System.out.println(tree);
tree.remove(3);
System.out.println("--------------------remove 3----------------------");
System.out.println(tree);
tree.remove(21);
System.out.println("--------------------remove 21----------------------");
System.out.println(tree);
tree.update(12, 77);
System.out.println("--------------------把12 改成 21----------------------");
System.out.println(tree);
}
}
打印结果
--------------------add---------------------
[2,3,4,7,11,12,21]
--------------------remove 3----------------
[2,4,7,11,12,21]
--------------------remove 21---------------
[2,4,7,11,12]
--------------------把12 改成 21-------------
[2,4,7,11,77]
总结:
对于二叉树数据结构算法,大家只要理解一下几点就可完全明白算法过程:
1.节点的4个属性
2.选择中序排序的 左->中->右 特点
3.各种小方法构成大方法,小方法:
1.根据数据获取节点:findNode(data)
2.包含的方法
4.大方法:添加,删除
5.最重要的就是逻辑的判断:确定删除的点是否为根节点,再确定删除的点的儿子情况:有无儿子,有哪个儿子,有多少个儿子,选择继承的儿子.
以上就是二叉树算法的完全解析和归纳总结,希望能帮助大家更好的理解