赞
踩
#include<vector> using namespace std; typedef struct BiTNode { ElemType data; BiTNode *lchild; BiTNode *rchild; } *BiTree; void InOrderTraverse(BiTree T, void(* Visit)(ElemType e)) { vector<BiTree> stack; stack.push_back(T); while(stack.size()>0) { //关键1:左节点依次进栈直至最左叶节点 while(stack[stack.size()-1] != nullptr) stack.push_back(stack[stack.size()-1]->lchild); stack.pop_back();//除去栈顶的空指针 if(stack[stack.size()-1] == nullptr) continue; // 关键2:访问栈顶结点,出栈,将出栈结点的右结点进栈 Visit(stack[stack.size()-1]->data); BiTNode *rchild = stack[stack.size()-1]->rchild; stack.pop_back(); stack.push_back(rchild); } }
上述算法其实有一个很巧妙的点,它利用空指针来避免了回溯时重复遍历左子树。也就是说,只有在向下遍历时,栈顶指针非空,因此左子树顺利进栈。回溯时栈顶总是有一个空指针,该空指针来自其左子树的最右结点的“空的右子树指针”,因此内层的while循环条件不满足,避免了左子树重复进栈,这其实也是树的非递归遍历的难点之一:如何判断到达当前状态的前一步是回溯还是向下探索,若是向下探索,应当继续向下探索,若是回溯,应该进访问自身同时右子树进栈。
typedef struct BiThrNode { ElemType data; BiThrNode *lchild; BiThrNode *rchild; bool LIsLink; // false means lchild point to forward node; true means lchild point to left child node bool RIsLink; } *BiThrTree; void InOrderTraverse(BiThrTree T, void(* Visit)(ElemType e)) { BiThrNode *p = T->lchild; while(p != T) { // 在循环的第一次遍历中,以下代码可以将p指向中序遍历的第一个结点,也就是根节点一直往左走到尽头的那个结点; // 在循环的第二次以及后续遍历中,配合循环体最后一句p = p->rchild,可以用于寻找右子树非空的结点的后继结点 while(p->LIsLink) p = p->lchild; // 从下面这一句开始,一直重复的是两件事情——访问自己,找到自己的后继结点。后继结点的两种找法: Visit(p->data); while(!(p->RIsLink))//1. 若无右子树,rchild直接指向后继结点 { p = p->rchild; Visit(p->data); } p = p.rchild;//2. 若有右子树,后继结点为右子树的最左结点,需要配合下一次循环的开头的while语句。 } }
二叉树是树的特例,树可以有多个子结点而不必要只有两个,因此树的存储结构不同于二叉树,二叉树只有两个子节点因此可以用 lchild 和 rchild 两个指针形成二叉链表,而树需要特殊的存储结构:
typedef struct CTNode // 链表结点的结构体 { int child; //该孩子在数组中的索引 struct CTNode *next; //指向下一个兄弟 } *ChildPtr; struct CTBox //树结点的结构体 { TElemType data; ChildPtr firstchild; //孩子链表头指针 int parent; //父结点的索引 } struct CTree //树的结构体 { CTBox nodes[MAX_TREE_SIZE]; int n, r; //结点数和根结点的索引 }
typedef struct CSNode
{
ElemType data;
struct CSNode *firstchild, *nextsibling, *parent;
} *CSTree;
树的计数感觉是数学问题,这里就不赘述了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。