PTA advanced 1021 Deepest Root

博客通过非递归栈模拟深度遍历解决PTA问题1021,寻找树中最长路径的端点。两次遍历确定所有最长路径,第一次记录起点最长路径尾端点,第二次从这些端点开始再次遍历。最终,两次遍历的尾端点组合即为最长路径的所有端点。

//使用非递归栈模拟深度遍历,否则此题用递归深搜会栈溢出
//本题的题意其实就是找出一棵树中所有最长路径的端点
//只需要遍历两次即可找到长度最长的路径的所有端点
//第一次遍历从任意一点开始,记录以这个点为起点的最长路径的尾端点(可能不止一个)
//第二次遍历从第一次的尾端点中任选一个为起点,再次记录此次遍历过程中最长路径的尾端点
//两次遍历的尾端点放在一块就是整个树中,最长路径的所有端点了
//(注意,第一次遍历和第二次遍历中记录的尾端点可能有重合,当第一次遍历起点恰好是最长路径中点时,便会重合)

//ac
#define MAXN 10000
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;

struct Node{
    int neighbors[MAXN];     //结点的邻居
    int visIdx;             //结点已访问到的邻居的下标
    int size;               //结点的邻居数
};

struct Stack{               //栈
    int data[MAXN];         //存放nodes数组下标
    int top;               //指向栈顶元素的下一个,初始为0
};

struct Gather{              //记录尾端点的集合
    int data[MAXN];         //存放nodes数组下标
    int size;               //尾端点数,初始为0
    //路径长度,初始为0  当遍历到一路径的长度比dph大时,更新dph,清空data,并加入尾端点
    //当遍历到一路径的长度等于dph时,直接把尾端点加入到data中
    int dph;                
};

Node* nodes[MAXN];           //所有结点
bool visited[MAXN];          //访问标志数组
Stack s;
Gather gat1;                 //存放第一次遍历的尾端点
Gather gat2;                 //存放第二次遍历的尾端点

void traversal(int st,Gather* gat){         //使用栈的深度遍历,从st开始的最长路径的尾端点会被记录在gat中
    /*
    这种遍历都是一个套路!!
    先入栈一个起点,然后当栈不空时一个大循环
    循环里面
    第一:访问栈顶元素(visited=true),检查栈顶元素是否命中,命中了就怎么怎么样啊巴拉巴拉,
        这个根据自己想做什么自己写
    第二:寻找栈顶元素的下一个访问元素,就是找栈顶元素的没访问过的下一个邻居,用node->visIdx记录访问到哪一个邻居了
        用visited检擦这个一邻居有没有访问过
    第三:如果我的邻居全部都访问完了(node->visIdx==node->size),那就弹出栈顶元素(top--);否则入栈我找到的这个邻居
    */
    s.top=0;
    s.data[s.top++]=st;        //起点入栈
    while(s.top!=0){           //当栈不空时一直循环
        //此处为查看遍历过程
        // for(int i=0;i<s.top;i++){
        //     printf("%d ",s.data[i]);
        // }
        // printf("\n");
        int topEmt=s.data[s.top-1];      //topEmt表示栈顶元素在nodes数组中的下标
        //以下检查栈顶是否命中
        visited[topEmt]=true;
        if(s.top > gat->dph){        //s.top恰好可以代表当前路径的深度,如果当前路径更深,那就更新
            gat->size=0;
            gat->dph=s.top;
            gat->data[gat->size++]=topEmt;
        }else if(s.top==gat->dph){   //如果当前路径一样深,那检查有没有记录过这个尾端点,没有的话就记录它
            bool tmp=false;
            for(int i=0;i<gat->size;i++){
                if(gat->data[i]==topEmt){
                    tmp=true;
                    break;
                }
            }
            if(!tmp)
                gat->data[gat->size++]=topEmt;
        }
        //寻找栈顶的下一个结点
        for(;nodes[topEmt]->visIdx < nodes[topEmt]->size;nodes[topEmt]->visIdx++){
            if(!visited[nodes[topEmt]->neighbors[nodes[topEmt]->visIdx]]){
                break;
            }
        }
        //如果找到了,入栈,如果没找到,弹出栈顶元素
        if(nodes[topEmt]->visIdx==nodes[topEmt]->size){
            s.top--;
        }else{
            //入栈
            s.data[s.top++]=nodes[topEmt]->neighbors[nodes[topEmt]->visIdx];
            nodes[topEmt]->visIdx++;   
        }
    }
}

bool cmp(int a,int b){
    return a<b;
}
int main(){
    int N;
    scanf("%d",&N);
    //初始化nodes,nodes[0]是不使用的
    for(int i=0;i<=N;i++){
        nodes[i]=(Node*)malloc(sizeof(Node));
        nodes[i]->size=0;
        nodes[i]->visIdx=0;
    }
    //添加邻居
    for(int i=1;i<=N-1;i++){
        int ti,tj;
        scanf("%d %d",&ti,&tj);
        nodes[ti]->neighbors[nodes[ti]->size++]=tj;
        nodes[tj]->neighbors[nodes[tj]->size++]=ti;
    }
    gat1.size=0;
    gat1.dph=0;
    gat2.size=0;
    gat2.dph=0;

    fill(visited,visited+MAXN,false);
    int cndNum=0;          //连通分支数
    for(int i=1;i<=N;i++){           
        //检查所有结点,如果没访问过就从它为起点进行遍历
        //如果只有一个连通分支,那第一次遍历就可以使所有结点都visit过
        //如果有多个连通分支,那就从以下一个没有访问过的结点为起点再遍历
        //有几个连通分支,就需要遍历几次
        if(!visited[i]){
            cndNum++;
            traversal(i,&gat1);
        }
    }
    if(cndNum>1){
        printf("Error: %d components\n",cndNum);
        return 0;
    }
    //只有一个连通分支的情况下,直接进行第二次遍历
    //这里要重新初始化visited,以及所有结点的visIdx
    fill(visited,visited+MAXN,false);
    for(int j=1;j<=N;j++){
        nodes[j]->visIdx=0;
    }
    //尾端点存到gat2中
    traversal(gat1.data[0],&gat2);
    //合并gat2和gat1到gat1中
    for(int i=0;i<gat2.size;i++){
        //合并前要先检查gat1中是否已经有gat2中的元素了
        int j=0;
        for(;j<gat1.size;j++){
            if(gat1.data[j]==gat2.data[i]){
                break;
            }
        }
        if(j==gat1.size){
            gat1.data[gat1.size++]=gat2.data[i];
        }
    }
    //按序号从小到大排序
    sort(gat1.data,gat1.data+gat1.size,cmp);
    //输出结果
    for(int i=0;i<gat1.size;i++){
        printf("%d\n",gat1.data[i]);
    }
}

感谢各位耐着性子看我的代码,不足之处欢迎指点。
努力努力再努力!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值