考研——图算法大题整理(二)

一、路径问题

1. 判断顶点i和j之间是否有路径

方法1:利用DFS
// DFS
// 从i开始深度遍历,若i和j之间有路径,
// 则j会被访问过,即visited[j] = true;
bool visited[MaxSize];
bool has_path(AGraph* G, int i, int j)
{
    memset(visited, false, sizeof visited);
    dfs(G, i);
    if (visited[j]) return true;
    return false;
}
void dfs(AGraph* G, int u)
{
    visited[u] = true;
    auto p = G->adjlist[u].firstarc;
    while (p)
    {
        auto v = p->adjvex;
        if (!visited[v]) dfs(G, v);
        p = p->nextarc;
    }
}
方法2:利用BFS
// bfs
// 从i开始宽度遍历,若i和j之间有路径,则有2种检索方式:
// 1. j会被访问过,即visited[j] = true;
// 2. j入过队列,在出队时可以进行判断
// 两种方法都可以,因为dfs用的是方法1,那此处bfs采用方法2
bool visited[MaxSize];
bool has_path(AGraph* G, int i, int j)
{
    memset(visited, false, sizeof visited);
    queue<int> q;
    q.push(i);
    visited[i] = true;
    while (q.size())
    {
        auto t = q.front();
        q.pop();
        if (t == j) return true;    // 关键的检索步骤
        auto p = G->adjlist[t].firstarc;
        while (p)
        {
            auto v = p->adjvex;
            if (!visited[v])
            {
                q.push(v);
                visited[v] = true;
            }
            p = p->nextarc;
        }
    }
    return false;
}

2. 输出从顶点i到j所有的简单路径

类似题目:二叉树根结点到叶子结点的所有路径

// 类比一下找二叉树根结点到叶子结点路径的问题
bool visited[MaxSize];
vector<int> path;
void find_path(AGraph* G, int i, int j)
{
    memset(visited, false, sizeof visited);
    dfs(G, i, j);
}
void dfs(AGraph* G, int u, int v)
{
    visited[u] = true;
    path.push_back(u);
    if (u == v)
    {
        for (auto pa : path) cout << pa << ' ';
        puts("");
        return;
    }
    auto p = G->adjlist[u].firstarc;
    while (p)
    {
        auto w = p->adjvex;
        if (!visited[w]) dfs(G, w, v);
        p = p->nextarc;
    }
    path.pop_back();
    visited[u] = false;
}

3. 求不带权无向连通图G中距离顶点u最远的一个结点

// 注意题目条件:无向连通图
// 无向连通图中,每2个结点之间都存在着路径,bfs能找到距离顶点u最远的点
int bfs(AGraph* G, int u)
{
    bool visited[MaxSize];
    memset(visited, false, sizeof visited);
    queue<int> q;
    vector<int> res;
    q.push(u);
    visited[u] = true;
    while (q.size())
    {
        auto t = q.front();
        q.pop();
        res.push_back(t);
        auto p = G->adjlist[t].firstarc;
        while (p)
        {
            auto v = p->adjvex;
            if (!visited[v])
            {
                q.push(v);
                visited[v] = true;
            }
            p = p->nextarc;
        }
    }
    return res[res.size() - 1];
}

4. 打印有向图G中的根结点

根结点定义:结点rG中每个结点都有路径,则称r为G的根结点

bool visited[MaxSize];
int cnt; // 表示dfs访问过的结点数,访问1个结点,cnt + 1
void has_root(AGraph* G)
{
    for (int i = 0; i < G->vexnum; i ++)
    {
        memset(visited, false, sizeof visited);
        cnt = 0;
        dfs(G, i);
        if (cnt == G->vexnum) cout << i << ' ';
    }
}
void dfs(AGraph* G, int u)
{
    visited[u] = true;
    cnt ++;
    auto p = G->adjlist[u].firstarc;
    while (p)
    {
        auto v = p->adjvex;
        if (!visited[v]) dfs(G, v);
        p = p->nextarc;
    }
}

二、连通与环问题

1. 判断无向图G是否是一棵树

注意:树的特点是含有n - 1条边的连通图,n为顶点个数

// 重点:图的边数 = 其结点数-1,图连通
// n个结点:设置一个计数器vn,用以记录访问过的结点
// n - 1条边:设置一个计数器en,用以记录访问过的边
// 连通:可以比较dfs之后访问过的结点数vn和图中顶点数vexnum是否相等判别
bool visited[MaxSize];
int vn = 0, en = 0;
bool is_tree(AGraph* G)
{
    memset(visited, false, sizeof visited);
    dfs(G, 1);
    if (vn == G->vexnum && en / 2 == G->vexnum - 1) return true;
    return false;
}
void dfs(AGraph* G, int u)
{
    visited[u] = true;
    vn ++;
    auto p = G->adjlist[u].firstarc;
    while (p)
    {
        en ++;
        auto v = p->adjvex;
        if (!visited[v]) dfs(G, v);
        p = p->nextarc;
    }
}

2. 判断有向图中是否存在经过给定顶点u的环

// 感觉有点难理解,直接背吧。。天勤P232
/* 对于有向图,如果从有向图的某个顶点u出发的遍历,
   在DFS(u)结束之前出现了一条从顶点v指向u的边,则此有向图必定存在环*/
bool visited[MaxSize];
bool has_circle(AGraph* G, int u)
{
    memset(visited, false, sizeof visited);
    if (dfs(G, u)) return true;
    return false;
}
bool dfs(AGraph* G, int u)
{
    bool flag;  // 如果有环,flag = true
    visited[u] == true;
    auto p = G->adjlist[u].firstarc;
    while (p)
    {
        auto v = p->adjvex;
        if (visited[v]) return true;
        else flag = dfs(G, v);
        if (flag == true) return true;
        visited[v] = false; 
        // 最后一句的含意,我的理解是:
        // 递归完一整条路径(最后一个顶点已经没有出边了)时,
        // 会从最后一个点依次往前抹除遍历痕迹,直到某个点i还存在未访问的邻接点
        // 然后从该点i一边继续往下dfs新的路径,一边对新加入路径的顶点进行访问查询
    }
    return false;
}

三、结点度数问题

1. 求有向图G中顶点k的入度

int cal_degree(AGraph* G, int k)
{
    int degree = 0; // 记录结点k的入度
    for (int i = 0; i < G->vexnum; i ++)
    {
        auto p = G->adjlist[i].firstarc;
        while (p)
        {
            auto v = p->adjvex;
            if (v == k)
            {
                degree ++;
                break;
            }
        }
        p = p->nextarc;
    }
    return degree;
}

2. 判断无向连通图G是否存在EL路径

EL路径:当G中度为奇数的顶点个数为不大于2的偶数时,就会存在此路径

// 重点:
// 对于无向图,邻接矩阵的每一行(列)中,非零元素的个数等于本行(列)对应顶点的度
// 对于有向图,邻接矩阵的每一行(列)中,非零元素的个数等于本行(列)对应顶点的出(入)度
// 方法1:采用邻接矩阵存储(原题)
int dist[MaxSize];  // 记录无向图中各顶点的度数
bool has_EL(MGraph G)
{
    memset(dist, 0, sizeof dist);
    for (int i = 0; i < G.vexnum; i ++)
        for (int j = 0; j < G.vexnum; j ++)
            dist[i] += G.edges[i][j];
    int cnt = 0;
    for (int i = 0; i < G.vexnum; i ++)
        if (dist[i] % 2) cnt ++;
    if (cnt == 0 || cnt == 2) return true;
    return false;
}
// 重点:
// 对于无向图,邻接表中,一个顶点的度数等于该顶点的邻接边条数
// 对于有向图,邻接表中,一个顶点的出度数等于该顶点的邻接边条数
				  // 而一个顶点的入度数就不是这样了
// 方法2:采用邻接表存储(发散)
int dist[MaxSize];  // 记录无向图中各顶点的度数
bool has_EL(MGraph G)
{
    memset(dist, 0, sizeof dist);
    for (int i = 0; i < G.vexnum; i ++)
    {
        auto p = G.adjlist[i].firstarc;
        while (p)
        {
            dist[i] ++;
            p = p->nextarc;
        }
    }
    int cnt = 0;
    for (int i = 0; i < G.vexnum; i ++)
        if (dist[i] % 2) cnt ++;
    if (cnt == 0 || cnt == 2) return true;
    return false;
}

最后——杂七杂八

1. 写出图的深度优先遍历DFS算法的非递归形式

bool visited[MaxSize];
void DfsTraverse(AGraph* G)	// 用的指针
 {
     memset(visited, false, sizeof visited);
     for (int i = 0; i < G->vexnum; i ++)
        if (!visited[i]) dfs(G, i);
 }
 // 图的dfs类似树的preorder
 void dfs(AGraph* G, int u)
 {
     stack<int> s;
     s.push(u);
     visited[u] = true;
     while (s.size())
     {
         auto t = s.top();
         s.pop();
         visit(t);
         auto p = G->adjlist[t].firstarc;
         while (p)
         {
             auto v = p->adjvex;
             if (!visited[v])
             {
                 s.push(v);
                 visited[v] = true;
             }
             p = p->nextarc;
         }
     }
 }

2. 写出无向图G从邻接表表示转换成邻接矩阵表示的算法

void AGraph_to_MGraph(AGraph* G1, MGraph & G2)
{
    // 将邻接矩阵置0
    for (int i = 0; i < G2.vexnum; i ++)
        for (int j = 0; j < G2.vexnum; j ++) 
            G2.edges[i][j] = 0;
    // 扫描邻接表给邻接矩阵赋值
    for (int i = 0; i < G1->vexnum; i ++)
    {
        auto p = G1->adjlist[i].firstarc;
        while (p)
        {
            auto j = p->adjvex;
            G2.edges[i][j] = 1;
            p = p->nextarc;
        }
    }
    // 边数和顶点数别忘了赋值给邻接矩阵
    G2.vexnum = G1->vexnum;
    G2.edgenum = G2->edgenum;
}
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值