当前位置:   article > 正文

自己用java实现二叉树的增,删,查_java 二叉树增删改查

java 二叉树增删改查
在看TreeMap的源码之前,有必要先了解下tree这个数据结构,很早之前看java版的数据结构与算法,也是卡到tree这里,没再读下去。现在重新复习这块知识。先从二叉树开始吧,BST 英文 binary search tree 直译二分查找树。
某个节点的值都大于该节点的左子树里所有的节点。反之右子树的所有节点的值都大于该节点。
记忆 : 大佐(左)
节点 有父类节点,左子树节点,右子树节点,实现了Comparable的value
  1. static class Node implements Comparable{
  2. Node parent;//父类节点
  3. Node left;//左子树节点
  4. Node right;//右子树节点
  5. Comparable value;//实现了Comparable的value
  6. public Node(Node parent, Comparable value, Node left, Node right){
  7. this.parent=parent;
  8. this.left=left;
  9. this.right=right;
  10. this.value=value;
  11. }
  12. }
它的操作主要有
a.插入
b.遍历
假设如下

遍历又分为
1.深度优先: 沿着树的深度遍历树的节点,尽可能深的搜索树的分支,
深度优先遍历又分为前序遍历,中序遍历,后序遍历
上面二叉树的前序遍历 顺序为:ABDECFG
上面二叉树的中序遍历 顺序为:DBEAFCG
上面二叉树的后序遍历 顺序为:DEBFGCA
根节点在哪个位置输出就是什么顺序遍历
2.广度优先 : 从根结点开始沿着树的宽度搜索遍历,
上面二叉树的遍历顺序为:ABCDEFG
c.删除
1.删除的节点只有右子树,只要把节点的父节点的右子树指向删除节点的右子树
eg:删除52后, 50.right(52.parent)--->54(52.right)
2.删除的节点只有左子树,只要把节点的父节点的左子树指向删除节点的左子树
eg:删除83后,85.left(83.parent)--->54(83.left)
3.删除的节点有右子树和左子树。有2种算法
从删除的节点的左子树里找到最大的值,作为删除节点的父节点的左或右子树(依据于删除节点原来是父节点的左或右)
eg:删除35后,68.left(35.parent)--->33
从删除的节点的右子树里找到最小的值,作为删除节点的父节点的左或右子树(依据于删除节点原来是父节点的左或右)
eg:删除35后,68.left(35.parent)--->50,删掉50后,后续52接上

纸上得来终觉浅,绝知此事要躬行。接下来就是撸代码了,主要是理解原理的基础上来写就不会觉得很难。

1.定义一个类MyBST
里面有static class Node。是不是和hashmap里的HashMap.Node一样,
这是读源码的一个好处吧。
  1. public class MyBST<C extends Comparable<?>> {
  2. static class Node implements Comparable,Cloneable{
  3. Node parent;//父类节点
  4. Node left;//左子树节点
  5. Node right;//右子树节点
  6. Comparable value;//实现了Comparable的value
  7. public Node(Node parent, Comparable value, Node left, Node right){
  8. this.parent=parent;
  9. this.left=left;
  10. this.right=right;
  11. this.value=value;
  12. }
  13. }
  14. }
2.增加
增加的代码不是很难写
  1. public boolean add(C c){
  2. if(root==null){
  3. root = new MyBST.Node(null,c,null,null);
  4. }else{
  5. Node node = root;
  6. Node nodeParent = root;
  7. boolean isNodeLeft = false;
  8. //按大左小右,直到node为空,nodeParent 就是要插入的父节点了,
  9. //注意isNodeLeft来判断到底是插入nodeParent.right还是nodeParent.left
  10. while(node!=null){
  11. nodeParent = node;
  12. if(node.value.compareTo(c)>0){
  13. //should put root.left
  14. node=node.left;
  15. isNodeLeft = true;
  16. }else if(node.value.compareTo(c)<0){
  17. //should put root.right
  18. node=node.right;
  19. isNodeLeft = false;
  20. }else{
  21. System.out.println("can‘t add duplicate value");
  22. return false;
  23. }
  24. }
  25. node = new MyBST.Node(nodeParent,c,null,null);
  26. if(isNodeLeft)
  27. nodeParent.left = node;
  28. else
  29. nodeParent.right = node;
  30. }
  31. return true;
  32. }
3.遍历
只写了深度优先的前序遍历,中序遍历,后序遍历。
算法有递归和非递归2种算法。递归很简单,非递归就比较麻烦,特别是后续遍历的非递归。
算法的理解参照 http://blog.csdn.net/pi9nc/article/details/13008511/ 感谢这位作者的分享,我在看了
算法的基础上以我理解的方式用 java来写
【先序遍历】
  1. //先序遍历递归写法 【根节点-左子树-右子树】
  2. public void preTravel_recrusive(MyBST.Node node){
  3. if(node==null)
  4. return;
  5. System.out.println(node.value);
  6. if(node.left!=null){
  7. preTravel_recrusive(node.left);
  8. }
  9. if(node.right!=null){
  10. preTravel_recrusive(node.right);
  11. }
  12. }
  13. //先序遍历非递归写法 借助了stack这个先进后出的数据结构
  14. public void preTravel(){
  15. Stack<Node> stack = new Stack<Node>();
  16. if(this.root==null)
  17. return;
  18. Node current = this.root;
  19. //先把root压入栈顶
  20. stack.push(current);
  21. while(current!=null || !stack.isEmpty()){
  22. if(current!=null){
  23. System.out.println(current.value);
  24. current = current.left;
  25. }else{
  26. current = stack.pop();
  27. current = current.right;
  28. }
  29. if(current!=null)
  30. stack.push(current);
  31. }
  32. }
【中序遍历】
//中序遍历 【左子树-根节点-右子树】
  1. public void midTravel_recrusive(MyBST.Node node){
  2. if(node==null)
  3. return;
  4. if(node.left!=null){
  5. midTravel_recrusive(node.left);
  6. }
  7. System.out.println(node.value);
  8. if(node.right!=null){
  9. midTravel_recrusive(node.right);
  10. }
  11. }
  12. //中序遍历非递归写法 借助了stack这个先进后出的数据结构
  13. public void midTravel(){
  14. Stack<Node> stack = new Stack<Node>();
  15. Node node = this.root;
  16. if(node==null)
  17. return;
  18. stack.push(node);
  19. //如果node为空,就要去stack里判断是否为空
  20. while(node!=null || !stack.isEmpty()){
  21. if(node!=null){
  22. node = node.left;
  23. }else{
  24. node = stack.pop();
  25. System.out.println(node.value);
  26. node = node.right;
  27. }
  28. if(node!=null)
  29. stack.push(node);
  30. }
  31. }
【后序遍历】
  1. //后序遍历 【左子树-右子树-根节点】
  2. public void behideTravel_recrusive(MyBST.Node node){
  3. if(node==null)
  4. return;
  5. if(node.left!=null){
  6. behideTravel_recrusive(node.left);
  7. }
  8. if(node.right!=null){
  9. behideTravel_recrusive(node.right);
  10. }
  11. System.out.println(node.value);
  12. }
  13. //非递归后序遍历,这个发了我很长时间调试。
  14. //主要是理解算法和原理,方向对了,剩下的就是不断的测试各种可能性
  15. public void behideTravel(){
  16. if(this.root==null){
  17. return ;
  18. }
  19. Stack<Node> stack = new Stack<Node>();
  20. Node node=null,cur=this.root,pre=null;
  21. stack.add(cur);
  22. while(!stack.isEmpty()){
  23. node=null;
  24. //注意pre要!=null
  25. if(cur.right!=null && !(pre!=null && (cur.left==pre||cur.right==pre))){
  26. stack.add(cur.right);
  27. node = cur.right;
  28. }
  29. //注意pre要!=null
  30. if(cur.left!=null && !(pre!=null && (cur.left==pre||cur.right==pre))){
  31. stack.add(cur.left);
  32. node =cur.left;
  33. }
  34. if(node!=null)
  35. cur = node;
  36. else{
  37. pre = cur;
  38. cur = stack.peek();
  39. //注意pre要!=null
  40. if( (cur.right==null || cur.left==null) ||
  41. (pre!=null && (cur.left==pre||cur.right==pre))
  42. ){
  43. System.out.println(cur.value);
  44. cur = stack.pop();
  45. }
  46. }
  47. }
  48. }
4.删除
  1. public boolean delete(C c){
  2. Node deleteNode = root;
  3. //isLeft 删除节点是它的父类的左还是右
  4. boolean isLeft = false;
  5. while(deleteNode!=null){
  6. if(deleteNode.value.compareTo(c)>0){
  7. deleteNode = deleteNode.left;
  8. isLeft = true;
  9. }else if (deleteNode.value.compareTo(c)<0){
  10. deleteNode = deleteNode.right;
  11. isLeft = false;
  12. }else{
  13. break;
  14. }
  15. }
  16. if(deleteNode==null)
  17. return false;
  18. else{
  19. if(deleteNode.left==null && deleteNode.right==null){//删除的节点是叶子节点
  20. if(deleteNode.parent==null){//注意头节点
  21. root = null;
  22. return true;
  23. }
  24. if(isLeft){
  25. deleteNode.parent.left=null;
  26. }else{
  27. deleteNode.parent.right=null;
  28. }
  29. deleteNode.parent=null;
  30. }else if(deleteNode.left!=null && deleteNode.right==null){//删除的节点有left
  31. if(deleteNode.parent==null){//delete node is root
  32. root = deleteNode.left;
  33. }else{
  34. deleteNode.parent.left=deleteNode.left;
  35. deleteNode.left.parent=deleteNode.parent;
  36. }
  37. }else if(deleteNode.right!=null && deleteNode.left==null){//删除的节点有right
  38. if(deleteNode.parent==null){//delete node is root
  39. root = deleteNode.right;
  40. }else{
  41. deleteNode.parent.right=deleteNode.right;
  42. deleteNode.right.parent=deleteNode.parent;
  43. }
  44. }else{//have right and left child tree
  45. Node successor = deleteNode.right;
  46. // 从删除的节点的右子树里找到最小的值,作为删除节点的父节点的左或右子树
  47. //(依据于删除节点原来是父节点的左或右)
  48. while(successor.left!=null){
  49. successor=successor.left;
  50. }
  51. if(isLeft){
  52. successor.parent.left = successor.right;
  53. }else{
  54. successor.parent.right = successor.right;
  55. }
  56. if(successor.right!=null){
  57. successor.right.parent=successor.parent;
  58. }
  59. if(deleteNode.parent==null){//注意删除的节点是头节点
  60. root = successor;
  61. }else{
  62. if(isLeft){
  63. deleteNode.parent.left=successor;
  64. }else{
  65. deleteNode.parent.right=successor;
  66. }
  67. }
  68. successor.right=deleteNode.right;
  69. successor.left=deleteNode.left;
  70. successor.parent=deleteNode.parent;
  71. }
  72. deleteNode.parent=null;
  73. deleteNode.right=null;
  74. deleteNode.left=null;
  75. return true;
  76. }
  77. }
在测试的时候要注意各种可能性,多测各种临界条件。
代码在

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/306663
推荐阅读
相关标签
  

闽ICP备14008679号