使用二叉链表作为树的存储方式,设计一个数据域、两个指针域(指向左右孩子)。
class Tree
{
public:
int data;
Tree* left;
Tree* right;
Tree(int x){
this->data = x;right = NULL; left = NULL;}//构造函数
};
初始化如下图所示的树
Tree *head = new Tree(1);//new返回的是指针,所以左侧定义节点指针变量作为头节点
head->left = new Tree(2);
head->left->left = new Tree(4);
head->left->right = new Tree(5);
head->right = new Tree(3);
head->right->left = new Tree(6);
head->right->right= new Tree(7);
先打印节点信息,再整颗遍历左子树,最后右子树,对于如上图的树,输出1,2,4,5,3,6,7
void preOrderRecur(Tree* head)
{
if(head == NULL)
return;
cout<<head->data<<" ";
preOrderRecur(head->left);
preOrderRecur(head->right);
}
为什么使用栈?
使用栈作为辅助的存储结构,为什么要用到栈,原因是树的数据结构只能够不断查找子树,而不能返回根节点,而从先序遍历的结果来看,对于子树和根都需要访问到而且并不是先跟在子树的访问方式,所以需要用栈来暂存一些节点,达到逆向访问的目的。
思路:
由于先序遍历是中左右的输出顺序,那么使用栈需要先压入右侧子树再压入左侧子树。具体来说首先压入根节点,在栈不空的情况下进行如下循环,先弹出一个栈里的元素,再分别压入弹出元素的右子树和左子树。
具体过程:
①压入根节点1进入循环
②弹出1,压入3,2(输出1)
②弹出2,压入5,4(输出1,2)
③弹出4,4没有子树,不压入(输出1,2,4)
④弹出5,5没有子树,不压入(输出1,2,4,5)
⑤弹出3,压入7,6(输出1,2,4,5,3)
⑥弹出6,6没有子树,不压入(输出1,2,4,5,3,6)
⑦弹出7,7没有子树,不压入(输出1,2,4,5,3,6,7)
⑧栈空结束循环,结束
核心代码:
void preOrderUnRecur(Tree* head)
{
//由于需要获取弹出栈的左右子树压入,
//所以需要把栈设计成树的指针的数据结构
//不能设计成int型,会丢失子树信息
stack<Tree*> t;
t.push(head);//压入根节点
while(!t.empty())//栈不为空时
{
head = t.top();//把栈顶指针(要弹出的数)存储,用来寻找左右子树
cout<<t.top()->data<<" ";
t.pop();
//先压入右再压入左
if(head->right!=NULL)
t.push(head->right);
if(head->left!=NULL)
t.push(head->left);
}
}
#include
#include
using namespace std;
class Tree
{
public:
int data;
Tree* left;
Tree* right;
//构造函数
Tree(int x){
this->data = x;right = NULL; left = NULL;}
};
void preOrderRecur(Tree* head)
{
if(head == NULL)
return;
cout<<head->data<<" ";
preOrderRecur(head->left);
preOrderRecur(head->right);
}
void preOrderUnRecur(Tree* head)
{
//由于需要获取弹出栈的左右子树压入,
//所以需要把栈设计成树的指针的数据结构
//不能设计成int型,会丢失子树信息
stack<Tree*> t;
t.push(head);//压入根节点
while(!t.empty())
{
head = t.top();//把栈顶指针(要弹出的数)存储,用来寻找左右子树
cout<<t.top()->data<<" ";
t.pop();
//先压入右再压入左
if(head->right!=NULL)
t.push(head->right);
if(head->left!=NULL)
t.push(head->left);
}
}
int main()
{
Tree *head = new Tree(1);//new返回的是指针,所以左侧定义节点指针变量作为头节点
head->left = new Tree(2);
head->left->left = new Tree(4);
head->left->right = new Tree(5);
head->right = new Tree(3);
head->right->left = new Tree(6);
head->right->right= new Tree(7);
//preOrderRecur(head);
preOrderUnRecur(head);
system("pause");
return 0;
}
头节点为空直接返回,先整颗遍历左子树,再当前节点,最后右子树,对于如上图的树,输出4,2,5,1,6,3,7
void inOrderRecur(Tree* head)
{
if(head == NULL)
return;
inOrderRecur(head->left);
cout<<head->data<<" ";
inOrderRecur(head->right);
}
思路:
在栈不空或者当前节点不为空的情况下进行循环,当前节点不为空时,入栈并把当前节点指向其左子树;当前节点为空时,出栈,当前节点指向出栈数的右子树。
具体过程:
①当前节点指向1,不为空,压入1,指向1的左子树2
②当前节点指向2,不为空,压入2,指向2的左子树4
③当前节点指向4,不为空,压入4,指向4的左子树NULL
④当前节点指向NULL,空,出栈4,指向4的右子树NULL(输出4)
⑤当前节点指向NULL,空,出栈2,指向2的右子树5(输出4,2)
⑥当前节点指向5,不为空,压入5,指向5的左子树NULL
⑦当前节点指向NULL,空,出栈5,指向5的右子树NULL(输出4,2,5)
⑧当前节点指向NULL,空,出栈1,指向1的右子树3(输出4,2,5,1)
⑨当前节点指向3,不为空,压入3,指向3的左子树6
⑩当前节点指向6,不为空,压入6,指向6的左子树NULL
当前节点指向NULL,空,出栈6,指向6的右子树NULL(输出4,2,5,1,6)
当前节点指向NULL,空,出栈3,指向3的右子树7(输出4,2,5,1,6,3)
当前节点指向7,不为空,压入7,指向7的左子树NULL
当前节点指向NULL,空,出栈7,指向7的右子树NULL(输出4,2,5,1,6,3,7)
当前节点为空且栈为空,退出循环结束。
核心代码:
void inOrderUnRecur(Tree* head)
{
stack<Tree*> t;
while(!t.empty() || head != NULL)
{
//当前节点不为空,入栈并把当前节点指向其左子树
if(head != NULL)
{
t.push(head);
head = head->left;
}
//当前节点为空时,出栈,当前节点指向出栈数的右子树
else
{
head = t.top();
cout<<t.top()->data<<" ";
t.pop();
head = head->right;
}
}
}
#include
#include
using namespace std;
class Tree
{
public:
int data;
Tree* left;
Tree* right;
Tree(int x)
{
this->data = x;
this->left = NULL;
this->right = NULL;
}
};
void inOrderRecur(Tree* head)
{
if(head == NULL)
return;
inOrderRecur(head->left);
cout<<head->data<<" ";
inOrderRecur(head->right);
}
void inOrderUnRecur(Tree* head)
{
stack<Tree*> t;
while(!t.empty() || head != NULL)
{
//当前节点不为空,入栈并把当前节点指向其左子树
if(head != NULL)
{
t.push(head);
head = head->left;
}
//当前节点为空时,出栈,当前节点指向出栈数的右子树
else
{
head = t.top();
cout<<t.top()->data<<" ";
t.pop();
head = head->right;
}
}
}
int main()
{
Tree *head = new Tree(1);
head->left = new Tree(2);
head->left->left = new Tree(4);
head->left->right = new Tree(5);
head->right = new Tree(3);
head->right->left = new Tree(6);
head->right->right= new Tree(7);
inOrderUnRecur(head);
//inOrderRecur(head);
system("pause");
return 0;
}
先遍历整颗左子树,再右子树,最后打印节点信息,对于如上图的树,输出4,5,2,6,7,3,1
void PosOrderRecur(Tree* head)
{
if(head==NULL)
return;
PosOrderRecur(head->left);
PosOrderRecur(head->right);
cout<<head->data<<" ";
}
思路:
由于后序是左右中的顺序,而先序是中左右的顺序,考虑用先序代码修改为后序遍历
具体过程:
先序非递归版是先压右后压左入栈得到中左右的,把压栈顺序调整为先压左后压右可变为中右左,再把结果放入一个栈中,最后弹出就得到左右中的后序遍历。
核心代码:
void PosOrderUnRecur(Tree* head)
{
stack<Tree*> t;
stack<Tree*> t1;
t.push(head);
while(!t.empty())
{
head = t.top();
//cout<data<<" ";
t1.push(t.top());
t.pop();
if(head->left!=NULL)
{
t.push(head->left);
}
if(head->right!=NULL)
{
t.push(head->right);
}
}
//出栈
while(!t1.empty())
{
cout<< t1.top()->data<<" ";
t1.pop();
}
}
#include
#include
using namespace std;
class Tree
{
public:
int data;
Tree* right;
Tree* left;
Tree(int x)
{
this->data = x;
this->right = NULL;
this->left = NULL;
}
};
void PosOrderRecur(Tree* head)
{
if(head==NULL)
return;
PosOrderRecur(head->left);
PosOrderRecur(head->right);
cout<<head->data<<" ";
}
void PosOrderUnRecur(Tree* head)
{
stack<Tree*> t;
stack<Tree*> t1;
t.push(head);
while(!t.empty())
{
head = t.top();
//cout<data<<" ";
t1.push(t.top());
t.pop();
if(head->left!=NULL)
{
t.push(head->left);
}
if(head->right!=NULL)
{
t.push(head->right);
}
}
//出栈
while(!t1.empty())
{
cout<< t1.top()->data<<" ";
t1.pop();
}
}
int main()
{
Tree *head = new Tree(1);
head->left = new Tree(2);
head->left->left = new Tree(4);
head->left->right = new Tree(5);
head->right = new Tree(3);
head->right->left = new Tree(6);
head->right->right= new Tree(7);
//PosOrderRecur(head);
PosOrderUnRecur(head);
system("pause");
return 0;
}
对于上述树,访问顺序如下图为
1,2,4,4,4,2
5,5,5,2,1
3,6,6,6,3
7,7,7,3,1
其中444等连续三个数表示的是访问4的左右节点。
对于访问顺序来说,每个节点都被访问三次。
先序遍历实际是打印第一次访问到这个节点:如图1,2,4,5,3,6,7
中序遍历实际是打印第二次访问到这个节点:4,2,5,1,6,3,7
后序遍历实际是打印第三次访问到这个节点:4,5,2,6,7,3,1