1. 程式人生 > >二叉樹構建、新增、刪除和遍歷總結

二叉樹構建、新增、刪除和遍歷總結

敬請關注部落格,後期不斷更新優質博文,謝謝
原始碼: ------------------------------------------------------------------------------------ Node.java: package cn.com.tree; /**  * 二叉樹節點  * @author daniel zhou  *  */ public class Node {       
// 資料項        public long data ;        // 資料項        public String sData ;        // 左子節點        public
Node leftChild ;        // 右子節點        public Node rightChild ;               /**        * 構造方法        *
@param data        * @param sData        */        public Node( long data, String sData){              this . data = data;              this . sData = sData;       } } -------------------------------------------------------------------------------- Tree.java:
package cn.com.tree; /**  * 二叉樹類  * @author daniel zhou  *  */ public class Tree {               // 根節點        public Node root ;               /**        * 原理:在一顆二叉樹上插入節點,插入是建立在小於父節點,        * 則插入到父節點左邊,如果大於父節點,則插入到父節點右邊。        * 在插入樹節點,需要判斷樹根是否為空,如果不為空,則需要        * 當前節點指向樹根和父節點指向當前節點。直到當前節點等於null,        * 那麼可以在父節點左邊或者右邊插入新節點,並且返回樹根跳出迴圈。        * 如果樹根為空,直接把樹根指向新建立的節點,實現過程如下所示:        *        * 插入節點        * @param value        */        public void insert( long value, String sValue){              // 封裝節點             Node newNode = new Node(value, sValue);              // 引用當前節點             Node current = root ;              // 引用父節點             Node parent;              // 如果root為空,也就是第一次插入的時候              if ( root == null ){                    root = newNode;                    return ;             } else {                    while ( true ){                          // 父節點指向當前節點                         parent = current;                          // 如果當前指向的節點資料比插入的要大,則向左走                          if (current. data > value){                               current = current. leftChild ;                                if (current == null ){                                     parent. leftChild = newNode;                                      return ;                               }                         } else {                               current = current. rightChild ;                                if (current == null ){                                     parent. rightChild = newNode;                                      return ;                               }                         }                   }             }       }               /**        * 查詢節點        * @param value        * @return        */        public Node find( long value){              // 引用當前節點             Node current = root ;              // 迴圈,只要查詢值不等於當前節點的資料項              while (current. data != value){                    // 進行比較,比較查詢值和當前節點的大小                    if (current. data > value){                         current = current. leftChild ;                   } else {                         current = current. rightChild ;                   }                    // 如果查詢不到                    if (current == null ){                          return null ;                   }             }              return current;       }       
二叉樹刪除節點原理圖: 由於過程比較複雜,這裡用圖來表示
       /**        * 刪除節點:        * 工作原理:                   從二叉查詢樹上刪除節點的操作複雜程度取決於刪除哪個節點。如果刪除沒有子節點的節點就非常簡單,                   如果節點只有一個子節點,不管是左子節點還是右子節點,就變得稍微有點複雜,如果節點包含兩個子節點就最複雜。                   如果待刪除節點是葉子節點,那麼只需要將從父節點指向它的連結指向null。                   如果待刪除節點只包含一個子節點,那麼原本指向它的節點就得使其指向它的子節點。                   如果待刪除節點包含兩個子節點,那麼我們可以採用兩種方式:                   一種是查詢待刪除節點左子樹上的最大值,                   一種是查詢待刪除節點右節點上的最小值。                   我們採取後者,找到最小值後,將臨時節點上的值複製到待刪除節點,然後再刪除臨時節點。        * @param value        */        public boolean delete( long value){              // 引用當前節點,從父節點開始             Node current = root ;              // 引用當前節點父節點             Node parent = root ;              // 是否為左節點              boolean isLeftChild = true ;                           while (current. data != value){                   parent = current;                    // 進行比較                    if (current. data > value){                          // 向左走                         current = current. leftChild ;                         isLeftChild = true ;                   } else {                          // 向左走                         current = current. rightChild ;                         isLeftChild = false ;                   }                    // 如果查詢不到                    if (current == null ){                          return false ;                   }             }                           // 找到對應的節點              if (current. leftChild == null && current. rightChild == null ){                    // 1,該節點沒有子節點,直接刪除葉子節點                    // 如果是根節點                    if (current == root ){                          root = null ;                   } else if (isLeftChild){                          // 如果該節點是其父節點的左子節點,將其父節點的左子節點置為null                         parent. leftChild = null ;                   } else {                          // 如果該節點是其父節點的右子節點,將其父節點的右子節點置為null                         parent. rightChild = null ;                   }             } else if (current. rightChild == null ){                    // 2,該節點沒有右子節點,直接將該節點的左子節點掛在其父節點的左/右節點上                    if (current == root ){                          root = current. leftChild ;                   } else if (isLeftChild){                          // 如果該節點是其父節點的左子節點,將該節點的左子節點掛在其父節點的左子節點上                         parent. leftChild = current. leftChild ;                   } else {                                // 如果該節點是其父節點的右子節點,將該節點的左子節點掛在其父節點的右子節點上                         parent. rightChild = current. leftChild ;                   }             } else if (current. leftChild == null ){                    // 3,該節點沒有左子節點,直接將該節點的右子節點掛在其父節點的左/右節點上                    if (current == root ){                          root = current. rightChild ;                   } else if (isLeftChild){                          // 如果該節點是其父節點的左子節點,將該節點的左子節點掛在其父節點的左子節點上                         parent. leftChild = current. rightChild ;                   } else {                          // 如果該節點是其父節點的右子節點,將該節點的右子節點掛在其父節點的右子節點上                         parent. rightChild = current. rightChild ;                   }             } else {                    // 4,該節點有左右子節點                    // 獲取替代被刪除節點的節點(即:找到該節點的中序後繼節點,並將該節點替代被刪除節點)                   Node successor = getSuccessor(current);                    if (current == root ){                       root = successor;                   } else if (isLeftChild){                          // 如果該節點是其父節點的左子節點                         parent. leftChild = successor;                   } else {                          // 如果該節點是其父節點的右子節點                         parent. rightChild = successor;                   }                    // 固定替代節點的位置(固定其在被刪除節點的位置,右邊位置已經固定,現在固定左邊)                   successor. leftChild = current. leftChild ;             }                           return true ;       }               /** 返回替代被刪除節點的節點物件        *  工作原理:        *  被刪除的有兩個孩子節點,這種情況最複雜,因為要考慮到刪除之後順序不能亂。        *    所以這種型別的節點要刪除,如果直接刪,真個樹的大小順序就亂了,所以需要考慮,        *    在樹中找到一個合適的節點來把這個節點給替換掉,用這種方法來保持整個數的穩定。        *    所以又一個問題又來了了,該找哪個節點來替換它?結論是,需要在樹中找出所有比        *    被刪除節點的值大的所有數,並在這些數中找出一個最小的數來。聽起來很拗,如果        *    把它用圖形來描述的話,就是,從被刪除的節點出發經過它的右節點,然後右節點最        *    左邊的葉子節點就是我們要找的,它有一個專業名詞叫中序後繼節點。下面專門來寫一個方法來找它:        */        public Node getSuccessor(Node delNode) {                           // 替代節點             Node successor = delNode;              // 替代節點的父節點             Node successorParent = delNode;              // 從該節點的出發經過它的右節點             Node current = delNode. rightChild ;                           //找到該節點右節點最左邊的葉子節點(就是我們要找的替代該節點的節點)              while (current != null ){                   successorParent = successor;                   successor = current;                   current = current. leftChild ;             }                           // 如果替代節點不為該節點的右節點              if (successor != delNode. rightChild ){                    // 替代節點的父節點的左節點=替代節點的右節點                   successorParent. leftChild = successor. rightChild ;                    // 替代節點(已經換到被刪除節點的位置上)的右節點=原來被刪除節點的右節點                   successor. rightChild = delNode. rightChild ;             }                           // 如果替代節點為該節點的右節點,直接返回              return successor ;       }                                                         //*************N--root,  L--left,  R--right****************//               /** 遍歷順序:
  •              前序遍歷:NLR
                      1.訪問根節點                       2.前序遍歷左子樹                       3.前序遍歷右子樹                   中序遍歷: LNR                       1.中序遍歷左子樹                       2.訪問根節點                       3.中序遍歷右子樹                   後序遍歷: LRN                       1.後序遍歷左子樹                       2.後序遍歷右子樹                       3.訪問根節點        */                     
   1 前序遍歷                       遍歷的順序為:ABDGHCEIF        /**        * 基本思想:                 1.訪問根節點                 2.前序遍歷左子樹                 3.前序遍歷右子樹        * 順序:NLR        * 前序遍歷(遞迴遍歷)        * @param localNode        */        public void frontOrder(Node localNode){              if (localNode != null ){                    // 訪問根節點                   System. out .println(localNode. data + ", " + localNode. sData );                    // 前序遍歷左子樹                   frontOrder(localNode. leftChild );                    // 前序遍歷右子樹                   frontOrder(localNode. rightChild );             }       }               /**        * 前序非遞迴遍歷:                   對於任一結點p:               a. 訪問結點p,並將結點p入棧;               b. 判斷結點p的左孩子是否為空,若為空,則取棧頂結點並進行出棧操作,                             並將棧頂結點的右孩子置為當前的結點p,迴圈置a;若不為空,則將p的左孩子置為當前結點p;               c. 直到p為空,並且棧為空,則遍歷結束。        * @param localNode        */        public void frontOrder2(Node localNode){                    }       

    2 中序遍歷                 遍歷的順序為:GDHBAEICF        /**        * 基本思想:                 1.中序遍歷左子樹                 2.訪問根節點                 3.中序遍歷右子樹        * 中序遍歷        * 順序:LNR        * @param localNode        */        public void inOrder(Node localNode){              if (localNode != null ){                    // 中序遍歷左子樹                   inOrder(localNode. leftChild );                    // 訪問根節點 &n