Java数据结构学习——排序二叉树
目录
前言
正文
排序二叉树的特点
插入节点
删除树节点
删除的节点是叶子节点
删除的节点存在左或者右子节点
删除的节点存在左右两子节点
遍历节点
先序遍历
中序遍历
后续遍历
全部代码展示
总结
前言
由于本人的数据结构和算法能力比较薄弱,不管是在面试还是对于代码的一个性能提升都是少不过数据结构和算法的支撑,所以有断断续续的学习,此专栏就记录一下自己的学习过程和成果把!对大家不一定有帮助,更希望如果有错误之处希望能给我提出来!
正文
排序二叉树的特点
- 一个父节点只能有左右两个子节点。
- 左节点的值比父节点要小。
- 右节点的值要比父节点要大。
插入节点
从排序二叉树的特点我们只要一个父节点只能有2个子节点,并且左小右大,所以我们来一串数据可以自己先手动排序理清思路。[6,1,4,2,10,-1,15,8];
建议大家也写一串数字,然后自己手绘一把理清思绪好为写代码做铺垫
代码如下:
// 根节点
private TreeNode rootNode;public TreeNode getRootNode() {return rootNode;
}public void insertNode(TreeNode node){// 从根节点开始遍历新增的位置,// 左右节点有为null的就可以直接插入// // 判断根节点是否存在,不存在当前节点就是根节点if(rootNode==null){ rootNode = node;rootNode.setLefe(null);rootNode.setRight(null);// 这里就是代表根节点存在,就开始往下找}else{ // 获取到根节点作为循环最开始父节点// 后续在循环中一直作为父节点,可变参数TreeNode currentNode = rootNode; // 循环判断新来的节点是小于还是大于父节点,看怎么走分支while(true){// 走当前父节点右边// 判断条件就是新来的节点的值是否小于当前父节点if(node.getValue()< currentNode.getValue()){// 为null就直接放if(currentNode.getLefe()==null){currentNode.setLefe(node);return;}// 不为null就代表还有子节点,继续把当前父节点的左节点作为下次循环的父节点currentNode = currentNode.getLefe();// 走当前父节点的右边// 判断条件就是新来的节点的值是否大于当前父节点}else if (node.getValue()> currentNode.getValue()){ // 为null就直接放if(currentNode.getRight()==null){currentNode.setRight(node);return;}// 不为null就代表还有子节点,继续把当前父节点的右节点作为下次循环的父节点currentNode = currentNode.getRight();// 大于和小于都判断过了,所以进入到这里就达标重复了// 重复的逻辑自己定义,可以使替换之类的... 我这里就不能重复了}else{System.out.println("当前节点已经存在于树中");return;}}}
}// 树节点的结构
class TreeNode{private int value;private TreeNode lefe;private TreeNode right;public TreeNode(int value) {this.value = value;}public int getValue() {return value;}public void setValue(int value) {this.value = value;}public TreeNode getLefe() {return lefe;}public void setLefe(TreeNode lefe) {this.lefe = lefe;}public TreeNode getRight() {return right;}public void setRight(TreeNode right) {this.right = right;}
}
详细讲解看代码块的注释,每行的注释都是讲解的特别仔细。
插入的逻辑比较容易,就是新来的节点一直和当前的父节点做大小的比较走不同的分支,直到当前的父节点的左或右的子节点为null就可以直接添加。
删除树节点
对于排序二叉树来说删除节点大致可以分为
- 删除的节点是叶子节点,也就是删除的节点不存在左右子节点。
- 删除的节点存在左子节点。
- 删除的节点存在右子节点。
- 删除的节点存在左右两子节点。
删除的节点是叶子节点
什么是叶子节点?
那我们该如何删除呢?
这里我们需要根据value值找到删除的叶子节点和叶子节点的父节点。
为什么要找到父节点呢?因为我们需要通过父节点来找到删除节点是在左边还是右边,并且将删除节点的父节点的left或者right指向为null,help GC来回收删除节点。
帮助删除方法——根据删除的value找到删除的节点
// 删除方法的帮助方法——根据删除的value查找到删除的节点private TreeNode selectNode(int value){TreeNode currentNode = rootNode;// 如果root节点为null就直接返回null// 如果root节点的left和right都为null就直接返回null// 冗余代码,因为查询删除节点和查询删除节点的父节点都要查询。可以只在调用的地方使用一次
// if (currentNode==null) {
// return null;
// }while(true) {if(currentNode.getLefe()==null && currentNode.getRight()==null){return null;}// 如果查找的值小于if(currentNode.getLefe()!=null && value < currentNode.getValue()) {if(currentNode.getLefe().getValue()==value){return currentNode.getLefe();}currentNode = currentNode.getLefe();}else if(currentNode.getRight()!=null&& value>currentNode.getValue()){ // 如果查找的值大于if(currentNode.getRight().getValue()==value){return currentNode.getRight();}currentNode = currentNode.getRight();}}}
帮助删除方法——根据删除的value找到删除节点的父节点
// 删除方法的帮助方法——根据删除的value查找到删除的节点的父节点private TreeNode selectParentNode(int value) {TreeNode currentNode = rootNode;// rootNode判空代码在调用此方法之前要做,这里不用判断可以避免没必要的冗余// 这种情况就是只有rootNode节点。if(currentNode.getLefe()==null && currentNode.getRight()==null){return currentNode;}while (true){// 不用考虑线程安全的问题的话// 先调用查找删除节点的方法就保证了有父节点,所以这里对节点的判空也可以避免if(currentNode.getLefe()!=null && value<currentNode.getValue()){if (currentNode.getLefe().getValue()==value) {// 这里是返回父节点return currentNode;}currentNode = currentNode.getLefe();}else if (currentNode.getRight()!=null && value>currentNode.getValue()){if (currentNode.getRight().getValue()==value) {// 这里是返回父节点return currentNode;}currentNode = currentNode.getRight();}}}
具体的删除代码在后面的删除的节点存在左右两字节点
删除的节点存在左或者右子节点
这种删除,我们就直接让左或者右子节点来代替删除节点就行了。
但是我们要考虑是左还是又子节点,并且还是考虑删除节点是在父节点的左子节点还是右子节点
具体的删除代码在后面的删除的节点存在左右两字节点
删除的节点存在左右两子节点
这种就比较复杂了,因为左右子节点都存在,这种情况,我们就要把当前删除节点的右子节点当做一个树,找到树中最小的节点,也就是最左边节点。找到后我们需要把最小节点来代替删除节点。并且这种情况也存在删除的右子节点的左子节点不存在,这种情况就直接把删除的右子节点来代替删除的节点,所以我们需要找到右子节点中最小节点和最小节点的父节点,找父节点的方法前面已经展示了。
帮助删除方法——根据删除的value找到删除节点的父节点
// 帮助删除——删除节点存在两个左右子节点的情况// 找右子树的最小节点private TreeNode selectRightMinimum(TreeNode node){TreeNode mixLeft = node.getRight().getLefe();// 冗余代码,此代码应该是调用此方法者处理。
// if(mixLeft ==null){
// return node.getRight();
// }while(true){if(mixLeft.getLefe()==null){return mixLeft;}mixLeft = mixLeft.getLefe();}}
所以整体的删除代码如下:
// 删除节点public void deleteNode(int value){// 因为selectNode和selectParentNode没有对root节点做判断,所以这里做,也减少了代码的冗余if(rootNode==null){System.out.println("删除失败,树还没任何节点");return;}// 这个代表只有根节点if (rootNode.getValue()==value && rootNode.getRight()==null && rootNode.getLefe()==null) {// 直接把根节点rootNode = null;return;}TreeNode targetNode = this.selectNode(value);// 这个代表删除的值在树中没得if (targetNode== null) {System.out.println("您删除的内容树中没有");return;}// 获取到删除节点的父节点,因为要靠他找到删除节点是左边还是右边,并且帮助GC回收删除节点TreeNode parentNode = this.selectParentNode(value);// 下面就是删除的具体逻辑代码了// 这里需要分情况讨论了// 可能是叶子节点,就直接把当前节点给删除,其他操作都不需要做if(targetNode.getLefe()==null && targetNode.getRight()==null){// 但是我们并不知道targetNode是parentNode的左还是右子节点,所以需要if判断if(parentNode.getLefe().getValue()==value){parentNode.setLefe(null);}else{parentNode.setRight(null);}// 这种情况就是处理删除节点存在左子节点,右子节点不存在// 但是我们并不知道删除节点是在父节点的左边还是右边, 所以下面还有对方法做判断}else if(targetNode.getLefe()!=null && targetNode.getRight()==null){// 删除节点在parent节点的左边if(parentNode.getLefe().getValue()==value){// 将删除节点的左边节点赋值给父节点的左边节点parentNode.setLefe(targetNode.getLefe());}else{ // 这里代表删除节点在父节点的右边 // 将删除节点的左边节点赋值给父节点的左边节点parentNode.setRight(targetNode.getLefe());}// 这种情况就是处理删除节点存在右子节点,左子节点不存在// 但是我们并不知道删除节点是在父节点的左边还是右边, 所以下面还有对方法做判断}else if(targetNode.getLefe()==null && targetNode.getRight()!=null){// 删除节点在parent节点的左边if(parentNode.getLefe().getValue()==value){parentNode.setLefe(targetNode.getRight());}else{ // 删除节点在parent节点的右边parentNode.setRight(targetNode.getRight());}// 这种情况代表删除节点同时存在左右子节点}else{ // 这里代表右边节点作为树不存在左节点,所以直接处理右节点,// 也就是右节点树最小的就是右边第一个节点if (targetNode.getRight().getLefe()==null) {// 将删除节点的父节点的指向改变parentNode.setRight(targetNode.getRight());// 这里的getRight()获取到的节点就是删除节点的右子节点了parentNode.getRight().setLefe(targetNode.getLefe());return;}// 执行到这里就代表删除节点的右子节树存在左子节点// 所以我们就要通过我们前面编写好的帮助方法找到最小值// 改变指向的代码我就不注释了,这个大家要根据自己手绘图自己思考。TreeNode minimumNode = this.selectRightMinimum(targetNode);TreeNode minimumNodeParent = this.selectParentNode(minimumNode.getValue());minimumNodeParent.setLefe(null); // help GCparentNode.setRight(minimumNode);parentNode.getRight().setLefe(targetNode.getLefe());parentNode.getRight().setRight(targetNode.getRight());}}
上面代码块的注释已经特别的仔细了,不过这里建议大家根据博主的代码+自己手绘图来逐行理解
方法的前半段我们做一些特殊的判断,之后就根据删除节点的子节点做一些判断并做出不同的操作
遍历节点
对于排序二叉树来说,遍历分为三种遍历。
先序:根->左->右
中序:左->根->右
后续:左-> 右 ->根
先序遍历
// 亲忽略我的中文式命名(如果不是英文差,谁又愿意呢...)
// 根->左->又
public void xianXu(TreeNode treeNode){// 递归的退出条件if(treeNode!=null){System.out.print(treeNode.getValue()+"\t\t");// 左边xianXu(treeNode.getLefe());// 右边xianXu(treeNode.getRight());}
}
对于递归的操作也不是文字能描述明白的,更多的是大家根据代码和输出结构再根据自己手绘的图来做一个理解,不过我这里还是画个图来给大家理解一下吧。
注:如果图理解起来比较晕看不懂的话,就自己画图,因为每个人的思想和思路不一样,不要被我的图打乱思绪
中序遍历
// 左->根->右
public void zhongXu(TreeNode treeNode){if(treeNode!=null){zhongXu(treeNode.getLefe());System.out.print(treeNode.getValue()+"\t\t");zhongXu(treeNode.getRight());}
}
如果理解了先序遍历的思路,这里也不需要任何解释,一个递归的先后顺序变了,但是有没有发现中序遍历时从小到大的。建议大家使用手绘图来理解递归!
后续遍历
// 左-> 右 ->根
public void houXu(TreeNode treeNode){if(treeNode!=null){zhongXu(treeNode.getLefe());zhongXu(treeNode.getRight());System.out.print(treeNode.getValue()+"\t\t");}
}
如果理解了先序遍历的思路,这里也不需要任何解释,就是递归的先后顺序变了。建议大家使用手绘图来理解递归!
全部代码展示
package tree.erchaTree;import sun.reflect.generics.tree.Tree;/*** @author liha* @version 1.0* @date 2022/3/8 11:18*/
public class BinaryTree {// 根节点private TreeNode rootNode;public TreeNode getRootNode() {return rootNode;}@Overridepublic String toString() {return "BinaryTree{" +"rootNode=" + rootNode +'}';}/***@description 新增节点*@params* @param node*@return*@create_time 2022/3/8 11:25*@update_time 2022/3/8 11:25*/public void insertNode(TreeNode node){// 从根节点开始遍历新增的位置,并且左右节点有为null的就可以直接插入if(rootNode==null){ // 判断根节点是否存在,不存在当前节点就是根节点rootNode = node;rootNode.setLefe(null);rootNode.setRight(null);}else{ // 这里就是代表根节点存在,就开始往下找TreeNode currentNode = rootNode; // 最开始获取到根节点// 循环判断新来的节点是小于还是大于当前节点,看怎么走分支while(true){// 走左边if(node.getValue()< currentNode.getValue()){// 为null就直接放if(currentNode.getLefe()==null){currentNode.setLefe(node);return;}currentNode = currentNode.getLefe();}else if (node.getValue()> currentNode.getValue()){ // 走右边// 为null就直接放if(currentNode.getRight()==null){currentNode.setRight(node);return;}// 不为null就赋值继续自旋currentNode = currentNode.getRight();}else{System.out.println("当前节点已经存在于树中");return;}}}}// 删除节点public void deleteNode(int value){// 因为selectNode和selectParentNode没有对root节点做判断,所以这里做,也减少了代码的冗余if(rootNode==null){System.out.println("删除失败,树还没任何节点");return;}// 这个代表只有根节点if (rootNode.getValue()==value && rootNode.getRight()==null && rootNode.getLefe()==null) {// 直接把根节点rootNode = null;return;}TreeNode targetNode = this.selectNode(value);// 这个代表删除的值在树中没得if (targetNode== null) {System.out.println("您删除的内容树中没有");return;}TreeNode parentNode = this.selectParentNode(value);// 这里需要分情况讨论了// 可能是叶子节点,就直接把当前节点给删除,其他操作都不需要做if(targetNode.getLefe()==null && targetNode.getRight()==null){// 但是我们并不知道targetNode是parentNode的左还是右子节点,所以需要if判断if(parentNode.getLefe().getValue()==value){parentNode.setLefe(null);}else{parentNode.setRight(null);}}else if(targetNode.getLefe()!=null && targetNode.getRight()==null){// 删除节点在parent节点的左边if(parentNode.getLefe().getValue()==value){parentNode.setLefe(targetNode.getLefe());}else{ // 删除节点在parent节点的右边parentNode.setRight(targetNode.getLefe());}}else if(targetNode.getLefe()==null && targetNode.getRight()!=null){// 删除节点在parent节点的左边if(parentNode.getLefe().getValue()==value){parentNode.setLefe(targetNode.getRight());}else{ // 删除节点在parent节点的右边parentNode.setRight(targetNode.getRight());}}else{ // 删除存在两个节点// 这里代表右边树不存在左节点,所以直接处理右节点,也就是右树最小的就是右边第一个父节点if (targetNode.getRight().getLefe()==null) {parentNode.setRight(targetNode.getRight());parentNode.getRight().setLefe(targetNode.getLefe());return;}// 执行到这里代表target节点子树存在左边最小值。// 就找到最小值TreeNode minimumNode = this.selectRightMinimum(targetNode);TreeNode minimumNodeParent = this.selectParentNode(minimumNode.getValue());minimumNodeParent.setLefe(null); // help GCparentNode.setRight(minimumNode);parentNode.getRight().setLefe(targetNode.getLefe());parentNode.getRight().setRight(targetNode.getRight());}}// 帮助删除——删除节点存在两个左右子节点的情况// 找右子树的最小节点private TreeNode selectRightMinimum(TreeNode node){TreeNode mixLeft = node.getRight().getLefe();// 冗余代码,调用此方法者处理。
// if(mixLeft ==null){
// return node.getRight();
// }while(true){if(mixLeft.getLefe()==null){return mixLeft;}mixLeft = mixLeft.getLefe();}}// 删除方法的帮助方法——根据删除的value查找到删除的节点private TreeNode selectNode(int value){TreeNode currentNode = rootNode;// 如果root节点为null就直接返回null// 如果root节点的left和right都为null就直接返回null// 冗余代码,因为查询删除节点和查询删除节点的父节点都要查询。可以只在调用的地方使用一次
// if (currentNode==null) {
// return null;
// }while(true) {if(currentNode.getLefe()==null && currentNode.getRight()==null){return null;}// 如果查找的值小于if(currentNode.getLefe()!=null && value < currentNode.getValue()) {if(currentNode.getLefe().getValue()==value){return currentNode.getLefe();}currentNode = currentNode.getLefe();}else if(currentNode.getRight()!=null&& value>currentNode.getValue()){ // 如果查找的值大于if(currentNode.getRight().getValue()==value){return currentNode.getRight();}currentNode = currentNode.getRight();}}}// 删除方法的帮助方法——根据删除的value查找到删除的节点的父节点private TreeNode selectParentNode(int value) {TreeNode currentNode = rootNode;// rootNode判空代码在调用此方法之前要做,这里不用判断可以避免没必要的冗余// 这种情况就是只有rootNode节点。if(currentNode.getLefe()==null && currentNode.getRight()==null){return currentNode;}while (true){// 不用考虑线程安全的问题的话// 先调用查找删除节点的方法就保证了有父节点,所以这里对节点的判空也可以避免if(currentNode.getLefe()!=null && value<currentNode.getValue()){if (currentNode.getLefe().getValue()==value) {// 这里是返回父节点return currentNode;}currentNode = currentNode.getLefe();}else if (currentNode.getRight()!=null && value>currentNode.getValue()){if (currentNode.getRight().getValue()==value) {// 这里是返回父节点return currentNode;}currentNode = currentNode.getRight();}}}// 遍历的几种方式// 根->左->右// 左->根->右// 左->右->根// 根->左->又public void xianXu(TreeNode treeNode){// 递归的退出条件if(treeNode!=null){System.out.print(treeNode.getValue()+"\t\t");// 左边xianXu(treeNode.getLefe());// 右边xianXu(treeNode.getRight());}}// 左->根->右public void zhongXu(TreeNode treeNode){if(treeNode!=null){zhongXu(treeNode.getLefe());System.out.print(treeNode.getValue()+"\t\t");zhongXu(treeNode.getRight());}}// 左-> 右 ->根public void houXu(TreeNode treeNode){if(treeNode!=null){zhongXu(treeNode.getLefe());zhongXu(treeNode.getRight());System.out.print(treeNode.getValue()+"\t\t");}}}class TreeNode{private int value;private TreeNode lefe;private TreeNode right;public TreeNode(int value) {this.value = value;}public int getValue() {return value;}public void setValue(int value) {this.value = value;}public TreeNode getLefe() {return lefe;}public void setLefe(TreeNode lefe) {this.lefe = lefe;}public TreeNode getRight() {return right;}public void setRight(TreeNode right) {this.right = right;}
}
测试代码
/*** @author liha* @version 1.0* @date 2022/3/8 14:24*/
public class Test {public static void main(String[] args) {BinaryTree tree = new BinaryTree();tree.insertNode(new TreeNode(6));tree.insertNode(new TreeNode(1));tree.insertNode(new TreeNode(4));tree.insertNode(new TreeNode(2));tree.insertNode(new TreeNode(10));tree.insertNode(new TreeNode(-1));tree.insertNode(new TreeNode(15));tree.insertNode(new TreeNode(8));tree.insertNode(new TreeNode(13));tree.insertNode(new TreeNode(20));tree.zhongXu(tree.getRootNode());tree.deleteNode(10);System.out.println("删除过后");tree.zhongXu(tree.getRootNode());}
}
总结
难度并不大,但是得花时间画图来理解。
最后如果此贴有帮助到您,希望能点赞+关注,您的支持是我最大的努力!~
Java数据结构学习——排序二叉树相关推荐
- java数据结构学习笔记-二叉树前、中、后序遍历
public class BinaryTreeDemo {public static void main(String args[]){Employee emp1= new Employee(1,&q ...
- (数据结构)排序二叉树
(数据结构)排序二叉树 建立并中序遍历一个排序二叉树 排序二叉树是指左子树的所有节点的值均小于它根节点的值,右子树的所有节点的值均大于它根节点的值,如下图是一棵排序二叉树 输入: 输入有一行,表示若干 ...
- Java数据结构学习笔记
Java数据结构 枚举(Enumeration)/位集合(BitSet)/向量(Vector)/栈(Stack)/字典(Dictionary)/哈希表(Hashtable)/属性(Properties ...
- java代码实现排序二叉树
创建排序二叉树的步骤: 1.以根节点为当前节点开始搜索 2.拿新节点的值和当前节点的值比较 3.如果新节点的值更大,则以当前结点的右子节点作为新的当前节点:如果新节点的值更小,则以当前节点的左子节点作 ...
- Java数据结构学习
Set 无序不重复,遍历只能使用迭代器或增强for循环(对java编译器而言,等价). 1)hashSet:用hash表实现: 2)TreeSet:用排序二叉树实现: 当通过contains方法判断s ...
- 数据结构_排序二叉树(C语言)
数据结构总目录 排序二叉树 1. 结构解析 排序二叉树的结构规则很简单,只遵循一个基本规则: 那就是在二叉树中,选择任意根结点,其左子树都比根节点小,右子树都比根节点大. 排序二叉树结构图 观察如下二 ...
- 【数据结构】——排序二叉树
排序二叉树即在构建二叉树的时候就对二叉树进行排序了,当中序遍历二叉树的时候即可得到一个有序的数列: 排序二叉树的规则就是: 若他的左子树不空,则左子树上所有结点的值均小于它的根结构的值: 若他的右子树 ...
- 数据结构之——排序二叉树、平衡二叉树和线索二叉树
1.排序二叉树 排序二叉树(BST)也称二叉查找树,排序二叉树或者是一棵空树,或者是一棵具有下列特性的非空二叉树: (1)若左子树非空,则左子树上所有结点关键字值小于根节点的关键字值. (2)若右子树 ...
- 【Java数据结构】线索二叉树
中序线索二叉树 线索二叉树概述 线索二叉树代码实现 线索二叉树的数据结构 线索二叉树的遍历 线索二叉树示例完整代码 线索二叉树类 ThreadedBinaryTree 线索二叉树节点类 Threade ...
最新文章
- 别再折腾开发环境了,一劳永逸的搭建方法
- asp.net必须要放在form中吗_传闻:已经有人在0.33或0.34集成了Echarts(更新中)
- linux跟aix时间同步,Linux 与AIX环境下修改时间
- php怎么上传函数,PHP单文件上传原理及上传函数的封装操作示例
- 斐波那契查找+思路分析
- matlab中求积函数,MATLAB软件及高斯勒让德求积公式MATLAB软件及高斯勒让德求积公式.doc...
- Java实现端口扫描器
- 银行卡按卡号识别银行
- 解析数论导论中的符号
- 几种物流业务的凭证模板设置说明
- java网络学习之 ssh 协议学习总结
- VC++6.0常见问题之fatal error C1083解决方案
- android opengl 简书,Android OpenGL入门
- testin云测操作
- pixel-wise,patch-wise,image-wise的含义
- RT-Thread应用实战-TI温湿度HDC1000软件包设计与制作
- 求求你们,别再刷 Star 了!这跟“爱国”没关系!
- 【Python错误】Simplify chained comparison
- Linux查看硬件信息命令(转)
- Assetbundle解密问题记录: Failed to decompress data for the Assetbundle