參考: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)找准算法的出口,明確算法結束的判斷條件。