1. 程式人生 > >二叉樹的深度優先dfs遍歷(前序、中序和後序;遞迴與非遞迴)

二叉樹的深度優先dfs遍歷(前序、中序和後序;遞迴與非遞迴)

//前序遍歷
//遞迴實現:根左右
void preOrder1(BinTree *root)
{
    if (root != NULL)
    {
        cout<<root->data<<endl;
        preOrder1(root->lchild);
        preOrder1(root->rchild);
    }
}

//非遞迴實現
/*注意,如前所述,我們將二叉樹的每一個結點都看作根結點。因此,從整個二叉樹的根結點root出發,一路向左,遇到的每一個結點都立即訪問(它是根,同時也是其父親的左子樹的根,所以這個過程訪問了“根和左”)併入棧,直到不能再左,轉向右(這個“右”上哪找呢?當然是父親的右兒子。父親去哪裡找呢?當然是棧裡),將這個右兒子當成新的根結點重複上述過程,直到棧為空且當前根結點也為空。*/
void preOrder2(BinTree *root)
{
	stack<BinTree *> s;
	BinTree *p=root;
 	while (p!=NULL || !s.empty())
 	{
  		//一路向左
  		while (p!=NULL)
		{      
			cout<<p->data<<endl;	 //訪問根結點
			s.push(p);
   			p=p->lchild;
		}
 
		//當不能再左時,開始向右
  		if (!s.empty())
  		{
   			p=s.top();//從棧裡面取出根結點
   			s.pop();
   			p=p->rchild;            //作為新的根結點
  		}
 	}
 }

//中序遍歷
//遞迴實現:左根右
void inOrder1(BinTree *root)
{
	if (root != NULL)
	{
  		inOrder1(root->lchild);
  		cout<<root->data<<endl;
  		inOrder1(root->rchild);
 	}
}

//中序遍歷 
//非遞迴實現
/*與前序遍歷類似,但是,根結點進棧時不訪問(否則就成了前序遍歷),根結點彈棧時才訪問(左根右)。*/
void inOrder2(BinTree *root)
{
	stack<BinTree *> s;
	BinTree *p=root;
 
	while (p!=NULL || !s.empty())
	{
  		//一路向左
 		while (p!=NULL)
 		{
 			s.push(p);
 			p=p->lchild;
 		}
 
  		//當不能再左時,訪問根結點,向右
  		if (!s.empty())
  		{
   			p=s.top();
   			cout<<p->data<<endl;	//在中間訪問根結點
   			s.pop();
   			p=p->rchild;
  		}
	}
}

//後序遍歷
//遞迴實現:左右根
void postOrder1(BinTree *root)
{
	if (root != NULL)
	{
		postOrder1(root->lchild);
		postOrder1(root->rchild);
		cout<<p->data<<endl;
	}
}
//順便說一句,刪除一棵二叉樹,即釋放一棵二叉樹的記憶體,用後續遍歷即可實現(這裡的“訪問”變成了delete 結點)。

//後序遍歷
//非遞迴實現
/*對任一結點p,邊一路向左邊進棧,直到其左兒子為空,這時,各個根結點在棧裡存放。然後依次出棧,但是此時還不能訪問(否則就是中序遍歷了),出棧之後以當前根節點的右兒子設定為新的根節點,重複前述過程;直到當前根節點第二次出棧(說明它的左右兒子都已被訪問),然後訪問此根結點(左右根)。*/
typedef struct _poNode
{
	BinTree *btnode;
	bool isFirst;
} poNode;

void postOrder2(BinTree *root)
{
	stack<poNode *> s;
	BinTree *p=root;
 
	poNode *temp;
 
	while (p!=NULL || !s.empty())
	{
 		//一路向左直到不能再左
		while (p!=NULL)
		{
			temp = (poNode *)malloc(sizeof(poNode));
			temp->btnode = p;
			temp->isFirst = True;	 //第一次進棧標記
			s.push(temp);
 
			p=p->lchild;
		}
 
		if (!s.empty())
		{
			temp = s.top();	 //此時還不能訪問,否則就是中序遍歷了
			s.pop();
 
			//如果是第一次進棧,那還需要再進棧一次,之後以它的右兒子為新的根結點
			if (temp->isFirst == true)
			{
				temp->isFirst = false;
				s.push(temp);
				p = temp->btnode->rchild;
 			}
			else
			{
				cout<<temp->btnode->data<<endl;	//後序訪問根結點
				p = NULL;	//不要忘了這一句,因為訪問過根結點之後應該直接彈棧考察上一個父結點
			}
 		}
 	}
 }
總結:所有結點都看作根結點,關鍵在於何時訪問。前序:入棧時訪問;中序:第一次退棧時訪問;後序:第二次退棧時訪問。