1. 程式人生 > >C語言強化(十一)二叉樹映象變化 | 要求:不使用遞迴

C語言強化(十一)二叉樹映象變化 | 要求:不使用遞迴

用了這麼久的遞迴,現在不讓用遞迴了,你行麼?

通過這道題,你可以學會

  • 如何映象變化一棵二叉樹
  • 什麼是遞迴的本質
  • 如何巧妙地使用輔助棧

題目:
輸入一顆二元查詢樹,將該樹轉換為它的映象,
即在轉換後的二元查詢樹中,左子樹的結點都大於右子樹的結點。
要求:
不使用遞迴

例如輸入:


輸出:


將二叉樹映象變化的方法很簡單,只要把所有節點的左右節點對調就行了,使用遞迴可以非常容易的實現。

如下為遞迴方法實現映象變化的函式

/**
二叉樹映象變化——遞迴方法實現
*/
void Revertsetree_Recursion(BTreeNode *root)
{
	if(!root)
		return;
	BTreeNode *p;
	p=root->leftNode;
	root->leftNode=root->rightNode;
	root->rightNode=p;
	if(root->leftNode)
		Revertsetree_Recursion(root->leftNode);
	if(root->rightNode)
		Revertsetree_Recursion(root->rightNode);
}

現在問題來了,不使用遞迴,怎麼辦?

思路:

遞迴的本質是儲存元素為函式的棧,

所以完成同樣任務時最簡單的辦法就是用一個輔助棧來模擬遞迴,輔之以迴圈語句

  • 首先,把樹的頭結點放入棧中
  • 在迴圈中,只要棧不為空,彈出棧的棧頂結點,交換它的左右子樹
  • 如果它有左子樹,把它的左子樹壓入棧中
  • 如果它有右子樹,把它的右子樹壓入棧中
  • 在下次迴圈中交換它兒子結點的左右子樹

如下為使用迴圈語句實現映象變化的函式

/**
二叉樹映象變化——迴圈方法實現
*/
void Revertsetree_Circle(BTreeNode *phead)
{
	if(!phead)
		return;
	stack<BTreeNode*> stacklist;
	stacklist.push(phead); //首先把樹的頭結點放入棧中。
	while(stacklist.size())
		//在迴圈中,只要棧不為空,彈出棧的棧頂結點,交換它的左右子樹
	{
		BTreeNode* pnode=stacklist.top();
		stacklist.pop();
		BTreeNode *ptemp;
		ptemp=pnode->leftNode;
		pnode->leftNode=pnode->rightNode;
		pnode->rightNode=ptemp;
		if(pnode->leftNode)
			stacklist.push(pnode->leftNode); //若有左子樹,把它的左子樹壓入棧中
		if(pnode->rightNode)
			stacklist.push(pnode->rightNode); //若有右子樹,把它的右子樹壓入棧中
	}
}

原始碼
/**
題目:
輸入一顆二元查詢樹,將該樹轉換為它的映象,
即在轉換後的二元查詢樹中,左子樹的結點都大於右子樹的結點。
要求:
不使用用遞迴

思路
把所有節點的左右節點對調
使用遞迴可以很簡單的實現

由於遞迴的本質是編譯器生成了一個函式呼叫的棧,
因此用迴圈來完成同樣任務時最簡單的辦法就是用一個輔助棧來模擬遞迴

首先我們把樹的頭結點放入棧中。
在迴圈中,只要棧不為空,彈出棧的棧頂結點,交換它的左右子樹。
如果它有左子樹,把它的左子樹壓入棧中;
如果它有右子樹,把它的右子樹壓入棧中。
這樣在下次迴圈中就能交換它兒子結點的左右子樹了
*/
#include<stdlib.h>
#include <iostream>
#include<sstream>
#include <vector>
#include<stack>

using namespace std;

//建立二叉樹結構體
struct BTreeNode{
	int value;
	BTreeNode *leftNode;
	BTreeNode *rightNode;
};

/*
二叉樹建構函式
按照二叉排序的演算法插入(左小右大)
node	二叉樹的根結點
value	要插入的結點的值 
*/
void insertToBTree(BTreeNode * &node,int value){
	//結點為空,插入結點為根結點
	if(NULL==node){
		BTreeNode * btNode=new BTreeNode();
		btNode->value=value;
		btNode->leftNode=NULL;
		btNode->leftNode=NULL;
		node=btNode;
	}else{//結點非空
		//值小於根結點,遞迴左結點
		if((node->value)>value)
			insertToBTree(node->leftNode,value);
		//值大於根結點,遞迴右結點
		else if(value>(node->value))
			insertToBTree(node->rightNode,value);
		//值與已有結點值相等,提示錯誤
		else
			printf("結點已經存在,不可以再次插入!");
	}
}

/*
前序遍歷二叉排序樹
btNode  二叉樹根結點
*/
void goThroughBTree(BTreeNode * btNode){
	//結點為空,返回
	if(NULL==btNode)
		return;
	cout<<btNode->value<<endl;
	//左結點不為空,繼續向左深入
	if(NULL!=btNode->leftNode)
		goThroughBTree(btNode->leftNode);
	//右不為空,向右深入
	if(NULL!=btNode->rightNode){
		goThroughBTree(btNode->rightNode);
	}
}

/**
二叉樹映象變化——遞迴方法實現
*/
void Revertsetree_Recursion(BTreeNode *root)
{
	if(!root)
		return;
	BTreeNode *p;
	p=root->leftNode;
	root->leftNode=root->rightNode;
	root->rightNode=p;
	if(root->leftNode)
		Revertsetree_Recursion(root->leftNode);
	if(root->rightNode)
		Revertsetree_Recursion(root->rightNode);
}

/**
二叉樹映象變化——迴圈方法實現
*/
void Revertsetree_Circle(BTreeNode *phead)
{
	if(!phead)
		return;
	stack<BTreeNode*> stacklist;
	stacklist.push(phead); //首先把樹的頭結點放入棧中。
	while(stacklist.size())
		//在迴圈中,只要棧不為空,彈出棧的棧頂結點,交換它的左右子樹
	{
		BTreeNode* pnode=stacklist.top();
		stacklist.pop();
		BTreeNode *ptemp;
		ptemp=pnode->leftNode;
		pnode->leftNode=pnode->rightNode;
		pnode->rightNode=ptemp;
		if(pnode->leftNode)
			stacklist.push(pnode->leftNode); //若有左子樹,把它的左子樹壓入棧中
		if(pnode->rightNode)
			stacklist.push(pnode->rightNode); //若有右子樹,把它的右子樹壓入棧中
	}
}

void main()
{
	BTreeNode *root=NULL;//二叉樹根結點

	//建立二叉排序樹
	insertToBTree(root,8);
	insertToBTree(root,6);
	insertToBTree(root,10);
	insertToBTree(root,5);
	insertToBTree(root,7);
	insertToBTree(root,9);
	insertToBTree(root,11);

	//遍歷二叉樹
	cout<<"原二叉樹:"<<endl;
	goThroughBTree(root);

	//Revertsetree_Recursion(root);
	Revertsetree_Circle(root);

	cout<<"映象二叉樹:"<<endl;
	goThroughBTree(root);
	system("pause");
}

執行圖


此次再次使用,相信同學們會對它有更深理解體會。

總結

遞迴的本質是一個儲存了函式的棧