1. 程式人生 > >二叉樹遍歷(迴圈和遞迴)

二叉樹遍歷(迴圈和遞迴)

遞迴

1.前序遍歷

void preorder(BinTree *T)
{
    if(T==NULL)
        return;
    cout << T->data;
    preorder(T->left);
    preorder(T->right);
}

2.中序遍歷

void inoeder(BinTree *T)
{
    if(T == NULL)
        return;
    inoeder(T->left);
    cout << T->data;
    inoeder(T->right);
}

3.後序遍歷

void postorder(BinTree *T)
{
    if (T==NULL)
        return;
    postorder(T->left);
    postorder(T->right);
    cout << T->data;
}


迴圈

1.前序遍歷 法一

void preorder(BinTree *T)
{
    //空樹,直接返回
    if(T==NULL)
        return;
    //定義一個存放二叉樹節點的棧結構
    stack<BinTree*>s;
    BinTree *pcur; 
    pcur = T;
    //迴圈遍歷二叉樹直到根節點為空或者棧為空
    while (pcur || !s.empty())
    {
        if (pcur)
        {
            cout << pcur->data;
            s.push(pcur);
            pcur = pcur->left;
        }else
        {
            //當根節點為空時,說明左子樹已遍歷完,通過取出棧頂結點,來訪問根節點的右子樹
            pcur = s.top();
            s.pop();
            pcur = pcur->right;
        }
    }
}

法二(自己)

void preOrder(binTree * root)
{
	if(root==NULL)
	return;
	
	stack<binTree*> stackTree;
	binTree* curTree;
	
	stackTree.push(root);
	
	while(!stackTree.empty())
	{
		curTree=stackTree.top();
		stackTree.pop();
		cout<<curTree->value;
		
		if(curTree->right!=NULL)
			stackTree.push(curTree->right);
		if(curTree->left!=NULL)
			stackTree.push(curTree->left);
	}
}

2.中序遍歷

void inorder(BinTree *T)
{
    if(T == NULL)
        return;
    stack<BinTree*>s;
    BinTree *pcur;
    pcur = T;
    while (pcur || !s.empty())
    {
        if (pcur)
        {
            //根節點非空,入棧,繼續遍歷左子樹直到根節點為空
            s.push(pcur);
            pcur = pcur->left;
        }else
        {
            //根節點為空,說明左子樹已經遍歷完,彈出根節點列印,通過根節點訪問右子樹
            pcur = s.top();
            s.pop();
            cout << pcur->data;
            pcur = pcur->right;            
        }
    }
}

3.後序遍歷

法一,會改變原來的內容

void postorder2(BinTree *T)
{
    if(T == NULL)
        return;
    stack<BinTree*>s;
    s.push(T);
    BinTree *pcur;
    pcur = NULL;
    while (!s.empty())
    {
        pcur = s.top();
        if (pcur->left == NULL && pcur->right == NULL)
        {
            cout << pcur->data;
            s.pop();
        }else
        {
            //注意右孩子先入棧左孩子後入棧,這樣再次迴圈時左孩子節點才先出棧
            if(pcur->right)
            {
                s.push(pcur->right);
                pcur->right = NULL;
            }
            if (pcur->left)
            {
                s.push(pcur->left);
                pcur->left = NULL;
            }    
        }
    }
}

法二,不用輔助棧,不改變內容

void PostOrderWithoutRecursion(BTNode* root)
{
    if (root == NULL)
        return;
    stack<btnode*> s;
    //pCur:當前訪問節點,pLastVisit:上次訪問節點
    BTNode* pCur, *pLastVisit;
    pCur = root;
    pLastVisit = NULL;
    //先把pCur移動到左子樹最下邊
    while (pCur)
    {
        s.push(pCur);
        pCur = pCur->lchild;
    }
    while (!s.empty())
    {
        //走到這裡,pCur都是空,並已經遍歷到左子樹底端(看成擴充二叉樹,則空,亦是某棵樹的左孩子)
        pCur = s.top();
        s.pop();
        //一個根節點被訪問的前提是:無右子樹或右子樹已被訪問過
        if (pCur->rchild == NULL || pCur->rchild == pLastVisit)
        {
            cout << pCur->data;
            //修改最近被訪問的節點
            pLastVisit = pCur;
        }
        /*這裡的else語句可換成帶條件的else if:
        else if (pCur->lchild == pLastVisit)//若左子樹剛被訪問過,則需先進入右子樹(根節點需再次入棧)
        因為:上面的條件沒通過就一定是下面的條件滿足。仔細想想!
        */
        else
        {
            //根節點再次入棧
            s.push(pCur);
            //進入右子樹,且可肯定右子樹一定不為空
            pCur = pCur->rchild;
            while (pCur)
            {
                s.push(pCur);
                pCur = pCur->lchild;
            }
        }
    }
    cout << endl;
}
法三。不用輔助棧,不改變內容,推薦
void beh_traverse(BTree pTree)  
{  
    PSTACK stack = create_stack();  //建立一個空棧  
    BTree node_pop;          //用來儲存出棧的節點  
    BTree pCur;              //定義指標,指向當前節點  
    BTree pPre = NULL;       //定義指標,指向上一各訪問的節點  
  
    //先將樹的根節點入棧  
    push_stack(stack,pTree);    
    //直到棧空時,結束迴圈  
    while(!is_empty(stack))  
    {  
        pCur = getTop(stack);   //當前節點置為棧頂節點  
        if((pCur->pLchild==NULL && pCur->pRchild==NULL) ||   
            (pPre!=NULL && (pCur->pLchild==pPre || pCur->pRchild==pPre)))  
        {  
            //如果當前節點沒有左右孩子,或者有左孩子或有孩子,但已經被訪問輸出,  
            //則直接輸出該節點,將其出棧,將其設為上一個訪問的節點  
            printf("%c ", pCur->data);  
            pop_stack(stack,&node_pop);  
            pPre = pCur;  
        }  
        else  
        {  
            //如果不滿足上面兩種情況,則將其右孩子左孩子依次入棧  
            if(pCur->pRchild != NULL)  
                push_stack(stack,pCur->pRchild);  
            if(pCur->pLchild != NULL)  
                push_stack(stack,pCur->pLchild);  
        }  
    }  
}  

4.層次遍歷

void leveltraversal(BinTree *T)
{
    queue<BinTree*> s;
    s.push(T);
    BinTree* pcur;
    while (!s.empty())
    {
        pcur=s.front();
        cout<<pcur->data;
        s.pop();
        if (pcur->left)
        {
            s.push(pcur->left);
        }
        if (pcur->right)
        {
            s.push(pcur->right);
        }
    }    
}
分層遍歷二叉樹,即從上到下按層次訪問該樹,每一層單獨輸出一行
void leveltraversal3(BinTree *T)
{
    if(T == NULL)
        return;
    queue<BinTree*>s;
    s.push(T);
    BinTree *pcur,*last,*nlast;
    last = T;
    nlast = NULL;
    while (!s.empty())
    {
        pcur = s.front();
        cout << pcur->data;
        s.pop();        
        if (pcur->left)
        {
            s.push(pcur->left);
            nlast = pcur->left;
        }
        if (pcur->right)
        {
            s.push(pcur->right);
            nlast = pcur->right;
        }
        if (last == pcur)
        {
            cout << endl;
            last = nlast;
        }            
    }
}