二叉树的建立和深度优先遍历

来源:互联网 发布:茶叶网络营销策划书 编辑:程序博客网 时间:2024/05/20 11:24

一、二叉树的ADT

首先我们定义二叉树的结点Node

struct Node{int data;Node* left;Node* right;Node(int _data):data(_data),left(0),right(0){}bool isLeaf() {return left==NULL && right==NULL;}};

然后我们定义二叉树的抽象类型:要注意的是二叉树只需要一个成员变量Node* root指向树根。如果root为空指针,则此二叉树为空树

typedef stack<Node*> Stack;typedef queue<Node*> Queue;class BinaryTree{private://what we only need is a root nodeNode* root;public:BinaryTree():root(0){}BinaryTree(Node* _root):root(_root){}~BinaryTree(){destroy(root);}void destroy(Node* n) {/*按照后序遍历的思想,先递归析构左右子树*/}void iterate_vlr(Node* v);void iterate_vlr() {iterate_vlr(getTree());}void iterate_lvr(Node* v);void iterate_lvr() {iterate_lvr(getTree());}void iterate_lrv(Node* v);void iterate_lrv() {iterate_lrv(getTree());}void queue_layer(Node* v);void queue_layer() {queue_layer(getTree());}void stack_vlr(Node* v);    void stack_vlr()   {stack_vlr(getTree());}void stack_lvr(Node* v);void stack_lvr()   {stack_lvr(getTree());}void stack_lrv(Node* v);void stack_lrv()   {stack_lrv(getTree());}bool isEmpty() {return root==0;}Node* getTree(){return root;}};

二、二叉树的深度优先递归遍历

这个很简单,cout加在哪里就是什么遍历

void BinaryTree::iterate_vlr(Node* v){if (v==NULL){//cout<<"the tree is empty"<<endl;return;}cout<<(v->data)<<' ';iterate_vlr(v->left);iterate_vlr(v->right);}

三、二叉树的深度非递归遍历

要想搞清非递归遍历背后的数学规律,我们有必要研究一下二叉树遍历的函数帧(stack frame)。

我们先忽略cout输出,只关注函数栈的变化

以如图二叉树为例:

函数帧应当如图所示:


从函数帧本身来说,函数每发生一次递归调用,栈就会升高一个单位;每接受一次返回,栈就会降低一个单位。因此每发生一次调用,栈必然或升或降一个单位,既不会与上一次调用持平,也不会升降更多。

从二叉树来说,每个非空结点都会发出两次调用,因此必然接受两次返回。

现在我们把栈顶用曲线连接起来,我们定义:

“单调递增点”就是指由递增产生的,并且继续递增的点,“单调递增边沿”就是由相邻的这些点组成的边沿。

同理也可以定义“单调递减边沿”。

 

结合cout出现的位置,我们发现:

i.      后序遍历的结果出现在单调递减边沿。

ii.    前序遍历的结果出现在单调递增边沿。

iii.   中序遍历的结果出现在“波谷”。

 

要想把这些发现转化成代码,还需要研究两种边沿和波谷的产生条件。

i.      有且只有栈的push操作才能产生递增边沿。对于二叉树来讲,(a)沿着左子树深搜以及(b)第一次到达右子树的根结点,这两种情况能引发入栈。

ii.    有且仅有栈的pop操作才能产生递减边沿。对于二叉树来讲,(a)左右子树都为空以及(b)左右子树都完成访问,这两种情况能引发出栈。

iii.   对于二叉树来说,(a)访问到叶子节点会产生波谷。同时,(b)如果某时刻一个节点的左子树已经访问完毕,但是还没有访问右子树,那么这个节点此时也在波谷。

 

void BinaryTree::stack_traverse(Node* v){Node* now = v;Node* tmp = 0; //tmp是用来记忆右子树是否被访问过的Stack s;while (now != 0 || !s.empty()){while (now != 0) //这个循环是用来深搜的{s.push(now);/*cout在这里就是先序遍历*/now = now->left;}now = s.top();if (now->right != tmp || now->right==0){/*cout在这里就是中序遍历*/}if (now->right==0 || now->right == tmp){tmp = now;s.pop();/*cout在这里就是中序遍历*/now = 0; //now=0确保执行下一轮主循环的时候,跳过深搜直接now=top}else now = now->right;}cout<<endl;}

这里我想解释一下两个if的条件为什么要这么写

第一个if很好解释:now->right != tmp就是右子树还没访问 now->right==0就是右子树为空。这是中序遍历(波谷)的条件

第二个if:如果now->right==0,那么为了模拟递归,我至少要做三件事:1. 记忆当前结点,代表当前结点已经被访问;  2.弹栈;  3.让now==0,目的是确保执行下一轮主循环的时候,跳过深搜直接now=top

 

附: 二叉树的广度层遍历:

inline bool visit(Node* T){if (T){printf("%d ", T->data);return true;}else return false;}void BinaryTree::queue_layer(Node* v){Queue q;Node* n = v;if (visit(n)){q.push(n);//push root}while (!q.empty()){n = q.front();q.pop(); //pop this layerif (visit(n->left))q.push(n->left); //push next layerif (visit(n->right))q.push(n->right); //push next layer}cout<<endl;}


0 0
原创粉丝点击