前、中序的基本结构,表现为先序压栈(NLR),中序出栈(LNR)
压栈的前半部分if(p)
,三种遍历都一样,因为都是L开始,所以将所以左子压入,直到最左的结点。
while(p||!isEmpty(S)){
if(p){
//此处cout<<p->data;即先序
push(S,p);
p=p->lchild; // Order(T->lchild);
}
else{
pop(S,p);
//此处cout<<p->data;即中序
p=p->rchild; //Order(T->rchild)
}
}
入栈顺序为(N)LR,故前序在压栈push前输出
出栈顺序为L(N)R,故中序在出栈pop后输出
(R是用来转道的)
1.前序:
void PreOrder2(BiTree T) {
InitStack(S);
BiNode* p = T;
while (p || !isEmpty(S)) { //当根的左子树及根全pop是,栈为空,p指向根右子树
if (p) {
cout << p->data; //(N)
push(S, p);
p = p->lchild; //(L)
}
else {
pop(S, p);
p = p->rchild; //(R)
}
}
}
对于前序的非递归,先压根(N),再压左子树(L),左子树出栈,根出栈,再压右子树(R),右子树出栈,结束。
前序的循环条件while(p||!isEmpty(S))
(1)一开始S是空的
(2)在根的左子树全部出栈,根也出栈后,栈为空,再压右子树,在右子树压栈出栈过程中可能再次出现栈空的状态。所以加个p,防止因为栈空断了循环。
2.中序:
void InOrder2(BiTree T) {
InitStack(S);
BiNode* p = T;
while (p || !isEmpty(S)) { //同先序,当根及其左子树全pop时,栈为空
if (p) {
push(S, p);
p = p->lchild; //(L)
}
else {
pop(S, p); cout << p->data; //(N)
p = p->rchild; //转右换道(R)
}
}
}
对于中序的非递归:压根,压左子树,左子树出栈(L),根出栈(N),压右子树,右子树出栈(R),结束。
中序的循环条件while(p||!isEmpty(S))
,原因同前序。
3.后序:
void PostOrder2(BiTree T) {
InitStack(S);
BiNode* p = T;
BiNode* r = NULL; //记录上一个出栈的结点,防止右边重复遍历
while (p || !isEmpty(S)) {
if (p) {
push(S, p);
p = p->lchild; //只要p不为空左子全部入栈(L)
}
else {
p = GetTop(S);
if (p->rchild && p->rchild != r) //右子为空且不是上一个出栈结点
p = p->rchild; //(R)
else {
pop(S, p);
cout << p->data; //(N)
r = p; //重置r为出栈结点
p = NULL; //置空!!!防止重复遍历左边
}
}
}
}
对于中序的非递归:压根,压左子树,左子树出栈(L),压右子树,右子树出栈(R),根出栈(N),结束。
压栈顺序为先序NLR。
出栈顺序为后序LRN。
中序的循环条件while(p||!isEmpty(S))
:因为一开始S是空,后序的S之后最后根输出才会空,所以中途不会出现栈空的状态。
每到右结点是我们都要取出栈顶结点(GetTop(S)
)来辅助判断。
所有我们需要r
保存上一个出栈的结点,是否转向右子树,判断其不为NULL
且不是已经访问过的才能p=p->rchild
。
并且在没有右结点而去回溯的时候,将p=NULL
置空,防止返回后又向下访问左子树。
综述
三个非递归的压栈顺序相同,都是NLR,因为这个:
if(p){
push(S,p);
p = p->lchild;
}
只有p存在,总是一路压左。
先序中序出栈相同:
总是左,根先出栈,在去转向右子树压栈。LNR。
else{
pop(S,p);
p=p->rchild;
}
后序特殊,因为要确定右子未被访问,防止重复入右。
else {
pop(S, p);
cout << p->data; //(N)
r = p; //重置r为出栈结点
p = NULL; //置空!!!防止重复遍历左边
}
以下是转右的判断,不是直接p=p->rchild;
然后 pop(S,p);
。
如果没处理,否则每次都会将右边都遍历一遍输出。。。
p = GetTop(S);
if (p->rchild && p->rchild != r) //右子为空且不是上一个出栈结点
p = p->rchild; //(R)
用if…else,也是因为转右和输出是不同的两部分,实际上L、N、R三部分都是if…else语句区分开。
对于pop(S,p);
之后也要进行处理,重置r=p
,防止重复访问右子树,p=NULL
,防止重复访问左子树。
else{
pop(S,p);
cout<<p->data;
r=p;
p=NULL;
}
自己写的玩的
1.前序
非递归实现需要借助栈——先压根,栈顶出栈,先右后左压栈
递归实现是NLR,输出当前结点,再进入左子树遍历,然后进入右子树遍历。每次<输出根,向左>*n,其中遇到右子树转右,在进行前面的操作。
非递归实现:
1.根压栈
2.循环进行——输出栈顶,若有右压右,有左压左
和层次遍历及其的像!!
层次遍历:
1.根进队
2.循环进行——输出队头,有左进左,有右进右
不同点:1.先序是栈,层次是队列。 2.先序是先右后左,层次是先左后右。
void PreOrder1(BiTree T) {
InitStack(S);
push(S,T); //根入栈
BiNode* p; //工作指针
while (!isEmpty(S)) {
pop(S, p);
cout << p->data;
if (p->rchild != NULL)
push(S, p->rchild); //右子进栈
if (p->lchild != NULL)
push(S, p->lchild); //左子进栈
}
}
2.中序
非递归实现需要借助栈,因为中序是先遍历左子树,然后再访问根,然后遍历右子树。
所以我们需要先压所有左子,然后出栈,看是否有右子,有右子就要进入右子树,继续前面的操作。
void InOrder1(BiTree T) {
InitStack(S);
BiNode* p;
while (T || !isEmpty(S)) {
while (T != NULL) { //(L)
push(S, T);
T = T->lchild; //将所以左子压入
}
pop(S, p);
cout << p->data; //取栈顶(N)
if (p->rchild != NULL) //遇到右就换道(R)
T = p->rchild;
}
}
辅助的栈
想折叠,但是CSDN的markdown没有找到折叠的方法,github上面的markdown有折叠的方法。
//顺序栈
typedef struct SqStack {
BiNode* val[Maxsize];
int top; //top指向栈顶元素
}SqStack;
void InitStack(SqStack& S) {
S.top = -1;
}
bool isEmpty(SqStack S) {
return S.top == -1 ? 1 : 0;
}
bool push(SqStack& S, BiNode* x) {
if (S.top == Maxsize - 1)
return 0;
S.val[++S.top] = x;
return 1;
}
bool pop(SqStack& S, BiNode*& x) {
if (S.top == -1)
return 0;
x = S.val[S.top--];
return 1;
}
BiNode* GetTop(SqStack S) {
return S.top == -1 ? NULL : S.val[S.top];
}
链栈?狗都不用!
打了几天链栈,手都打累了,才发现顺序栈可以....
//链栈
typedef struct Linode{
BiNode* node;
Linode* next; // 前面链队是LiNode
}Linode,*LinkStack;
void InitStack(LinkStack& S) { //带头结点
S = new Linode;
S->next = NULL;
}
bool isEmpty(LinkStack S) {
return S->next != NULL ? 0 : 1;
}
BiNode* GetTop(LinkStack S) {
return S->next == NULL ? NULL : S->next->node;
}
void push(LinkStack S, BiNode* x) {
Linode* p = new Linode;
p->node = x;
p->next = S->next;
S->next = p;
}
bool pop(LinkStack S, BiNode* &x) {
if (S->next == NULL) //判栈空
return false;
x = S->next->node;
Linode* p = S->next;
S->next = p->next;
delete(p);
}