轉 二叉樹之Java實現二叉樹基本操作


參考自《Java數據結構與算法》

  • 定義一個節點類,使節點與二叉樹操作分離


     
     
     
             
  1. class Node {
  2. int value;
  3. Node leftChild;
  4. Node rightChild;
  5. Node( int value) {
  6. this.value = value;
  7. }
  8. public void display() {
  9. System.out.print( this.value + "\t");
  10. }
  11. @Override
  12. public String toString() {
  13. // TODO Auto-generated method stub
  14. return String.valueOf(value);
  15. }
  16. }

  • 需要實現的二叉樹操作


     
     
     
             
  1. class BinaryTree {
  2. private Node root = null;
  3. BinaryTree( int value) {
  4. root = new Node(value);
  5. root.leftChild = null;
  6. root.rightChild = null;
  7. }
  8. public Node findKey(int value) {} //查找
  9. public String insert(int value) {} //插入
  10. public void inOrderTraverse() {} //中序遍歷遞歸操作
  11. public void inOrderByStack() {} //中序遍歷非遞歸操作
  12. public void preOrderTraverse() {} //前序遍歷
  13. public void preOrderByStack() {} //前序遍歷非遞歸操作
  14. public void postOrderTraverse() {} //后序遍歷
  15. public void postOrderByStack() {} //后序遍歷非遞歸操作
  16. public int getMinValue() {} //得到最小(大)值
  17. public boolean delete(int value) {} //刪除
  18. }

  • 查找數據:


     
     
     
             
  1. public Node findKey(int value) {
  2. Node current = root;
  3. while ( true) {
  4. if (value == current.value) {
  5. return current;
  6. } else if (value < current.value) {
  7. current = current.leftChild;
  8. } else if (value > current.value) {
  9. current = current.rightChild;
  10. }
  11. if (current == null) {
  12. return null;
  13. }
  14. }
  15. }

  • 插入數據:與查找數據類似,不同點在於當節點為空時,不是返回而是插入


     
     
     
             
  1. public String insert(int value) {
  2. String error = null;
  3. Node node = new Node(value);
  4. if (root == null) {
  5. root = node;
  6. root.leftChild = null;
  7. root.rightChild = null;
  8. } else {
  9. Node current = root;
  10. Node parent = null;
  11. while ( true) {
  12. if (value < current.value) {
  13. parent = current;
  14. current = current.leftChild;
  15. if (current == null) {
  16. parent.leftChild = node;
  17. break;
  18. }
  19. } else if (value > current.value) {
  20. parent = current;
  21. current = current.rightChild;
  22. if (current == null) {
  23. parent.rightChild = node;
  24. break;
  25. }
  26. } else {
  27. error = "having same value in binary tree";
  28. }
  29. } // end of while
  30. }
  31. return error;
  32. }

  • 遍歷數據:

            1)中序遍歷:最常用的一種遍歷方法


     
     
     
             
  1. /**
  2. * //中序遍歷(遞歸):
  3. * 1、調用自身來遍歷節點的左子樹
  4. * 2、訪問這個節點
  5. * 3、調用自身來遍歷節點的右子樹
  6. */
  7. public void inOrderTraverse() {
  8. System.out.print( "中序遍歷:");
  9. inOrderTraverse(root);
  10. System.out.println();
  11. }
  12. private void inOrderTraverse(Node node) {
  13. if (node == null)
  14. return ;
  15. inOrderTraverse(node.leftChild);
  16. node.display();
  17. inOrderTraverse(node.rightChild);
  18. }

               


     
     
     
             
  1. /**
  2. * 中序非遞歸遍歷:
  3. * 1)對於任意節點current,若該節點不為空則將該節點壓棧,並將左子樹節點置為current,重復此操作,直到current為空。
  4. * 2)若左子樹為空,棧頂節點出棧,訪問節點后將該節點的右子樹置為current
  5. * 3) 重復1、2步操作,直到current為空且棧內節點為空。
  6. */
  7. public void inOrderByStack() {
  8. System.out.print( "中序非遞歸遍歷:");
  9. Stack<Node> stack = new Stack<Node>();
  10. Node current = root;
  11. while (current != null || !stack.isEmpty()) {
  12. while (current != null) {
  13. stack.push(current);
  14. current = current.leftChild;
  15. }
  16. if (!stack.isEmpty()) {
  17. current = stack.pop();
  18. current.display();
  19. current = current.rightChild;
  20. }
  21. }
  22. System.out.println();
  23. }

              2)前序遍歷:


     
     
     
             
  1. /**
  2. * //前序遍歷(遞歸):
  3. * 1、訪問這個節點
  4. * 2、調用自身來遍歷節點的左子樹
  5. * 3、調用自身來遍歷節點的右子樹
  6. */
  7. public void preOrderTraverse() {
  8. System.out.print( "前序遍歷:");
  9. preOrderTraverse(root);
  10. System.out.println();
  11. }
  12. private void preOrderTraverse(Node node) {
  13. if (node == null)
  14. return ;
  15. node.display();
  16. preOrderTraverse(node.leftChild);
  17. preOrderTraverse(node.rightChild);
  18. }

               


     
     
     
             
  1. /**
  2. * 前序非遞歸遍歷:
  3. * 1)對於任意節點current,若該節點不為空則訪問該節點后再將節點壓棧,並將左子樹節點置為current,重復此操作,直到current為空。
  4. * 2)若左子樹為空,棧頂節點出棧,將該節點的右子樹置為current
  5. * 3) 重復1、2步操作,直到current為空且棧內節點為空。
  6. */
  7. public void preOrderByStack() {
  8. System.out.print( "前序非遞歸遍歷:");
  9. Stack<Node> stack = new Stack<Node>();
  10. Node current = root;
  11. while (current != null || !stack.isEmpty()) {
  12. while (current != null) {
  13. stack.push(current);
  14. current.display();
  15. current = current.leftChild;
  16. }
  17. if (!stack.isEmpty()) {
  18. current = stack.pop();
  19. current = current.rightChild;
  20. }
  21. }
  22. System.out.println();
  23. }

             3)后序遍歷:


     
     
     
             
  1. /**
  2. * //后序遍歷(遞歸):
  3. * 1、調用自身來遍歷節點的左子樹
  4. * 2、調用自身來遍歷節點的右子樹
  5. * 3、訪問這個節點
  6. */
  7. public void postOrderTraverse() {
  8. System.out.print( "后序遍歷:");
  9. postOrderTraverse(root);
  10. System.out.println();
  11. }
  12. private void postOrderTraverse(Node node) {
  13. if (node == null)
  14. return ;
  15. postOrderTraverse(node.leftChild);
  16. postOrderTraverse(node.rightChild);
  17. node.display();
  18. }

     
     
     
             
  1. /**
  2. * 后序非遞歸遍歷:
  3. * 1)對於任意節點current,若該節點不為空則訪問該節點后再將節點壓棧,並將左子樹節點置為current,重復此操作,直到current為空。
  4. * 2)若左子樹為空,取棧頂節點的右子樹,如果右子樹為空或右子樹剛訪問過,則訪問該節點,並將preNode置為該節點
  5. * 3) 重復1、2步操作,直到current為空且棧內節點為空。
  6. */
  7. public void postOrderByStack() {
  8. System.out.print( "后序非遞歸遍歷:");
  9. Stack<Node> stack = new Stack<Node>();
  10. Node current = root;
  11. Node preNode = null;
  12. while (current != null || !stack.isEmpty()) {
  13. while (current != null) {
  14. stack.push(current);
  15. current = current.leftChild;
  16. }
  17. if (!stack.isEmpty()) {
  18. current = stack.peek().rightChild;
  19. if (current == null || current == preNode) {
  20. current = stack.pop();
  21. current.display();
  22. preNode = current;
  23. current = null;
  24. }
  25. }
  26. }
  27. System.out.println();
  28. }


  • 得到最小(大)值:依次向左(右)直到空為之

     
     
     
             
  1. public int getMinValue() {
  2. Node current = root;
  3. while ( true) {
  4. if (current.leftChild == null)
  5. return current.value;
  6. current = current.leftChild;
  7. }
  8. }
  • 刪除:刪除操作很復雜,刪除節點大致分為三種情況:
             1)刪除節點為葉子節點

               

              2)刪除節點只有一個子節點:只有一個左子節點和只有一個右子節點

               

              3)刪除節點有兩個子節點:這種情況比較復雜,需要尋找后繼節點,即比要刪除的節點的關鍵值次高的節點是它的后繼節點。

            說得簡單一些,后繼節點就是比要刪除的節點的關鍵值要大的節點集合中的最小值

            得到后繼節點的代碼如下:


     
     
     
             
  1. /**
  2. *
  3. * 得到后繼節點,即刪除節點的左后代
  4. */
  5. private Node getSuccessor(Node delNode) {
  6. Node successor = delNode;
  7. Node successorParent = null;
  8. Node current = delNode.rightChild;
  9. while (current != null) {
  10. successorParent = successor;
  11. successor = current;
  12. current = current.leftChild;
  13. }
  14. //如果后繼節點不是刪除節點的右子節點時,
  15. if (successor != delNode.rightChild) {
  16. //要將后繼節點的右子節點指向后繼結點父節點的左子節點,
  17. successorParent.leftChild = successor.rightChild;
  18. //並將刪除節點的右子節點指向后繼結點的右子節點
  19. successor.rightChild = delNode.rightChild;
  20. }
  21. //任何情況下,都需要將刪除節點的左子節點指向后繼節點的左子節點
  22. successor.leftChild = delNode.leftChild;
  23. return successor;
  24. }
                  a)如果后繼節點是剛好是要刪除節點的右子節點(此時可以知道,這個右子節點沒有左子點,如果有,就不該這個右子節點為后繼節點)

          

            


     
     
     
             
  1. //刪除的節點為父節點的左子節點時:
  2. parent.leftChild = successor;
  3. successor.leftChild = delNode.leftChild;
  4. //刪除的節點為父節點的右子節點時:
  5. parent.rightChild = successor;
  6. successor.leftChild = delNode.leftChild

 
           

             b)如果后繼節點為要刪除節點的右子節點的左后代:

             

            


     
     
     
             
  1. //刪除的節點為父節點的左子節點時:
  2. successorParent.leftChild = successor.rightChild;
  3. successor.rightChild = delNode.rightChild;
  4. parent.leftChild = successor;
  5. successor.leftChild = delNode.leftChild;
  6. //刪除的節點為父節點的右子節點時:
  7. successorParent.leftChild = successor.rightChild;
  8. successor.rightChild = delNode.rightChild;
  9. parent.rightChild = successor;
  10. successor.leftChild = delNode.leftChild;

                 綜合以上各種情況,刪除代碼如下: 
           


     
     
     
             
  1. public boolean delete(int value) {
  2. Node current = root; //需要刪除的節點
  3. Node parent = null; //需要刪除的節點的父節點
  4. boolean isLeftChild = true; //需要刪除的節點是否父節點的左子樹
  5. while ( true) {
  6. if (value == current.value) {
  7. break;
  8. } else if (value < current.value) {
  9. isLeftChild = true;
  10. parent = current;
  11. current = current.leftChild;
  12. } else {
  13. isLeftChild = false;
  14. parent = current;
  15. current = current.rightChild;
  16. }
  17. //找不到需要刪除的節點,直接返回
  18. if (current == null)
  19. return false;
  20. }
  21. //分情況考慮
  22. //1、需要刪除的節點為葉子節點
  23. if (current.leftChild == null && current.rightChild == null) {
  24. //如果該葉節點為根節點,將根節點置為null
  25. if (current == root) {
  26. root = null;
  27. } else {
  28. //如果該葉節點是父節點的左子節點,將父節點的左子節點置為null
  29. if (isLeftChild) {
  30. parent.leftChild = null;
  31. } else { //如果該葉節點是父節點的右子節點,將父節點的右子節點置為null
  32. parent.rightChild = null;
  33. }
  34. }
  35. }
  36. //2、需要刪除的節點有一個子節點,且該子節點為左子節點
  37. else if (current.rightChild == null) {
  38. //如果該節點為根節點,將根節點的左子節點變為根節點
  39. if (current == root) {
  40. root = current.leftChild;
  41. } else {
  42. //如果該節點是父節點的左子節點,將該節點的左子節點變為父節點的左子節點
  43. if (isLeftChild) {
  44. parent.leftChild = current.leftChild;
  45. } else { //如果該節點是父節點的右子節點,將該節點的左子節點變為父節點的右子節點
  46. parent.rightChild = current.leftChild;
  47. }
  48. }
  49. }
  50. //2、需要刪除的節點有一個子節點,且該子節點為右子節點
  51. else if (current.leftChild == null) {
  52. //如果該節點為根節點,將根節點的右子節點變為根節點
  53. if (current == root) {
  54. root = current.rightChild;
  55. } else {
  56. //如果該節點是父節點的左子節點,將該節點的右子節點變為父節點的左子節點
  57. if (isLeftChild) {
  58. parent.leftChild = current.rightChild;
  59. } else { //如果該節點是父節點的右子節點,將該節點的右子節點變為父節點的右子節點
  60. parent.rightChild = current.rightChild;
  61. }
  62. }
  63. }
  64. //3、需要刪除的節點有兩個子節點,需要尋找該節點的后續節點替代刪除節點
  65. else {
  66. Node successor = getSuccessor(current);
  67. //如果該節點為根節點,將后繼節點變為根節點,並將根節點的左子節點變為后繼節點的左子節點
  68. if (current == root) {
  69. root = successor;
  70. } else {
  71. //如果該節點是父節點的左子節點,將該節點的后繼節點變為父節點的左子節點
  72. if (isLeftChild) {
  73. parent.leftChild = successor;
  74. } else { //如果該節點是父節點的右子節點,將該節點的后繼節點變為父節點的右子節點
  75. parent.rightChild = successor;
  76. }
  77. }
  78. }
  79. current = null;
  80. return true;
  81. }

  •                測試代碼

     
     
     
             
  1. public class BinaryTreeDemo {
  2. public static void main(String[] args) {
  3. BinaryTree bt = new BinaryTree( 52);
  4. bt.insert( 580);
  5. bt.insert( 12);
  6. bt.insert( 50);
  7. bt.insert( 58);
  8. bt.insert( 9);
  9. bt.insert( 888);
  10. bt.insert( 248);
  11. bt.insert( 32);
  12. bt.insert( 666);
  13. bt.insert( 455);
  14. bt.insert( 777);
  15. bt.insert( 999);
  16. bt.inOrderTraverse();
  17. bt.preOrderTraverse();
  18. bt.postOrderTraverse();
  19. System.out.println(bt.findKey( 32));
  20. System.out.println(bt.findKey( 81));
  21. System.out.println( "最小值:" + bt.getMinValue());
  22. // bt.delete(32); //刪除葉子節點
  23. // bt.delete(50); //刪除只有一個左子節點的節點
  24. // bt.delete(248); //刪除只有一個右子節點的節點
  25. // bt.delete(248); //刪除只有一個右子節點的節點
  26. // bt.delete(580); //刪除有兩個子節點的節點,且后繼節點為刪除節點的右子節點的左后代
  27. // bt.delete(888); //刪除有兩個子節點的節點,且后繼節點為刪除節點的右子節點
  28. bt.delete( 52); //刪除有兩個子節點的節點,且刪除節點為根節點
  29. bt.inOrderTraverse();
  30. }
  31. }
測試結果:

中序遍歷:91232 50 52 58248455580 666777 888999
中序非遞歸遍歷:9 12 32 50 5258248 455580 666777 888999
前序遍歷:52 12 9 50 3258058 248 455 888 666 777 999
前序非遞歸遍歷:52 12 9 50 3258058 248 455 888 666 777 999
后序遍歷:9 32 50 12 45524858 777 666 999 888 580 52
后序非遞歸遍歷:9 32 50 12 45524858 777 666 999 888 580 52
32
null
最小值:9
中序遍歷:9 12 32 50 58248455580 666777 888999

        </article>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM