参考: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)找准算法的出口,明确算法结束的判断条件。