056312409数据结构(C语言版)(夏燕张兴科)--习题答案--第7章
- 格式:doc
- 大小:383.00 KB
- 文档页数:9
严蔚敏《数据结构(c语言版)习题集》答案第七章图第七章图7.14Status Build_AdjList(ALGraph &G)//输入有向图的顶点数,边数,顶点信息和边的信息建立邻接表{InitALGraph(G);scanf("%d",&v);if(v<0) return ERROR; //顶点数不能为负G.vexnum=v;scanf("%d",&a);if(a<0) return ERROR; //边数不能为负G.arcnum=a;for(m=0;m<v;m++)G.vertices[m].data=getchar(); //输入各顶点的符号for(m=1;m<=a;m++){t=getchar();h=getchar(); //t为弧尾,h为弧头if((i=LocateVex(G,t))<0) return ERROR;if((j=LocateVex(G,h))<0) return ERROR; //顶点未找到p=(ArcNode*)malloc(sizeof(ArcNode));if(!G.vertices.[i].firstarc) G.vertices[i].firstarc=p;else{for(q=G.vertices[i].firstarc;q->nextarc;q=q->nextarc);q->nextarc=p;}p->adjvex=j;p->nextarc=NULL;}//whilereturn OK;}//Build_AdjList7.15//本题中的图G均为有向无权图,其余情况容易由此写出Status Insert_Vex(MGraph &G, char v)//在邻接矩阵表示的图G上插入顶点v {if(G.vexnum+1)>MAX_VERTEX_NUM return INFEASIBLE;G.vexs[++G.vexnum]=v;return OK;}//Insert_VexStatus Insert_Arc(MGraph &G,char v,char w)//在邻接矩阵表示的图G上插入边(v,w) {if((i=LocateVex(G,v))<0) return ERROR;if((j=LocateVex(G,w))<0) return ERROR;if(i==j) return ERROR;if(!G.arcs[i][j].adj){G.arcs[i][j].adj=1;G.arcnum++;}return OK;}//Insert_ArcStatus Delete_Vex(MGraph &G,char v)//在邻接矩阵表示的图G上删除顶点v{n=G.vexnum;if((m=LocateVex(G,v))<0) return ERROR;G.vexs[m]<->G.vexs[n]; //将待删除顶点交换到最后一个顶点for(i=0;i<n;i++){G.arcs[i][m]=G.arcs[i][n];G.arcs[m][i]=G.arcs[n][i]; //将边的关系随之交换}G.arcs[m][m].adj=0;G.vexnum--;return OK;}//Delete_Vex分析:如果不把待删除顶点交换到最后一个顶点的话,算法将会比较复杂,而伴随着大量元素的移动,时间复杂度也会大大增加.Status Delete_Arc(MGraph &G,char v,char w)//在邻接矩阵表示的图G上删除边(v,w) {if((i=LocateVex(G,v))<0) return ERROR;if((j=LocateVex(G,w))<0) return ERROR;if(G.arcs[i][j].adj){G.arcs[i][j].adj=0;G.arcnum--;}return OK;}//Delete_Arc7.16//为节省篇幅,本题只给出Insert_Arc算法.其余算法请自行写出.Status Insert_Arc(ALGraph &G,char v,char w)//在邻接表表示的图G上插入边(v,w) {if((i=LocateVex(G,v))<0) return ERROR;if((j=LocateVex(G,w))<0) return ERROR;p=(ArcNode*)malloc(sizeof(ArcNode));p->adjvex=j;p->nextarc=NULL;if(!G.vertices[i].firstarc) G.vertices[i].firstarc=p;else{for(q=G.vertices[i].firstarc;q->q->nextarc;q=q->nextarc)if(q->adjvex==j) return ERROR; //边已经存在q->nextarc=p;}G.arcnum++;return OK;}//Insert_Arc7.17//为节省篇幅,本题只给出较为复杂的Delete_Vex算法.其余算法请自行写出. Status Delete_Vex(OLGraph &G,char v)//在十字链表表示的图G上删除顶点v { if((m=LocateVex(G,v))<0) return ERROR;n=G.vexnum;for(i=0;i<n;i++) //删除所有以v为头的边{if(G.xlist[i].firstin->tailvex==m) //如果待删除的边是头链上的第一个结点{q=G.xlist[i].firstin;G.xlist[i].firstin=q->hlink;free(q);G.arcnum--;}else //否则{for(p=G.xlist[i].firstin;p&&p->hlink->tailvex!=m;p=p->hlink);if(p){q=p->hlink;p->hlink=q->hlink;free(q);G.arcnum--;}}//else}//forfor(i=0;i<n;i++) //删除所有以v为尾的边{if(G.xlist[i].firstout->headvex==m) //如果待删除的边是尾链上的第一个结点{q=G.xlist[i].firstout;G.xlist[i].firstout=q->tlink;free(q);G.arcnum--;}else //否则{for(p=G.xlist[i].firstout;p&&p->tlink->headvex!=m;p=p->tlink);if(p){q=p->tlink;p->tlink=q->tlink;free(q);G.arcnum--;}}//else}//forfor(i=m;i<n;i++) //顺次用结点m之后的顶点取代前一个顶点{G.xlist[i]=G.xlist[i+1]; //修改表头向量for(p=G.xlist[i].firstin;p;p=p->hlink)p->headvex--;for(p=G.xlist[i].firstout;p;p=p->tlink)p->tailvex--; //修改各链中的顶点序号}G.vexnum--;return OK;}//Delete_Vex7.18//为节省篇幅,本题只给出Delete_Arc算法.其余算法请自行写出.Status Delete_Arc(AMLGraph &G,char v,char w)////在邻接多重表表示的图G上删除边(v,w){if((i=LocateVex(G,v))<0) return ERROR;if((j=LocateVex(G,w))<0) return ERROR;if(G.adjmulist[i].firstedge->jvex==j)G.adjmulist[i].firstedge=G.adjmulist[i].firstedge->ilink;else{for(p=G.adjmulist[i].firstedge;p&&p->ilink->jvex!=j;p=p->ilink);if (!p) return ERROR; //未找到p->ilink=p->ilink->ilink;} //在i链表中删除该边if(G.adjmulist[j].firstedge->ivex==i)G.adjmulist[j].firstedge=G.adjmulist[j].firstedge->jlink;else{for(p=G.adjmulist[j].firstedge;p&&p->jlink->ivex!=i;p=p->jlink);if (!p) return ERROR; //未找到q=p->jlink;p->jlink=q->jlink;free(q);} //在i链表中删除该边G.arcnum--;return OK;}//Delete_Arc7.19Status Build_AdjMulist(AMLGraph &G)//输入有向图的顶点数,边数,顶点信息和边的信息建立邻接多重表{InitAMLGraph(G);scanf("%d",&v);if(v<0) return ERROR; //顶点数不能为负G.vexnum=v;scanf(%d",&a);if(a<0) return ERROR; //边数不能为负G.arcnum=a;for(m=0;m<v;m++)G.adjmulist[m].data=getchar(); //输入各顶点的符号for(m=1;m<=a;m++){t=getchar();h=getchar(); //t为弧尾,h为弧头if((i=LocateVex(G,t))<0) return ERROR;if((j=LocateVex(G,h))<0) return ERROR; //顶点未找到p=(EBox*)malloc(sizeof(EBox));p->ivex=i;p->jvex=j;p->ilink=NULL;p->jlink=NULL; //边结点赋初值if(!G.adjmulist[i].firstedge) G.adjmulist[i].firstedge=p;else{q=G.adjmulist[i].firstedge;while(q){r=q;if(q->ivex==i) q=q->ilink;else q=q->jlink;}if(r->ivex==i) r->ilink=p;//注意i值既可能出现在边结点的ivex域中, else r->jlink=p; //又可能出现在边结点的jvex域中}//else //插入i链表尾部if(!G.adjmulist[j].firstedge) G.adjmulist[j].firstedge=p;else{q=G.adjmulist[i].firstedge;while(q){r=q;if(q->jvex==j) q=q->jlink;else q=q->ilnk;}if(r->jvex==j) r->jlink=p;else r->ilink=p;}//else //插入j链表尾部}//forreturn OK;}//Build_AdjList7.20int Pass_MGraph(MGraph G)//判断一个邻接矩阵存储的有向图是不是可传递的,是则返回1,否则返回0{for(x=0;x<G.vexnum;x++)for(y=0;y<G.vexnum;y++)if(G.arcs[x][y]){for(z=0;z<G.vexnum;z++)if(z!=x&&G.arcs[y][z]&&!G.arcs[x][z]) return 0;//图不可传递的条件}//ifreturn 1;}//Pass_MGraph分析:本算法的时间复杂度大概是O(n^2*d).7.21int Pass_ALGraph(ALGraph G)//判断一个邻接表存储的有向图是不是可传递的,是则返回1,否则返回0{for(x=0;x<G.vexnum;x++)for(p=G.vertices[x].firstarc;p;p=p->nextarc){y=p->adjvex;for(q=G.vertices[y].firstarc;q;q=q->nextarc){z=q->adjvex;if(z!=x&&!is_adj(G,x,z)) return 0;}//for}//for}//Pass_ALGraphint is_adj(ALGraph G,int m,int n)//判断有向图G中是否存在边(m,n),是则返回1,否则返回0{for(p=G.vertices[m].firstarc;p;p=p->nextarc)if(p->adjvex==n) return 1;return 0;}//is_adj7.22int visited[MAXSIZE]; //指示顶点是否在当前路径上int exist_path_DFS(ALGraph G,int i,int j)//深度优先判断有向图G中顶点i到顶点j是否有路径,是则返回1,否则返回0{if(i==j) return 1; //i就是jelse{visited[i]=1;for(p=G.vertices[i].firstarc;p;p=p->nextarc){k=p->adjvex;if(!visited[k]&&exist_path(k,j)) return 1;//i下游的顶点到j有路径}//for}//else}//exist_path_DFS7.23int exist_path_BFS(ALGraph G,int i,int j)//广度优先判断有向图G中顶点i到顶点j是否有是则返回1,否则返回0 路径,{int visited[MAXSIZE];InitQueue(Q);EnQueue(Q,i);while(!QueueEmpty(Q)){DeQueue(Q,u);visited[u]=1;for(p=G.vertices[i].firstarc;p;p=p->nextarc){k=p->adjvex;if(k==j) return 1;if(!visited[k]) EnQueue(Q,k);}//for}//whilereturn 0;}//exist_path_BFS7.24void STraverse_Nonrecursive(Graph G)//非递归遍历强连通图G { int visited[MAXSIZE];InitStack(S);Push(S,GetVex(S,1)); //将第一个顶点入栈visit(1);visited =1;while(!StackEmpty(S)){while(Gettop(S,i)&&i){j=FirstAdjVex(G,i);if(j&&!visited[j]){visit(j);visited[j]=1;Push(S,j); //向左走到尽头}}//whileif(!StackEmpty(S)){Pop(S,j);Gettop(S,i);k=NextAdjVex(G,i,j); //向右走一步if(k&&!visited[k]){visit(k);visited[k]=1;Push(S,k);}}//if}//while}//Straverse_Nonrecursive 分析:本算法的基本思想与二叉树的先序遍历非递归算法相同,请参考6.37.由于是强连通图,所以从第一个结点出发一定能够访问到所有结点. 7.25见书后解答.7.26Status TopoNo(ALGraph G)//按照题目要求顺序重排有向图中的顶点 { int new[MAXSIZE],indegree[MAXSIZE]; //储存结点的新序号n=G.vexnum;FindInDegree(G,indegree);InitStack(S);for(i=1;i<G.vexnum;i++)if(!indegree[i]) Push(S,i); //零入度结点入栈count=0;while(!StackEmpty(S)){Pop(S,i);new[i]=n--; //记录结点的拓扑逆序序号count++;for(p=G.vertices[i].firstarc;p;p=p->nextarc){k=p->adjvex;if(!(--indegree[k])) Push(S,k);}//for}//whileif(count<G.vexnum) return ERROR; //图中存在环for(i=1;i<=n;i++) printf("Old No:%d New No:%d\n",i,new[i])return OK;}//TopoNo分析:只要按拓扑逆序对顶点编号,就可以使邻接矩阵成为下三角矩阵. 7.27 int visited[MAXSIZE];int exist_path_len(ALGraph G,int i,int j,int k)//判断邻接表方式存储的有向图G的顶点i到j是否存在长度为k的简单路径{if(i==j&&k==0) return 1; //找到了一条路径,且长度符合要求else if(k>0){visited[i]=1;for(p=G.vertices[i].firstarc;p;p=p->nextarc){l=p->adjvex;if(!visited[l])if(exist_path_len(G,l,j,k-1)) return 1; //剩余路径长度减一}//forvisited[i]=0; //本题允许曾经被访问过的结点出现在另一条路径中}//elsereturn 0; //没找到}//exist_path_len7.28int path[MAXSIZE],visited[MAXSIZE]; //暂存遍历过程中的路径 intFind_All_Path(ALGraph G,int u,int v,int k)//求有向图G中顶点u到v之间的所有简单路径,k表示当前路径长度{path[k]=u; //加入当前路径中visited[u]=1;if(u==v) //找到了一条简单路径{printf("Found one path!\n");for(i=0;path[i];i++) printf("%d",path[i]); //打印输出}elsefor(p=G.vertices[u].firstarc;p;p=p->nextarc){l=p->adjvex;if(!visited[l]) Find_All_Path(G,l,v,k+1); //继续寻找}visited[u]=0;path[k]=0; //回溯}//Find_All_Pathmain(){...Find_All_Path(G,u,v,0); //在主函数中初次调用,k值应为0...}//main7.29int GetPathNum_Len(ALGraph G,int i,int j,int len)//求邻接表方式存储的有向图G的顶点i到j之间长度为len的简单路径条数{if(i==j&&len==0) return 1; //找到了一条路径,且长度符合要求else if(len>0){sum=0; //sum表示通过本结点的路径数visited[i]=1;for(p=G.vertices[i].firstarc;p;p=p->nextarc){l=p->adjvex;if(!visited[l])sum+=GetPathNum_Len(G,l,j,len-1)//剩余路径长度减一}//forvisited[i]=0; //本题允许曾经被访问过的结点出现在另一条路径中}//elsereturn sum;}//GetPathNum_Len7.30int visited[MAXSIZE];int path[MAXSIZE]; //暂存当前路径int cycles[MAXSIZE][MAXSIZE]; //储存发现的回路所包含的结点 int thiscycle[MAXSIZE]; //储存当前发现的一个回路 int cycount=0; //已发现的回路个数void GetAllCycle(ALGraph G)//求有向图中所有的简单回路 {for(v=0;v<G.vexnum;v++) visited[v]=0;for(v=0;v<G.vexnum;v++)if(!visited[v]) DFS(G,v,0); //深度优先遍历}//DFSTraversevoid DFS(ALGraph G,int v,int k)//k表示当前结点在路径上的序号 {visited[v]=1;path[k]=v; //记录当前路径for(p=G.vertices[v].firstarc;p;p=p->nextarc){w=p->adjvex;if(!visited[w]) DFS(G,w,k+1);else //发现了一条回路{for(i=0;path[i]!=w;i++); //找到回路的起点for(j=0;path[i+j];i++) thiscycle[j]=path[i+j];//把回路复制下来if(!exist_cycle()) cycles[cycount++]=thiscycle;//如果该回路尚未被记录过,就添加到记录中for(i=0;i<G.vexnum;i++) thiscycle[i]=0; //清空目前回路数组}//else}//forpath[k]=0;visited[k]=0; //注意只有当前路径上的结点visited为真.因此一旦遍历中发现当前结点visited为真,即表示发现了一条回路}//DFSint exist_cycle()//判断thiscycle数组中记录的回路在cycles的记录中是否已经存在 {int temp[MAXSIZE];for(i=0;i<cycount;i++) //判断已有的回路与thiscycle是否相同{ //也就是,所有结点和它们的顺序都相同j=0;c=thiscycle�; //例如,142857和857142是相同的回路for(k=0;cycles[i][k]!=c&&cycles[i][k]!=0;k++);//在cycles的一个行向量中寻找等于thiscycle第一个结点的元素if(cycles[i][k]) //有与之相同的一个元素{for(m=0;cycles[i][k+m];m++)temp[m]=cycles[i][k+m];for(n=0;n<k;n++,m++)temp[m]=cycles[i][n]; //调整cycles中的当前记录的循环相位并放入temp 数组中if(!StrCompare(temp,thiscycle)) //与thiscycle比较return 1; //完全相等for(m=0;m<G.vexnum;m++) temp[m]=0; //清空这个数组}}//forreturn 0; //所有现存回路都不与thiscycle完全相等}//exist_cycle分析:这个算法的思想是,在遍历中暂存当前路径,当遇到一个结点已经在路径之中时就表明存在一条回路;扫描路径向量path可以获得这条回路上的所有结点.把结点序列(例如,142857)存入thiscycle中;由于这种算法中,一条回路会被发现好几次,所以必须先判断该回路是否已经在cycles中被记录过,如果没有才能存入cycles的一个行向量中.把cycles的每一个行向量取出来与之比较.由于一条回路可能有多种存储顺序,比如142857等同于285714和571428,所以还要调整行向量的次序,并存入temp数组,例如,thiscycle为142857第一个结点为1,cycles的当前向量为857142,则找到后者中的1,把1后部分提到1前部分前面,最终在temp中得到142857,与thiscycle比较,发现相同,因此142857和857142是同一条回路,不予存储.这个算法太复杂,很难保证细节的准确性,大家理解思路便可.希望有人给出更加简捷的算法.7.31int visited[MAXSIZE];int finished[MAXSIZE];int count; //count在第一次深度优先遍历中用于指示finished数组的填充位置 void Get_SGraph(OLGraph G)//求十字链表结构储存的有向图G的强连通分量 {count=0;for(v=0;v<G.vexnum;v++) visited[v]=0;for(v=0;v<G.vexnum;v++) //第一次深度优先遍历建立finished数组if(!visited[v]) DFS1(G,v);for(v=0;v<G.vexnum;v++) visited[v]=0; //清空visited数组for(i=G.vexnum-1;i>=0;i--) //第二次逆向的深度优先遍历{v=finished(i);if(!visited[v]){printf("\n"); //不同的强连通分量在不同的行输出DFS2(G,v);}}//for}//Get_SGraphvoid DFS1(OLGraph G,int v)//第一次深度优先遍历的算法{visited[v]=1;for(p=G.xlist[v].firstout;p;p=p->tlink){w=p->headvex;if(!visited[w]) DFS1(G,w);}//forfinished[++count]=v; //在第一次遍历中建立finished数组}//DFS1void DFS2(OLGraph G,int v)//第二次逆向的深度优先遍历的算法 {visited[v]=1;printf("%d",v); //在第二次遍历中输出结点序号for(p=G.xlist[v].firstin;p;p=p->hlink){w=p->tailvex;if(!visited[w]) DFS2(G,w);}//for}//DFS2分析:求有向图的强连通分量的算法的时间复杂度和深度优先遍历相同,也为O(n+e). 7.32void Forest_Prim(ALGraph G,int k,CSTree &T)//从顶点k出发,构造邻接表结构的有向图G的最小生成森林T,用孩子兄弟链表存储{for(j=0;j<G.vexnum;j++) //以下在Prim算法基础上稍作改动if(j!=k){closedge[j]={k,Max_int};for(p=G.vertices[j].firstarc;p;p=p->nextarc)if(p->adjvex==k) closedge[j].lowcost=p->cost;}//ifclosedge[k].lowcost=0;for(i=1;i<G.vexnum;i++){k=minimum(closedge);if(closedge[k].lowcost<Max_int){Addto_Forest(T,closedge[k].adjvex,k); //把这条边加入生成森林中closedge[k].lowcost=0;for(p=G.vertices[k].firstarc;p;p=p->nextarc)if(p->cost<closedge[p->adjvex].lowcost)closedge[p->adjvex]={k,p->cost};}//ifelse Forest_Prim(G,k); //对另外一个连通分量执行算法}//for}//Forest_Primvoid Addto_Forest(CSTree &T,int i,int j)//把边(i,j)添加到孩子兄弟链表表示的树T中 {p=Locate(T,i); //找到结点i对应的指针p,过程略q=(CSTNode*)malloc(sizeof(CSTNode));q->data=j;if(!p) //起始顶点不属于森林中已有的任何一棵树{p=(CSTNode*)malloc(sizeof(CSTNode));p->data=i;for(r=T;r->nextsib;r=r->nextsib);r->nextsib=p;p->firstchild=q;} //作为新树插入到最右侧else if(!p->firstchild) //双亲还没有孩子p->firstchild=q; //作为双亲的第一个孩子else //双亲已经有了孩子{for(r=p->firstchild;r->nextsib;r=r->nextsib);r->nextsib=q; //作为双亲最后一个孩子的兄弟}}//Addto_Forestmain(){...T=(CSTNode*)malloc(sizeof(CSTNode)); //建立树根T->data=1;Forest_Prim(G,1,T);...}//main分析:这个算法是在Prim算法的基础上添加了非连通图支持和孩子兄弟链表构建模块而得到的,其时间复杂度为O(n^2).7.33typedef struct {int vex; //结点序号int ecno; //结点所属的连通分量号} VexInfo; VexInfo vexs[MAXSIZE]; //记录结点所属连通分量号的数组void Init_VexInfo(VexInfo &vexs[ ],int vexnum)//初始化 { for(i=0;i<vexnum;i++)vexs[i]={i,i}; //初始状态:每一个结点都属于不同的连通分量 }//Init_VexInfoint is_ec(VexInfo vexs[ ],int i,int j)//判断顶点i和顶点j是否属于同一个连通分量{if(vexs[i].ecno==vexs[j].ecno) return 1;else return 0;}//is_ecvoid merge_ec(VexInfo &vexs[ ],int ec1,int ec2)//合并连通分量ec1和ec2{for(i=0;vexs[i].vex;i++)if(vexs[i].ecno==ec2) vexs[i].ecno==ec1;}//merge_ecvoid MinSpanTree_Kruscal(Graph G,EdgeSetType &EdgeSet,CSTree &T)//求图的最小生成树的克鲁斯卡尔算法{Init_VexInfo(vexs,G.vexnum);ecnum=G.vexnum; //连通分量个数while(ecnum>1){GetMinEdge(EdgeSet,u,v); //选出最短边if(!is_ec(vexs,u,v)) //u和v属于不同连通分量{Addto_CSTree(T,u,v); //加入到生成树中merge_ec(vexs,vexs[u].ecno,vexs[v].ecno); //合并连通分量ecnum--;}DelMinEdge(EdgeSet,u,v); //从边集中删除}//while}//MinSpanTree_Kruscalvoid Addto_CSTree(CSTree &T,int i,int j)//把边(i,j)添加到孩子兄弟链表表示的树T中 {p=Locate(T,i); //找到结点i对应的指针p,过程略q=(CSTNode*)malloc(sizeof(CSTNode));q->data=j;if(!p->firstchild) //双亲还没有孩子p->firstchild=q; //作为双亲的第一个孩子else //双亲已经有了孩子{for(r=p->firstchild;r->nextsib;r=r->nextsib);r->nextsib=q; //作为双亲最后一个孩子的兄弟}}//Addto_CSTree分析:本算法使用一维结构体变量数组来表示等价类,每个连通分量所包含的所有结点属于一个等价类.在这个结构上实现了初始化,判断元素是否等价(两个结点是否属于同一个连通分量),合并等价类(连通分量)的操作.7.34Status TopoSeq(ALGraph G,int new[ ])//按照题目要求给有向无环图的结点重新编号,并存入数组new中{int indegree[MAXSIZE]; //本算法就是拓扑排序FindIndegree(G,indegree);Initstack(S);for(i=0;i<G.vexnum;i++)if(!indegree[i]) Push(S,i);count=0;while(!stackempty(S)){Pop(S,i);new[i]=++count; //把拓扑顺序存入数组的对应分量中for(p=G.vertices[i].firstarc;p;p=p->nextarc){k=p->adjvex;if(!(--indegree[k])) Push(S,k);}}//whileif(count<G.vexnum) return ERROR;return OK;}//TopoSeq7.35int visited[MAXSIZE];void Get_Root(ALGraph G)//求有向无环图的根,如果有的话{for(v=0;v<G.vexnum;v++){for(w=0;w<G.vexnum;w++) visited[w]=0;//每次都要将访问数组清零DFS(G,v); //从顶点v出发进行深度优先遍历for(flag=1,w=0;w<G.vexnum;w++)if(!visited[w]) flag=0; //如果v是根,则深度优先遍历可以访问到所有结点if(flag) printf("Found a root vertex:%d\n",v);}//for}//Get_Root,这个算法要求图中不能有环,否则会发生误判void DFS(ALGraph G,int v){visited[v]=1;for(p=G.vertices[v].firstarc;p;p=p->nextarc){w=p->adjvex;if(!visited[w]) DFS(G,w);}}//DFS7.36void Fill_MPL(ALGraph &G)//为有向无环图G添加MPL域 {FindIndegree(G,indegree);for(i=0;i<G.vexnum;i++)if(!indegree[i]) Get_MPL(G,i);//从每一个零入度顶点出发构建MPL域 }//Fill_MPLint Get_MPL(ALGraph &G,int i)//从一个顶点出发构建MPL域并返回其MPL 值 {if(!G.vertices[i].firstarc){G.vertices[i].MPL=0;return 0; //零出度顶点}else{max=0;for(p=G.vertices[i].firstarc;p;p=p->nextarc){j=p->adjvex;if(G.vertices[j].MPL==0) k=Get_MPL(G,j);if(k>max) max=k; //求其直接后继顶点MPL的最大者}G.vertices[i]=max+1;//再加一,就是当前顶点的MPLreturn max+1;}//else}//Get_MPL7.37int maxlen,path[MAXSIZE]; //数组path用于存储当前路径 intmlp[MAXSIZE]; //数组mlp用于存储已发现的最长路径 voidGet_Longest_Path(ALGraph G)//求一个有向无环图中最长的路径 { maxlen=0;FindIndegree(G,indegree);for(i=0;i<G.vexnum;i++){for(j=0;j<G.vexnum;j++) visited[j]=0;if(!indegree[i]) DFS(G,i,0);//从每一个零入度结点开始深度优先遍历}printf("Longest Path:");for(i=0;mlp[i];i++) printf("%d",mlp[i]); //输出最长路径 }//Get_Longest_Pathvoid DFS(ALGraph G,int i,int len) {visited[i]=1;path[len]=i;if(len>maxlen&&!G.vertices[i].firstarc) //新的最长路径{for(j=0;j<=len;j++) mlp[j]=path[j]; //保存下来maxlen=len;}else{for(p=G.vertices[i].firstarc;p;p=p->nextarc){j=p->adjvex;if(!visited[j]) DFS(G,j,len+1);}}//elsepath[i]=0;visited[i]=0;}//DFS7.38void NiBoLan_DAG(ALGraph G)//输出有向无环图形式表示的表达式的逆波兰式 {FindIndegree(G,indegree);for(i=0;i<G.vexnum;i++)if(!indegree[i]) r=i; //找到有向无环图的根PrintNiBoLan_DAG(G,i);}//NiBoLan_DAGvoid PrintNiBoLan_DAG(ALGraph G,int i)//打印输出以顶点i为根的表达式的逆波兰式 {c=G.vertices[i].data;if(!G.vertices[i].firstarc) //c是原子printf("%c",c);else //子表达式{p=G.vertices[i].firstarc;PrintNiBoLan_DAG(G,p->adjvex);PrintNiBoLan_DAG(G,p->nexarc->adjvex);printf("%c",c);}}//PrintNiBoLan_DAG7.39void PrintNiBoLan_Bitree(Bitree T)//在二叉链表存储结构上重做上一题 { if(T->lchild) PrintNiBoLan_Bitree(T->lchild);if(T->rchild) PrintNiBoLan_Bitree(T->rchild);printf("%c",T->data);}//PrintNiBoLan_Bitree7.40int Evaluate_DAG(ALGraph G)//给有向无环图表示的表达式求值 { FindIndegree(G,indegree);for(i=0;i<G.vexnum;i++)if(!indegree[i]) r=i; //找到有向无环图的根return Evaluate_imp(G,i); }//NiBoLan_DAGint Evaluate_imp(ALGraph G,int i)//求子表达式的值{if(G.vertices[i].tag=NUM) return G.vertices[i].value;else{p=G.vertices[i].firstarc;v1=Evaluate_imp(G,p->adjvex);v2=Evaluate_imp(G,p->nextarc->adjvex);return calculate(v1,G.vertices[i].optr,v2);}}//Evaluate_imp分析:本题中,邻接表的vertices向量的元素类型修改如下: struct { enum tag{NUM,OPTR};union {int value;char optr;};ArcNode * firstarc;} Elemtype;7.41void Critical_Path(ALGraph G)//利用深度优先遍历求网的关键路径 { FindIndegree(G,indegree);for(i=0;i<G.vexnum;i++)if(!indegree[i]) DFS1(G,i); //第一次深度优先遍历:建立vefor(i=0;i<G.vexnum;i++)if(!indegree[i]) DFS2(G,i); //第二次深度优先遍历:建立vlfor(i=0;i<=G.vexnum;i++)if(vl[i]==ve[i]) printf("%d",i); //打印输出关键路径}//Critical_Pathvoid DFS1(ALGraph G,int i) {if(!indegree[i]) ve[i]=0;for(p=G.vertices[i].firstarc;p;p=p->nextarc) {dut=*p->info;if(ve[i]+dut>ve[p->adjvex])ve[p->adjvex]=ve[i]+dut;DFS1(G,p->adjvex);}}//DFS1void DFS2(ALGraph G,int i) {if(!G.vertices[i].firstarc) vl[i]=ve[i]; else{for(p=G.vertices[i].firstarc;p;p=p->nextarc) {DFS2(G,p->adjvex);dut=*p->info;if(vl[p->adjvex]-dut<vl[i])vl[i]=vl[p->adjvex]-dut;}}//else}//DFS27.42void ALGraph_DIJ(ALGraph G,int v0,Pathmatrix &P,ShortestPathTable &D)//在邻接表存储结构上实现迪杰斯特拉算法{for(v=0;v<G.vexnum;v++)D[v]=INFINITY;for(p=G.vertices[v0].firstarc;p;p=p->nextarc)D[p->adjvex]=*p->info; //给D数组赋初值for(v=0;v<G.vexnum;v++){final[v]=0;for(w=0;w<G.vexnum;w++) P[v][w]=0; //设空路径if(D[v]<INFINITY){P[v][v0]=1;P[v][v]=1;}}//forD[v0]=0;final[v0]=1; //初始化for(i=1;i<G.vexnum;i++){min=INFINITY;for(w=0;w<G.vexnum;w++)if(!final[w])if(D[w]<min) //尚未求出到该顶点的最短路径{v=w;min=D[w];}final[v]=1;for(p=G.vertices[v].firstarc;p;p=p->nextarc){w=p->adjvex;if(!final[w]&&(min+(*p->info)<D[w])) //符合迪杰斯特拉条件{D[w]=min+edgelen(G,v,w);P[w]=P[v];P[w][w]=1; //构造最短路径}}//for}//for}//ALGraph_DIJ分析:本算法对迪杰斯特拉算法中直接取任意边长度的语句作了修改.由于在原算法中,每次循环都是对尾相同的边进行处理,所以可以用遍历邻接表中的一条链来代替.。
8.10习题与上机操作⒈选择题⒉⑴ 1⑵n2⑶n-1⑷n(n+1)/2⑸设N=(V,E)是一连通网,U 是顶点集V的一个非空子集。
若(u ,v)是一条具有最小权值的边,其中u∈U,v∈V-U,则存在一棵包含边(u ,v)的最小生成树。
⑹ 5 6 4 2 1 , 5 6 2 1 4⑺非空有穷,可以⑻邻接矩阵,邻接表⑼构造最小生成树,加点法,加边法⑽路径长度递增,最短路径⒊简答题⑴对于存储结构采用邻接矩阵的无向图,如何判断下列有关问题?①图中有多少条边?答:矩阵中所有非0元素的个数的一半。
②任意两顶点间是否有边相连?答:若第i行第j列的元素非0,则说明i顶点和j顶点间有边,否则说明其没边。
③任意一个顶点的度是多少?答:第i行(列)中非0元素的个数即为第i个顶点的度。
⑵对于存储结构采用邻接表的有向图,如何判断下列有关问题?①图中有多少条边?答:邻接表中边(弧)结点的个数。
②图中是否存在从V i到V j的边?答:若在第i个单链表中存在值域为j的弧结点,则说明存在从V i到V j的边,否则说明不存在从V i到V j的边。
③如何求顶点V i的入度和出度?答:第i个单链表中边(弧)结点的个数为顶点V i的出度。
所有单链表中值域为i 的边(弧)结点的个数为顶点V i的入度。
⒋画图题⑴答:①g1的图示和图g1的邻接表如下图所示。
0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 01 1 0 0 1 0图g1② 图g1的邻接矩阵如下图所示:③ V1、V2、V3、V4、V5的度分别为:2,3,2,3,2⑵ 答:① g2的图示和g2的邻接矩阵如下所示:图g2 图g2的邻接矩阵② 图g2的邻接表和逆邻接表如下:③ V1到V6的入度依次为:3,1,0,1,2,0V1到V6的出度依次为:0,2,0,1,1,30 1 0 1 0 1 0 0 1 1 0 0 0 1 1 11 1 0 00 1 1 0 0图g2的邻接表 图g2的逆邻接表图g3的连通分量 答:g3不是连通图。
第七章图
1.下面是一个图的邻接表结构,画出此图,并根据此存储结构和深度优先搜索算法写出从C开始的深度优先搜索序列。
1
2
3
4
5
【解答】
A B F
C D E
C开始的深度优先搜索序列:CDEABF(唯一的结果)
2.假定要在某县所辖六个镇(含县城)之间修公路,若镇I和镇J 之间有可能通过道路连接,则Wij表示这条路的长度。
要求每个镇都通公路且所修公路总里程最短,那么应选择哪些线路来修。
(1).画出该图。
(2).用C语言描述该图的数组表示法存储结构,并注明你所使用变量
的实际含义。
(3).图示你所定义的数据结构。
(4).标识出你选择的线路。
【解答】 (1)
(2)
#define MAX 6 typedef struct {
char vexs[MAX]; // 顶点信息 int arcs[MAX][MAX]; // 边的信息
int vexnum, arcnum; // 顶点数,边数 } MGraph; (3)略
(4){(1,3), (3,4), (2,4), (4,5), (5,6)}
3.图G 如下所示。
(1).给出该图的所有强连通分量。
(2).在图中删除弧<2,1>,然后写出从顶点1开始的拓扑有序序列。
5
4
6
1
3
2
4
15 10
2
15
20
30
4
10
10
【解答】
(1) 共4个强连通分量:
(2) 1,3,2,6,5,4。
数据结构(C语言版)(第2版)课后习题答案数据结构(C语言版)(第2版)课后习题答案目录第1章绪论1 第2章线性表5 第3章栈和队列13 第4章串、数组和广义表26 第5章树和二叉树33 第6章图43 第7章查找54 第8章排序65 第1章绪论1.简述下列概念:数据、数据元素、数据项、数据对象、数据结构、逻辑结构、存储结构、抽象数据类型。
答案:数据:是客观事物的符号表示,指所有能输入到计算机中并被计算机程序处理的符号的总称。
如数学计算中用到的整数和实数,文本编辑所用到的字符串,多媒体程序处理的图形、图像、声音、动画等通过特殊编码定义后的数据。
数据元素:是数据的基本单位,在计算机中通常作为一个整体进行考虑和处理。
在有些情况下,数据元素也称为元素、结点、记录等。
数据元素用于完整地描述一个对象,如一个学生记录,树中棋盘的一个格局(状态)、图中的一个顶点等。
数据项:是组成数据元素的、有独立含义的、不可分割的最小单位。
例如,学生基本信息表中的学号、姓名、性别等都是数据项。
数据对象:是性质相同的数据元素的集合,是数据的一个子集。
例如:整数数据对象是集合N={0,±1,±2,。
},字母字符数据对象是集合C={‘A’,‘B’,。
,‘Z’,‘a’,‘b’,。
,‘z’},学生基本信息表也可是一个数据对象。
数据结构:是相互之间存在一种或多种特定关系的数据元素的集合。
换句话说,数据结构是带“结构”的数据元素的集合,“结构”就是指数据元素之间存在的关系。
逻辑结构:从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。
因此,数据的逻辑结构可以看作是从具体问题抽象出来的数学模型。
存储结构:数据对象在计算机中的存储表示,也称为物理结构。
抽象数据类型:由用户定义的,表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称。
具体包括三部分:数据对象、数据对象上关系的集合和对数据对象的基本操作的集合。
2.试举一个数据结构的例子,叙述其逻辑结构和存储结构两方面的含义和相互关系。
数据结构第七章的习题答案数据结构第七章的习题答案数据结构是计算机科学中非常重要的一门学科,它研究如何组织和管理数据以便高效地访问和操作。
第七章是数据结构课程中的一个关键章节,它涵盖了树和二叉树这两个重要的数据结构。
本文将为读者提供第七章习题的详细解答,帮助读者更好地理解和掌握这些概念。
1. 问题:给定一个二叉树,如何判断它是否是二叉搜索树?解答:要判断一个二叉树是否是二叉搜索树,我们可以使用中序遍历的方法。
中序遍历会按照从小到大的顺序访问二叉树中的节点。
所以,如果一个二叉树是二叉搜索树,那么它的中序遍历结果应该是一个有序的序列。
具体实现时,我们可以使用递归或者迭代的方式进行中序遍历,并将遍历的结果保存在一个数组中。
然后,我们检查这个数组是否是有序的即可判断二叉树是否是二叉搜索树。
2. 问题:给定一个二叉树,如何找到它的最大深度?解答:要找到一个二叉树的最大深度,我们可以使用递归的方式。
对于每个节点,它的最大深度等于其左子树和右子树中的最大深度加1。
递归的终止条件是节点为空,此时深度为0。
具体实现时,我们可以定义一个递归函数,该函数接收一个节点作为参数,并返回以该节点为根节点的子树的最大深度。
在递归函数中,我们先判断节点是否为空,如果为空则返回0;否则,我们分别计算左子树和右子树的最大深度,然后取两者中的较大值加1作为当前节点的最大深度。
3. 问题:给定一个二叉树,如何判断它是否是平衡二叉树?解答:要判断一个二叉树是否是平衡二叉树,我们可以使用递归的方式。
对于每个节点,我们分别计算其左子树和右子树的高度差,如果高度差大于1,则该二叉树不是平衡二叉树。
具体实现时,我们可以定义一个递归函数,该函数接收一个节点作为参数,并返回以该节点为根节点的子树的高度。
在递归函数中,我们先判断节点是否为空,如果为空则返回0;否则,我们分别计算左子树和右子树的高度,然后取两者中的较大值加1作为当前节点的高度。
最后,我们判断左子树和右子树的高度差是否大于1,如果大于1,则该二叉树不是平衡二叉树。
第七章习题答案1.填空题(1) 平均查找长度(2) 哈希查找(3)顺序 有序(4)越大(5) 21(n + 1) (6)开放定址法 拉链法(7)3(8)2(9)中序(10)建表2.判断题(1)×(2)×(3)√(4)√(5)√(6)√(7)√(8)√(9)×(10)×3.简答题(1)①表中只有一个关键字等于给定值k 的记录,无序表、有序表:顺序查找成功时,平均查找长度均为1/(n)*(1+2+…+n)=(n+1)/2;两者相同。
②无序表:顺序查找不成功时,平均查找长度为n+1;有序表,平均查找长度为1/(n+1)*(1+2+…+(n+1))=(n+2)/2;两者不相同。
③表中只有m 个关键字等于给定值k 的记录,无序表:ASL=n+1;有序表:ASL=(n+1)/2+m ;两者不相同。
(2)(3H(22)=(22 mod 7)+1=2 H(8)=(8 mod 7)+1=2 H(34)=(34 mod 7)+1=7 H(19)=(19 mod 7)+1=6 H(21)=(21 mod 7)+1=1 H(29)=(29 mod 7)+1=2 ①0 1 2 3 4 5 6 7 8②1 2 3 4 5 6 7 8 ③4.(1)int SeqSearch(RecordType r, int n, KeyType k){ i=0;r[n].key=k;while (r[i].key!=k) i++;if (i<n) return n;else return -1;}(2)KeyType MaxNode (BSTree bt){ if (bt!=NULL){ f=bt;while (bt->rchild!=NULL){ f=bt; bt=bt->rchild;}return f->key;}}(3)int level(BSTree bt, KeyType k){ d=0;if (bt==NULL) return 0; //空树深度为0else{ d++; //根结点深度为1while (bt->data!=k){ if (bt->data<k) bt=bt->rchild; //右子树中查找else bt=bt->rchild; //左子树中查找d++;}return d;}(4)#define N 50int IsBSTree(BSTree bt){BSNode *stack[N],*p;KeyType k;int isbst,top;isbst=1; //标志变量,首先假定bt是二叉排序树top=-1; //置空栈p=bt; //p从根出发while (isbst && (p!=NULL || top!=-1)){while ((p!=NULL){k=p->key;if (p->lchild->key>k || p->rchild->key<k){ isbst=0; break;}if (p->rchild!=NULL)stack[++top]=p->rchild;p=p->lchild;}if (top!=-1)p=stack[top--];}return isbst;}(5)BSTree BSTDeleteAll(BSTree t, KeyType k){BSTree p;p=BSTSearch1(t,k); //查找关键字值不小于x的结点while (p!=NULL){ t=BSTDelete(t,p); //删除p所指示结点p=BSTSearch1(t,k);}}注:函数BSTSearch1,BSTDelete参阅教材§7.3.3,§7.3.4。
9.7.1 习题与上机操作9.7.1习题⒈⒉⑴哈希表查找法⑵ 15⑶⎣⎦1log 2+n⑷结点数n ,生成过程⑸越大,越小⑹ 1(21n ASL +=1log 11(2-+=+n n n ASL1(21++=s snASL⑺顺序存储结构有序的⑻右子树⑼素数⑽ n n+1 ⒊应用题⑴顺序表类型定义#define MAX 100 /* 顺序表长度*/ typedef int elemtype /* 关键字类型为整数*/typedef struct{ elemtype elem[MAX+1] ;/*查找表数据元素存储空间基址,建表时按实际长度分配,0号单元留空*/ int length ; /*表中元素个数*/ }SStable ; /*顺序表类型*/ 顺序查找算法(监视哨设在高下标端 int SeqSearch(SStable ST, keytype kx {int i ;ST.elem[length+1].key=kx; /*设置哨兵*/ i=1;while (ST.elem[i].key != kx /*从表尾开始查找*/ i++ ;if (i == ST.length +1printf(“Searching Fail!\n”;elseprintf(“Searching Success!\n”;return( i ; /*找到,返回结点的序号,否则返回0*/ }在等概率情况下, 表长为n:查找成功的平均查找长度ASL=21n查找不成功的平均查找长度ASL=n+1⑵对有序数据表(5,7,9,12,15,18,20,22,25,30,100,用折半查找方法查找关键字为12和28的数据。
①查找关键字kx=12的过程:↑↑↑low mid high 因为12<18,修改high=mid-1=5,重新选择mid。
↑↑↑low mid high因为12>9,修改low=mid+1=4,重新选择mid。
数据结构(C语言版)第三版__清华大学出版社_习题参考答案数据结构(C语言版)第三版__清华大学出版社_习题参考答案引言:数据结构是计算机科学的基础,对于学习和理解数据结构的相关概念和算法非常重要。
本文将对清华大学出版社出版的《数据结构(C语言版)第三版》中的习题进行参考答案的提供。
通过正确的理解和掌握这些习题的解答,读者可以加深对数据结构的认识,并提高自己的编程能力。
第一章:绪论1.1 数据结构的定义与作用数据结构是指数据对象以及数据对象之间的关系、运算和存储结构的总称。
数据结构的作用是在计算机中高效地组织和存储数据,同时支持常见的数据操作和算法。
1.2 算法的定义与特性算法是解决特定问题的一系列步骤和规则。
算法具有确定性、有穷性、可行性和输入输出性等特点。
第二章:线性表2.1 线性表的定义和基本操作线性表是同类型数据元素的一个有限序列。
线性表的基本操作包括初始化、查找、插入、删除和遍历等。
2.2 顺序存储结构顺序存储结构是将线性表中的元素按顺序存放在一块连续的存储空间中。
顺序存储结构的特点是随机存取、插入和删除操作需要移动大量元素。
2.3 链式存储结构链式存储结构通过结点之间的指针链表来表示线性表。
链式存储结构的特点是插入和删除操作方便,但查找操作需要遍历整个链表。
第三章:栈和队列3.1 栈的定义和基本操作栈是只能在一端进行插入和删除操作的线性表。
栈的基本操作包括初始化、入栈、出栈和获取栈顶元素等。
3.2 队列的定义和基本操作队列是只能在一端插入操作,在另一端进行删除操作的线性表。
队列的基本操作包括初始化、入队、出队和获取队头元素等。
第四章:串4.1 串的定义和基本操作串是由零个或多个字符组成的有限序列。
串的基本操作包括初始化、串的赋值、串的连接和串的比较等。
第五章:树5.1 树的基本概念和术语树是n(n>=0)个结点的有限集。
树的基本概念包括根结点、子树、深度和高度等。
5.2 二叉树二叉树是每个结点最多有两个子树的树结构。
7.8 习题与上机操作1.选择题:2.填空题:⑴ 3 4 6 1 2 A F G⑵ 5⑶ n2+1⑷完全 log2n +1 最大高度 n⑸ 2K-1 2K-1 2K-1⑹ 129⑺ 99⑻ 2n n-1 n+1⑼ n+1⑽ 55⑾ 2m-13. 程序填空题:⑴① bt ②p ③p=p->lchild ④p=s.elem[--s.top] ⑤p=p->rchild⑵①top->addr=bt ②top!=null ③top->addr ④t&&(t->lchild||t->rchild)⑤t->lchild ⑥t->rchild4.应用题:⑴ 答案A⑵ 答案:⑶ 答案:①先序序列和中序序列相同的二叉树为空树或任一结点均无左孩子的非空二叉树②中序和后序序列相同的二叉树为空树或任一结点均无右孩子的非空二叉树③先序和后序序列相同的二叉树为空树或仅有一个结点⑷ 答案:(A) (B) (C)⑸答案:⑹答案:的00于010为0110得01110在01111是10有1100地1101和11105.编程题:⑴ 设计一个算法,判定给定二叉树是否为二叉排序树。
①编程思想:用先序算法建立一棵二叉树,设置好二叉树初始标志为flag为1,在中序遍历二叉树时若后一个节点值大于前一个,则用max记录下这个大值,若后一个结点值小于前一个结点值(由max记录),则不为二叉排序树,设置flag为0,当树遍历结束后若flag 值为1,则二叉树为二叉排序树。
在此程序中,用0表示树为空树。
②C源程序:#define NULL 0int flag=1,max,k=0;typedef struct BitNode{int data;struct BitNode *lchild, *rchild;}BitNode,*BinTree;void sortBitree(BinTree T) /*判断二叉树是否排序树,若否flag设为0*/{if(T){sortBitree(T->lchild);k++;if(k==1)max=T->data;else{ if(T->data>max)max=T->data;elseflag=0;}sortBitree(T->rchild);}}void CreateBinTree(BinTree *root)scanf("%d",&x);if (x==0)*root=NULL;else{*root=(BinTree)malloc(sizeof(BitNode));(*root)->data=x;CreateBinTree(&((*root)->lchild));CreateBinTree(&((*root)->rchild));}}main(){ BinTree BT;CreateBinTree(&BT);sortBitree(BT);if(flag==1)printf("It is a sorted tree");if(flag==0)printf("It is not a sorted tree");}⑵试写出对一棵二叉树中所有结点的左右子树相互交换的算法。
①编程思想:为验证二叉树通过算法是否成功交换,本程序用CreateBinTree()建立了一棵二叉树,调用exchange()方法进行左右子树的交换,PreOrder()方法输出树的结点值,用以验证二叉树左右子树是否成功交换。
exchange()方法主要思路是判当前树是否空树,若非空,再判断该树的左右子树是否同时为空,只要不同时为空,则交换左右子树。
②C源程序:#define NULL 0typedef struct BitNode{int data;struct BitNode *lchild, *rchild;}BitNode,*BinTree;void PreOrder(BinTree r){if (r){ PreOrder(r->lchild);printf(" %d",r->data);PreOrder(r->rchild);}}void exchange(BinTree T) /* 交换左右子树*/{BinTree p;if(T){if(T->lchild||T->rchild){p=T->lchild;T->lchild=T->rchild;T->rchild=p;}exchange(T->lchild);exchange(T->rchild);}}void CreateBinTree(BinTree *root) /* 建立子树*/{int x;scanf("%d",&x);if (x==0)*root=NULL;else{*root=(BinTree)malloc(sizeof(BitNode));(*root)->data=x;CreateBinTree(&((*root)->lchild));CreateBinTree(&((*root)->rchild));}}main(){BinTree BT;CreateBinTree(&BT);exchange(BT);PreOrder(BT);}⑶设计一个算法,要求该算法把二叉树的叶子结点按从左到右的顺序连成一个单链表,表头指针为head。
二叉树按二叉链表方式存储,链接时用叶子结点的右指针域来存放单链表指针。
①编程思想:为验证二叉树叶子结点是否连成一个单链表,本程序用CreateBinTree()建立了一棵二叉树,调用leaf_link ()方法进行叶子结点的连接,printLeaf()方法输出由单链表连接的叶子结点的值,用以验证二叉树左右子树是否成功交换。
leaf_link()的主要思路是判定树是否为空,若非空,则判断结点的左右子树是否同时为空,若同时为空,则该结点为叶结点,并单链表的链接方法把该结点加入到叶结点生成的单链表中。
②C源程序:#define NULL 0typedef struct BitNode{int data;struct BitNode *lchild, *rchild;}BitNode,*BinTree;BinTree head,tail;void leaf_link(BinTree T) /* 叶子结点生成单链表* /{if(T){leaf_link(T->lchild);if(!T->lchild&&!T->rchild){ if(head==NULL){head=T;tail=T;}else{tail->rchild=T;tail=T;}}leaf_link(T->rchild);}}void printLeaf( BinTree head) /*打印叶子结点*/{while(head){printf(" %d",head->data);head=head->rchild;}}void CreateBinTree(BinTree *root){int x;scanf("%d",&x);if (x==0)*root=NULL;else{*root=(BinTree)malloc(sizeof(BitNode));(*root)->data=x;CreateBinTree(&((*root)->lchild));CreateBinTree(&((*root)->rchild));}}main(){BinTree BT;CreateBinTree(&BT);leaf_link(BT);printLeaf(head);}⑷已知一棵以线索链表为存储结构的中序线索二叉树T,设计一个算法,试在该二叉树上求任意结点x的中序后继。
①编程思想:对于结点x,若要找其后继结点,当x->Rtag=1时,x->RChild即为x的后继结点;当x->Rtag=0时,说明x有右子树,此时x的中序后继结点即为其右子树的最左下端的结点。
②C源程序:#include "stdio.h"#define NULL 0typedef char Elemtype;typedef struct thread{Elemtype data;struct thread *LChild;struct thread *RChild;short int Ltag;short int Rtag;}ThreadNode,*ThreadTree;ThreadTree pre;ThreadTree insucc(ThreadTree p) /* 找出p的后继结点*/ {if(p->Rtag==1)return p->RChild;else{ p=p->RChild;while(p->Ltag==0) p=p->LChild;return p;}}void CreateBinTree(ThreadTree *root) /* 建立树*/{char x;x=getchar();if (x=='#')*root=NULL;else{*root=(ThreadTree)malloc(sizeof(ThreadNode));(*root)->data=x;CreateBinTree(&((*root)->LChild));CreateBinTree(&((*root)->RChild));}}void InThread(ThreadTree root){if (root!=NULL){InThread(root->LChild);if (root->LChild==NULL){root->Ltag=1;root->LChild=pre;}if (pre!=NULL && pre->RChild==NULL) /* 置后继线索 */{pre->RChild=root;pre->Rtag=1;}pre=root;InThread(root->RChild); /* 线索化右子树 */}}main(){ThreadTree T,q;CreateBinTree(&T);InThread(T);q=insucc(T);printf("%c ",q->data);}⒍操作题⑴已知一棵二叉树按顺序方式存储在数组A[1..n]中。