遍历是所有非线性结构(包括二叉树,树,图和广义表)的操作基础
不同的应用需要应用不同的搜索路径
1. 深度优先搜索遍历 2广度优先搜索遍历
深度优先搜索遍历:
二叉树先序,中序,后序
树 先根,后根
图 先访问顶点,后访问顶点
广义表先遍历表头,先遍历表尾
深度优先搜索遍历算法的两种形式:递归,非递归
6.12已知L[i]和R[i](i=1,2,…n-1)分别指示二叉树中第i个结点的左孩子和右孩子结点,0表示空。试写判别结点u是否是结点v的子孙的算法。
分析:
1. u是否是结点v的子孙===从结点v出发遍历能否到达结点u;“访问”===判结点u是否是结点v的孩子
2. 给出的存储结构实质上是一个二叉链表
32_002 |
Status descendent(int L[], int R[], int u,int v) { if(u && v) { if(L[v] == u || R[v] == u) return TRUE; else if(descendent(L, R, u, L[v])) return TRUE; else return descendent(L, R, u, R[v]); } else return FALSE; } |
6.19 编写递归算法:对于二叉树中每一个元素值为x的结点,删去以它为根的子树,并释放相应的空间。
分析:
1. 先序遍历二叉树的过程中查找每一个元素值为x的结点;
2. 修改其双亲结点的相应指针;
3. 释放以它为艮的子树上的所有结点,则应该后序遍历以它为根的子树
32_003 |
void Delete-X(BiTree &BT, ElemType x) { if(BT) { if(BT->data == x) { disp(BT); BT = NULL; } else { Delete-X(BT->Lchild, x); Delete-X(BT->Rchild, x); } } } |
6.22 编写一个算法:输出以二叉树表示的算数表达式,若该表达式中含有括号,则输出时应添上
分析:
1. 原表达式即为带括弧的中缀表达式,则此题应该进行中序遍历
2. 表达式求值应该进行后序遍历,则表明左、右子树的运算应该先于根结点的运算进行;因此,若左、右子树根的运算符的优先数低于根节点运算符的优先数,则应对左子树和右子树应加上括弧;
32_004 |
void Expression(BiTree T) { if(T) { if(precede(T->data, T->Lchild->data) { printf("("); Expression(T->Lchild); printf(")"); } else Expression(T->Lchild); printf(T->data); if(precede(T->data, T->Lchild->data)) } } |
6.24 编写算法完成下列操作:无重复地输出以孩子兄弟链表存储的树T中所有的边。输出的形式为(k1,k2),…,(ki,kj),..,其中,ki和kj为树结点中的结点标识。
分析:
在孩子兄弟链表中,哪一些结点是根的孩子?
33_001 |
void OutEdge(CStree T) { if(T) { p = T->firstchild; while(p) { prntf(T->data, p->data); OutEdge(p); p = p->nextsibling; } } }//先根遍历输出树中各条边 |
7.7 已知有向图和图中两个顶点u和v,试编写算法求有向图从u到v的所有简单路径。
33_002 |
void DFSearch(Graph G, int v, int s) { visited[v] = TRUE; //访问第v个顶点 Append(PATH, getVertex(v)); for(w = FirstAdjVex(v); w != 0; w = NextAdjVex(v)) OutPath(PATH); if(! visited[w]) DFSearch(G, w, s); visited[v] = FLASE; } |
深度优先搜索遍历的非递归形式的算法:一般情况下,需要一个“栈”保留一些信息
例如:遍历二叉树时,需要利用栈保留曾经路过的根结点(中序);或者保留尚未遍历的右子树的根结点(先序)以便从左子树返回时继续遍历右子树。
后序遍历二叉树的情况要复杂一些,由于根结点的访问在左、右子树的遍历之后,则不仅要保存结点的信息,还应保存“标志”。
反之,如果在存储结构中加上适当的信息,
6.15假设在二叉链表的结点中增设两个域;双亲域(parent)以指示其双亲结点;标识域(mark:0,1,2)以区分在遍历过程中到达该节点时应继续向左或向右访问该结点,试以此存储结构编写不用栈进行后序遍历的递归形式的算法。
Mark域的作用在于识别当前的状态:
Mark=0表示当前状态是对结点第一次访问
Mark=1表示当前状态是对结点第二次访问
Mark=2表示当前状态是对结点第三次访问
33_003 |
void postorder(BiTree T) { p = T; while(p) { switch(p->mark) { case 0: p->mark = 1; if(p->Lchild) { p = p->Lchild; } break; case 1: p->mark = 2; if(p->Rchild) { p = p->Rchild; } break; case 2: p->mark = 0; //... } } } |
深度优先遍历图的非递归形式的算法
33_004 |
void DFS(Graph G, int v) { visited[v] = TRUE; VisitFunc(v); for(w = FirstAdjVex(G, v); w != 0; w = NextAdjVex(G, v, w)) if(! visited[w]) DFS(G, w); } |
可见,栈中保留的信息应该是:当前访问的顶点和它当前的邻接点
33_005 |
void DFS(Graph g, int v) { Visit(v); visited[v] = TRUE; ... while(! StackEmpty(S)) { v = GetTop(S).vp; w = GetTop(S),wp; if(w) { if(! visited[w]) { Visit(w); visited[w] = TRUE; ... } else { w = NextAdj(G, v, w); ... } Pop(S); }
} } |
广度优先搜索遍历的算法
33_006 |
void BFSTraverse(Graph G, Status(* Visit)(int v)) { for(v = 0; v < G.vexnum; ++ v) visited[v] = FALSE; InitQueue(Q); //置空的辅助队列Q for(v = 0; v < G.vexnum; ++v) { if(! visited[v]) { //v尚未访问 visited[v] = TRUE; Visit(v); //访问v EnQueue(Q, v); //v入队列 while(! QueueEmpty(Q)) { DeQueue(Q, u); //队列元素出队并置为u for(w = FirstAdjVex(G, u); w != 0; w = NextAdjVex(G, u, w)) if(! visited[w]) { visited[w] = TRUE; Visit(w); Enqueue(Q, w); // } } } } } |
6.20编写按层次顺序(同一层自左至右)遍历二叉树的算法
33_007 |
void BFSTraverse(BiTree T) { InitQueue(Q); //置空的辅助队列Q if(T) EnQueue(Q, T); //根结点入队列 while(! QueueEmpty(Q)) { DeQueue(Q, p); //队头元素出队并置为p Visit(p); if(p->Lchild) EnQueue(Q, p->Lchild); //左子树根入队列 if(p->Rchild) EnQueue(Q, p->Rchild); //右子树根入队列 } } |
33_008 |
void BFSTraverse(CSTree T) { InitQueue(Q); //置空的辅助队列Q if(T) EnQueue(Q, T); //根结点入队列 while(! QueueEmpty(Q)) { DeQueue(Q, p); //队头元素出队并置为p Visit(p); for(q = p->firstchild; !q; q = q->nextsibling) EnQueue(Q, q); //子树根入队列 } } |