資料結構面試之六——二叉樹的常見操作2(非遞迴遍曆&二叉排序樹)

來源:互聯網
上載者:User
資料結構面試之六——二叉樹的常見操作2 (非遞迴遍曆 & 二叉排序樹)

題註:《面試寶典》有相關習題,但思路相對不清晰,排版有錯誤,作者對此參考相關書籍和自己觀點進行了重寫,供大家參考。

六、二叉樹的基本操作(非遞迴遍曆)&二叉排序樹的操作

       接上一節第五部分,主要分析二叉樹的非遞迴遍曆和二叉排序樹的操作。

1.      非遞迴中序遍曆

//1.依次將根節點root的左子樹入棧,直到lchild=NULL,執行2

//2.將棧的元素出棧、訪問;將當前指標指向節點的rchild,迴圈遍曆。直到棧空為止!

      

 template<typenameelemType>       voidbinaryTreeType<elemType>::noRecursionInorderTraversal()                      //非遞迴中序遍曆       {              cout<< "noRecursionInorderTraversal--------------------------->"<< endl;              linkedStackType<nodeType<elemType>* > stack;              nodeType<elemType>*current = root;              while(current!= NULL || !stack.isEmptyStack())  //或者||              {                     if(current!= NULL)                     {                            stack.push(current);                            current= current->llink;                     }                     else                     {                            stack.pop(current);                            cout<< current->info << "\t"; //出棧的時候訪問節點                            current= current->rlink;                     }              }              cout<< endl;              cout<< "<------------------------noRecursionInorderTraversal"<< endl;       }

2.      非遞迴先序遍曆

       //在中序遍曆的基礎上,訪問次序發生變化;

       //先序遍曆,需要先逐個遍曆根節點,然後依次處理其左、右孩子節點。

     

  template<typenameelemType>       voidbinaryTreeType<elemType>::noRecursionPreorderTraversal()                     //非遞迴前序走訪       {              cout<<"noRecursionPreorderTraversal--------------------------->"<< endl;              linkedStackType<nodeType<elemType>* > stack;              nodeType<elemType>*current = root;              while(current!= NULL || !stack.isEmptyStack())  //或者||              {                     if(current!= NULL)                     {                            cout<< current->info << "\t";   //先訪問節點後入棧                            stack.push(current);                            current= current->llink;                     }                     else                     {                            stack.pop(current);                            current= current->rlink;                     }              }              cout<< endl;              cout<< "<------------------------noRecursionPreorderTraversal"<< endl;       }

3.      非遞迴後序遍曆

由於訪問的順序為先左子樹、然後右子樹,最後根節點。並且對於每一個節點都是上述操作,所以,對於遍曆來講,需要識別當前節點類型是根(相對)、左孩子節點 、右孩子節點。故,我們設定了flag標記變數,flag=0初始標記,節點尚未入棧;在訪問左孩子之前將flag置為1;在訪問右孩子之前將flag置為2;並且在訪問右孩子之後,將flag置為0。

       //後序非遞迴遍曆比較複雜..

     

  template<typenameelemType>voidbinaryTreeType<elemType>::noRecursionPostorderTraversal()                    //非遞迴後序遍曆       {              cout<<"noRecursionPostorderTraversal--------------------------->"<< endl;              linkedStackType<nodeType<elemType>* > stack;              linkedStackType<int>intStack;                       //標記位同步棧.              nodeType<elemType>*current = root;              intnflag = 0;                                      //初始標記為0.              if(current== NULL)              {                     cout<< "The Stack is Empty!" << endl;              }              else              {                     //1.將前端節點先入棧,                     stack.push(current);                     intStack.push(1);               current = current->llink;        //注意此處需要調整指向******                     while(!stack.isEmptyStack()&& !intStack.isEmptyStack())                              {                            if(current!= NULL && nflag == 0)                                                                 {                               stack.push(current);                                   intStack.push(1);   //標記位為1,[在訪問左孩子之前,將其值置為1]。                               current = current->llink;                            }                            else                            {                                   stack.pop(current);                                   intStack.pop(nflag);    //此時的標記位為傳回值,需要根據其做判斷                                   if(nflag== 1)         //說明下一步需要入棧的為右孩子.                                   {                                          stack.push(current);   //繼續將該節點入棧,                                                                                                       intStack.push(2);      //但[在訪問右孩子之前,將其置為2]。                                          current= current->rlink;           //訪問右節點,                                          nflag= 0;                                  //置標記位為0                                   }                                   else                                   {                                          cout<< current->info << " ";  //待左右子樹都為空白再訪問節點。                                   }                            }                     }                     cout<< endl;                     cout<< "<------------------------noRecursionPostorderTraversal"<< endl;              }           }

 

4.      二叉排序樹的搜尋操作

明確概念,國內、國外的著作裡提及的下三個概念等價,二叉搜尋樹=二叉尋找樹=二叉排序樹。

//二叉排序樹的尋找存在以下幾種情況:

//1.鏈表為空白,提示並返回;

//2.鏈表非空,需要迴圈尋找直到指標為空白,若存在,則bfound=true;否則尋找至最後bfound=預設false。

template <class elemType>boolbSearchTreeType<elemType>::search(const elemType& searchItem){       nodeType<elemType>*current = new nodeType<elemType>;       boolbFound = false;        if(root== NULL)       {              cout<< "The bSearchTree is NULL\n";       //case1: 鏈表為空白!              returnfalse;       }       else       {              current= root;              while(current!= NULL && !bFound) //case2:在鏈表中尋找,根據大小鎖定左、右子樹.              {                     if(current->info== searchItem)                     {                            bFound= true;                     }                     elseif(current->info > searchItem)                     {                            current= current->llink;              //左子樹                     }                     elseif(current->info < searchItem)                     {                            current= current->rlink;             //右子樹                     }              }       }        returnbFound;}

5.      二叉排序樹的插入存在以下幾種情況:

//1.鏈表為空白,插入元素即為根節點;

//2.鏈表非空,需要尋找插入位置後插入。

//2.1插入元素已經存在,則提示出錯。

//2.2總能找到大於或小於某節點的位置,記錄trailcurrent完成插入操作。

template <class elemType>voidbSearchTreeType<elemType>::insert(const elemType& insertItem){       nodeType<elemType>*newNode = new nodeType<elemType>;       nodeType<elemType>*current;       nodeType<elemType>*trailCurrent;        newNode->info= insertItem;       newNode->llink= NULL;       newNode->rlink= NULL;        if(root== NULL)       {              root= newNode;                                //case1:樹為空白.       }       else       {              current= root;              while(current!= NULL)                          //case2,3,4搜尋才知道!              {                     trailCurrent= current;                     if(current->info== insertItem)                     {                            cout<< "the elem is already exist!\n";  //case2:元素已經存在                            return;                     }                     else                     {                            if(current->info> insertItem)                            {                                   current= current->llink;           //case3:鎖定左側位置...                            }                            else                            {                                   current= current->rlink;           //case4:鎖定右側位置...                            }                     }              }//endwhile               //case3,4根據大小進行連結              if(trailCurrent->info< insertItem)                         {                     trailCurrent->rlink= newNode;              }              else              {                     trailCurrent->llink= newNode;              }        }//end else}

6.      二叉排序樹的刪除存在以下幾種情況【此處可能複雜些】:

//刪除一個節點,要首先判斷元素值在二叉排序樹中是否存在,

//若不存在則返回;

//若存在則需要鎖定其對應位置為1根節點;2分葉節點;3其餘節點。

//根據要刪除的節點是否含有左右子樹的不同,分為4種情況考慮,

//見deleteFromTree()函數。

template <class elemType>voidbSearchTreeType<elemType>::deleteNode(const elemType& deleteItem){       //1.尋找節點       //2.1找不到,不存在;       //2.2找到,刪除,調用函數       nodeType<elemType>*current;       nodeType<elemType>*trailCurrent;       boolbFound = false;        if(root== NULL)       {              cout<< "Can't delete an Empty BST" << endl;              return;       }       else       {              current= root;              trailCurrent= root;              while(current != NULL && !bFound)              {                     if(current->info== deleteItem)                     {                            bFound= true;                     }                     elseif(current->info > deleteItem)                     {                            trailCurrent= current;                            current= current->llink;    //左                     }                     else                     {                            trailCurrent= current;                            current= current->rlink;   //右                     }              }//endwhile               if(current== NULL)              {                     cout<< deleteItem << " is not Exist in the BST!\n" <<endl;              }              elseif(bFound)              {                     if(current== root)                     {                            deleteFromTree(root);                  //可能是根節點                     }                     elseif(trailCurrent->info > deleteItem)                     {                            deleteFromTree(trailCurrent->llink);//左半分支,調整trailCurrent的指向                     }                     elseif(trailCurrent->info < deleteItem)                     {                            deleteFromTree(trailCurrent->rlink);  //右半分支,調整trailCurrent的指向                     }              }//endif bFound         }//end else}

//[原理]:某節點的前驅是該節點左子樹的最右端的節點(中序遍曆的結果)

template <class elemType>voidbSearchTreeType<elemType>::deleteFromTree(nodeType<elemType>*&p){       nodeType<elemType>*temp;       nodeType<elemType>*current;       nodeType<elemType>*trailCurrent;        if(p== NULL)       {              cout<< "The BST is NULL!" << endl;              return;       }       if(p->llink== NULL && p->rlink == NULL)      //情況1,左右節點都為空白(分葉節點)       {              temp= p;              p= NULL;              deletetemp;       }       elseif( p->rlink == NULL)                     //情況2,右子樹為空白,左非空       {              temp= p;              p= temp->llink;              deletetemp;       }       elseif(p->llink == NULL)                      //情況3,左子樹為空白,右非空       {              temp= p;              p= temp->rlink;              deletetemp;       }       else                           //情況4,左右都非空[用中序遍曆的前一個節點替換]       {              current= p->llink;              trailCurrent= NULL;               while(current->rlink!= NULL)              {                     trailCurrent= current;   //trailCurrent最終指向準備刪除節點的前一個節點                     current= current->rlink;              }               p->info= current->info;                //資訊賦值               if(trailCurrent== NULL)              //僅一個左孩子節點              {                     p->rlink = current->llink;                       }              else              {                     trailCurrent->rlink= current->llink; //給刪除前點的前面一個節點調整指標指向              }              deletecurrent;       } }

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.