題注
《面試寶典》有相關習題,但思路相對不清晰,排版有錯誤,作者對此參考相關書籍和自己觀點進行了重寫,供大家參考。
接上一節第五部分,主要分析二叉樹的非遞歸周遊和二叉排序樹的操作。
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;
}
}
作者:銘毅天下
來源:CSDN
原文:
https://blog.csdn.net/laoyang360/article/details/7884449版權聲明:本文為部落客原創文章,轉載請附上博文連結!