"二叉树"-实现数据结构算法,完全解析,通俗易懂的图文及代码讲解
来源:互联网 发布:弗林认罪 知乎 编辑:程序博客网 时间:2024/05/21 13:58
二叉树的完全理解
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.最重要的就是逻辑的判断:确定删除的点是否为根节点,再确定删除的点的儿子情况:有无儿子,有哪个儿子,有多少个儿子,选择继承的儿子.
以上就是二叉树算法的完全解析和归纳总结,希望能帮助大家更好的理解
0 0
- "二叉树"-实现数据结构算法,完全解析,通俗易懂的图文及代码讲解
- 面试准备--数据结构与算法(二)--二叉查找树的图文解析及其java实现
- "双向链表"-数据结构算法-之通俗易懂,完全解析
- AVL树建立 删除 最详细 最通俗易懂的代码 加图文解析!!!
- javascript数据结构及实现二叉树算法
- "startActivityForResult"-通俗易懂图文+代码全解析
- ORB算法讲解的通俗易懂
- 数据结构(十五) 线索二叉树讲解(带头节点中序加线索) 以及实现的完整代码
- 数据结构(十六) 线索二叉树讲解(带头节点后序加线索) 以及实现的完整代码
- 【数据结构与算法】求完全二叉树节点的个数
- 【数据结构和算法】【二叉树】二叉树遍历的代码实现
- 二叉查找树 图文解析 和 Java的实现
- 数据结构和算法--二叉树的实现
- "多线程和线程池"-之通俗易懂的介绍+图文,代码解析
- 数据结构(十三) 线索二叉树讲解(不带头节点前序加线索) 以及实现的完整代码
- 数据结构(十四) 线索二叉树讲解(不带头节点中序加线索) 以及实现的完整代码
- EM算法及其通俗易懂的讲解
- 【通俗易懂】层次聚类算法的原理及实现
- Android开发用过的十大框架
- MIT Introduction to Algorithms Record-8
- 物流营销服务
- 重定向NSLog
- 函数柯里化
- "二叉树"-实现数据结构算法,完全解析,通俗易懂的图文及代码讲解
- powershell复制目录结构和删除空目录
- 第四部分 linux led驱动代码分析
- Ignoring InnerClasses attribute for an anonymous inner class
- 回溯法解决批处理作业调度问题
- 【bzoj4569】【SCOI2016】【萌萌哒】【st表+并查集】
- 1052. 卖个萌 (20)
- 多维数组(Multi-dimensional Array)
- 我们缺的不是技术,而是解决问题的能力