非遞歸遍歷二叉樹的實現

參考:http://blog.csdn.net/kofsky/article/details/2886453/
#pragma once
#define NUM_NODE  8
#include<iostream>
#include<stack>
#include<ctime>
#include<assert.h>
class binnode {
public:
	int key;
	char value;
	binnode  *left, *right,*parent;
	bool beVisited;          //用於中序遍歷的指針回溯版本
	bool bePushed;           //用於中序遍歷的
	binnode(){}
	binnode(int k, char v) :key(k), value(v) { left = right = parent = nullptr; beVisited = false; bePushed = false; }
	binnode(const binnode& bn) {
		key = bn.key;
		value = bn.value;
		left = right = parent = nullptr;
		beVisited = false;
		bePushed = false;
	}
};
class bintree {
public:
	bintree();
	bool insert(const binnode& bn);
	void inorder();
	void postorder();
	void preorder();
	void menu();
protected:
	void inorder(binnode * bn);             //遞歸前序遍歷
	void _inorder();                        //非遞歸前序遍歷
	void __inorder(binnode * bn);
	void postorder(binnode * bn);
	void _postorder();
	void preorder(binnode * bn);
	void _preorder();
	void __preorder(binnode * bn);
private:
	void visit(binnode *bn);
	binnode *root;
	binnode *NIL;
};
#include"bintree.h"
bintree::bintree() {
	root = nullptr;
	NIL = nullptr;
}
bool bintree::insert(const binnode& bn) {
	binnode *tmp = root;                         //指向插入位置的指針
	binnode *parent_tmp = nullptr;               //插入位置的父節點

	while (tmp != NIL) {
		parent_tmp = tmp;                        //先賦值父節點
		if (bn.key <= tmp->key) {
			tmp = tmp->left;                     //指針指向子節點
		}
		else
			tmp = tmp->right;
	}
	tmp = new binnode(bn);                       //產生插入的節點
	if (tmp == nullptr) { std::cout << "allocated error!.\n"; return false; }
	tmp->parent = parent_tmp;                    //賦值子節點的父指針
	if (tmp->parent == NIL) {                    //若父節點爲空,則當前插入的節點是根節點
		root = tmp;                              //賦值根節點
		return true;
	}
	if (tmp->key <= tmp->parent->key) {          //判斷插入的位置是父節點的左子女還是右子女
		tmp->parent->left = tmp;
	}
	else
		tmp->parent->right = tmp;
	return true;
}
void bintree::visit(binnode *bn) {
	std::cout << bn->value << " ";
/*	if (bn->parent != nullptr) {
		std::cout << bn->value << "'s parent is " << bn->parent->value << " ";
	}*/
}
void bintree::menu() {
	int select;
	std::cout << "1.前序遍歷. \t 2.中序遍歷. \t 3.後序遍歷. \t 4.退出\n";
	std::cin >> select;
	while (select != 1 && select != 2 && select != 3 && select != 4) {
		std::cout << "輸入錯誤,請重新輸入!";
		std::cin >> select;
	}
	switch (select)
	{
	case 1:preorder(); break;
	case 2:inorder(); break;
	case 3:postorder(); break;
	case 4:exit(0); break;
	default:
		break;
	}
}

int main() {
	bintree bt;
	int key[NUM_NODE] = {4,2,3,1,5,6,7};
	char value[NUM_NODE] = {'A','B','C','D','E','F','G','\0'};
	binnode bn[NUM_NODE];
	/*創建二叉查找樹*/
	for (int i = 0; i < NUM_NODE; ++i) {
		bn[i].key = key[i];
		bn[i].value = value[i];
		bn[i].left = bn[i].right = bn[i].parent = nullptr;
		bn[i].beVisited = false;
		bn[i].bePushed = false;
	}
	for (int i = 0; i < NUM_NODE; ++i) {
		std::cout << bn[i].value << " ";
	}
	std::cout << std::endl;
	for (int i = 0; i < NUM_NODE; ++i) {
		bt.insert(bn[i]);
	}
	/*測試遍歷*/
	bt.menu();
	return 0;
}
#include"bintree.h"
void bintree::inorder() {
	int select;
	std::cout << "1.遞歸中序遍歷. \t2.非遞歸中序遍歷.\t 3.非遞歸中序遍歷2 \t 4.返回主菜單\n";
	std::cin >> select;
	while (select != 1 && select != 2 && select != 3 && select != 4) {
		std::cout << "輸入錯誤,請重新輸入!" << std::endl;
		std::cin >> select;
	}
	switch (select) {
	case 1:inorder(root); std::cout << std::endl; inorder(); break;
	case 2:_inorder(); std::cout << std::endl; inorder(); break;
	case 3:__inorder(root); std::cout << std::endl; inorder(); break;
	case 4:menu(); break;
		
	}
}
void bintree::inorder(binnode * bn) {            //遞歸中序遍歷
	if (bn != nullptr) {
		inorder(bn->left);
		visit(bn);
		inorder(bn->right);
	}
}
/*
	// 中序遍歷僞代碼:非遞歸版本,用棧實現,版本1
void InOrder1(TNode* root)
{
    Stack S;
    while ( root != NULL || !S.empty() )
    {
        while( root != NULL )   // 左子樹入棧
        {
            S.push(root);
            root = root->left;
        }
        if ( !S.empty() )
        {
            root = S.pop();
            Visit(root->data);   // 訪問根結點
            root = root->right;  // 通過下一次循環實現右子樹遍歷
        }
    }
}
*/
void bintree::_inorder() {                       //非遞歸中序遍歷
	std::stack<binnode*> s;
	binnode *p = root;
	while (p != NULL || !s.empty())              //循環結束條件:當節點爲空且棧爲空結束循環
	{
		while (p != NULL)                        //遍歷左子樹並進棧,直到左子樹爲空
		{
			s.push(p);
			p = p->left;
		}
		if (!s.empty())                          //若棧不爲空則出棧,打印並進入節點的右子樹
		{
			p = s.top();
			visit(p);
			s.pop();
			p = p->right;
		}
	}
}
/*
	// 中序遍歷僞代碼:非遞歸版本,不用棧,增加指向父節點的指針
void InOrder3(TNode* root)
{
    while ( root != NULL ) // 回溯到根節點時爲NULL,退出
    {
        while ( root->left != NULL && !root->left->bVisited )
        {                  // 沿左子樹向下搜索當前子樹尚未訪問的最左節點           
            root = root->left;
        }
        if ( !root->bVisited )
        {                  // 訪問尚未訪問的最左節點
            Visit(root);
            root->bVisited=true;
        }
        if ( root->right != NULL && !root->right->bVisited )
        {                  // 遍歷當前節點的右子樹  
            root = root->right;
        }
        else
        {                 // 回溯至父節點
            root = root->parent;
        }
    }
}
*/
void bintree::__inorder(binnode * bn) {
	binnode * p = bn;
	while (p != nullptr) {
		while (p->left != nullptr && !p->left->beVisited) {
			p = p->left;
		}
		if (!p->beVisited) {
			visit(p);
			p->beVisited = true;
		}
		if (p->right != nullptr && !p->right->beVisited) {
			p = p->right;
		}
		else {
			p = p->parent;
		}
	}
}
/*
	// 中序遍歷僞代碼:非遞歸版本,用棧實現,版本2
void InOrder2(TNode* root)
{
    Stack S;
    if( root != NULL )
    {
        S.push(root);
    }
    while ( !S.empty() )
    {
        TNode* node = S.pop(); 
        if ( node->bPushed )
        {   // 如果標識位爲true,則表示其左右子樹都已經入棧,那麼現在就需要訪問該節點了
            Visit(node);        
        }
        else
        {   // 左右子樹尚未入棧,則依次將 右節點,根節點,左節點 入棧
            if ( node->right != NULL )
            {
                node->right->bPushed = false; // 左右子樹均設置爲false
                S.push(node->right);
            }
            node->bPushed = true;  // 根節點標誌位爲true
            S.push(node);
            if ( node->left != NULL )
            {
                node->left->bPushed = false;
                S.push(node->left);
            }
        }
    }
}
*/
#include"bintree.h"
void bintree::preorder() {
	int select;
	binnode * p = root;
	std::cout << "1.遞歸前序遍歷. \t2.非遞歸前序遍歷.\t 3.非遞歸遍歷2. \t 4.返回主菜單\n";
	std::cin >> select;
	while (select != 1 && select != 2 && select != 3 && select != 4) {
		std::cout << "輸入錯誤,請重新輸入!" << std::endl;
		std::cin >> select;
	}
	switch (select) {
	case 1:preorder(p); std::cout << std::endl; preorder(); break;
	case 2:_preorder(); std::cout << std::endl; preorder(); break;
	case 3:__preorder(root); break;
	case 4:menu(); break;
	}
}
void bintree::preorder(binnode * bn) {
	if (bn == nullptr)return;
	std::cout << bn->value << " ";
	preorder(bn->left);
	preorder(bn->right);
}
void bintree::_preorder() {
	binnode * p = root;
	std::stack<binnode*> s;
	p = root;
	assert(s.empty());
	while (p != nullptr || !s.empty()) {
		if (p != nullptr) {
			visit(p);
			s.push(p);
			p = p->left;
		}
		else {
			p = s.top();
			s.pop();
			p = p->right;
		}
	}
}
void bintree::__preorder(binnode * bn) {
	binnode * p = bn;
	std::stack<binnode*> s;
	s.push(p);
	while (!s.empty()) {
		p = s.top();
		s.pop();
		if (p != nullptr) {           //入棧的時候可能有空指針,只有非空指針才能進行操作
			visit(p);
			s.push(p->right);
			s.push(p->left);
		}
	}
}
/*
// 先序遍歷僞代碼:非遞歸版本,用棧實現,版本1
void preOrder1(TNode* root)
{
	Stack S;
	while ((root != NULL) || !S.empty())
	{
		if (root != NULL)
		{
			Visit(root);
			S.push(root);       // 先序就體現在這裏了,先訪問,再入棧
			root = root->left;  // 依次訪問左子樹
		}
		else
		{
			root = S.pop();     // 回溯至父親節點
			root = root->right;
		}
	}
}
*/
/*
// 先序遍歷僞代碼:非遞歸版本,用棧實現,版本2
void preOrder2(TNode* root)
{
	if ( root != NULL)
	{
		Stack S;
		S.push(root);
		while (!S.empty())
		{
			TNode* node = S.pop();
			Visit(node);          // 先訪問根節點,然後根節點就無需入棧了
			S.push(node->right);  // 先push的是右節點,再是左節點
			S.push(node->left);
		}
	}
}
*/
#include"bintree.h"
void bintree::postorder() {
	int select;
	std::cout << "1.遞歸後序遍歷. \t2.非遞歸後序遍歷.\t 3.返回主菜單\n";
	std::cin >> select;
	while (select != 1 && select != 2 && select != 3) {
		std::cout << "輸入錯誤,請重新輸入!" << std::endl;
		std::cin >> select;
	}
	switch (select) {
	case 1:postorder(root); postorder(); break;
	case 2:_postorder(); postorder(); break;
	case 3:menu(); break;
	}
}

void bintree::postorder(binnode * bn) {                 //遞歸後序遍歷
	if (bn != nullptr) {
		postorder(bn->left);
		postorder(bn->right);
		std::cout << bn->value << " ";
	}
}
/*
	// 後序遍歷僞代碼:非遞歸版本,用棧實現
void PostOrder(TNode* root)
{
    Stack S;
    if( root != NULL )
    {
        S.push(root);
    }
    while ( !S.empty() )
    {
        TNode* node = S.pop(); 
        if ( node->bPushed )
        {   // 如果標識位爲true,則表示其左右子樹都已經入棧,那麼現在就需要訪問該節點了
            Visit(node);        
        }
        else
        {  
			node->bPushed = true;            // 根節點標誌位爲true
            S.push(node);
		// 左右子樹尚未入棧,則依次將 右節點,左節點,根節點 入棧
            if ( node->right != NULL )
            {
                node->right->bPushed = false; // 左右子樹均設置爲false
                S.push(node->right);
            }
            if ( node->left != NULL )
            {
                node->left->bPushed = false;
                S.push(node->left);
            }
        }
    }
}
*/
void bintree::_postorder() {
	std::stack<binnode*> s;
	binnode *p = root;
	if (p != nullptr) {
		s.push(p);
	}
	while (!s.empty()) {
		p = s.top();
		s.pop();
		if (p->bePushed) {
			visit(p);
		}
		else {
			p->bePushed = true;
			s.push(p);
			if (p->right != nullptr) {
				s.push(p->right);
				p->right->bePushed = false;
			}
			if (p->left != nullptr) {
				s.push(p->left);
				p->left->bePushed = false;
			}
		}
	}
}




總結:1)遞歸改寫成非遞歸,關鍵是要仿照遞歸的遍歷順序,不論是利用棧還是利用指針回溯,最終都是依靠條件來約束非遞歸時的遍歷順序。

    如中序遍歷的指針回溯版本,先走到左子樹的盡頭,若左子樹有右子樹則進入右子樹,若無右子樹則回溯父節點,通過判斷右子樹的有無改變遍歷的順             序,使遍歷的順序跟遞歸調用的遍歷順序一樣。

            2)找准算法的出口,明確算法結束的判斷條件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章