【数据结构】无向图的先深遍历和先广遍历

Problem A 图的先深搜索

输出无向图的给定起点的先深序列。

输入格式:

输入第一行给出三个正整数,分别表示无向图的节点数N(1<N≤10)、边数M(≤50)和探索起始节点编号S(节点从1到N编号)。

随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个节点的编号。

输出格式:

输出从S开始的无向图的先深搜索序列,用一个空格隔开,最后也有一个空格;如果为非连通图,再在结尾处另起一行输出一个0,表示此图非连通。

由于深度优先遍历的节点序列是不唯一的,为了使得输出具有唯一的结果,我们约定以表头插入法构造邻接表。

输入样例1:

6 8 2
1 2
2 3
3 4
4 5
5 6
6 4
3 6
1 5

输出样例1:

2 3 6 4 5 1 

输入样例2:

4 3 1
1 2
2 3
3 1

输出样例1:

1 3 2 
0

思路

首先要明确无向图深度优先遍历的序列以及代码表示。

这道题要注意输入的无向图有可能是非连通的,可以通过比较一趟深度优先搜索后遍历结点的次数于该图的结点总数是否相等来判断该无向图是否连通,用人话说就是如果一次遍历不能全部遍历完所有结点,那这个图就是非连通图。下一步就是图的存储结构问题,这道题目明确说明要用头插法来构造邻接表,即注意要进行两次headInsert操作,否则生成的是有向图的邻接表

实际上用头插法构造邻接表部分可以使用Vector数组来模拟。如图所示,定义一个Vector[]数组,并对每个元素进行push_back操作即可实现在结点后插入结点,再使用倒序遍历即可模拟出邻接表头插法的效果,妙!

Vector数组模拟邻接表

代码

#include <bits/stdc++.h>
using namespace std;

#define MAX_VERTEX_NUM 20 // 最大结点数量
#define VertexType int
bool visited[MAX_VERTEX_NUM];
int cnt;

typedef struct ArcNode {
    
    
    int adjvex; // 邻接顶点
    struct ArcNode *nextarc; // 下一条边指针
} ArcNode;

typedef struct VexNode {
    
    
    VertexType data;
    ArcNode *firstarc; // 邻接的顶点
} VexNode, AdjList[MAX_VERTEX_NUM];

typedef struct ALGraph {
    
    
    AdjList vertices;
    int vexnum, arcnum; // 结点数和边数
    int startNode; // 开始结点
} ALGraph;

/// @brief 头插法插入结点,在结点vex1后插入结点vex2
/// @param alg 有向图
/// @param vex1 结点vex1
/// @param vex2 结点vex2
void headInsert(ALGraph &alg, int vex1, int vex2) {
    
    
    ArcNode *p, *q;
    p = (ArcNode *)malloc(sizeof(ArcNode)); // 给新的边结点分配地址
    p->adjvex = vex2; // 边的邻接结点是vex2
    if (!alg.vertices[vex1].firstarc) {
    
     // 头插法插入结点
        alg.vertices[vex1].firstarc = p;
        p->nextarc = NULL;
    } else {
    
    
        q = alg.vertices[vex1].firstarc;
        p->nextarc = q;
        alg.vertices[vex1].firstarc = p;
    }
} // headInsert

/// @brief 初始化有向图,构造有向图邻接表
/// @param alg 有向图alg
void initGraph(ALGraph &alg) {
    
    
    int vex1, vex2;
    cin >> alg.vexnum; // 结点数
    cin >> alg.arcnum; // 边数
    cin >> alg.startNode; // 起始结点
    for (int v = 1; v <= alg.vexnum; v++) {
    
    
        alg.vertices[v].firstarc = NULL; // 先讲邻接表的每个元素指向null
        alg.vertices[v].data = v; // 结点data为自身索引
    }
    for (int v = 1; v <= alg.arcnum; v++) {
    
    
        cin >> vex1 >> vex2; // 输入边
        headInsert(alg, vex1, vex2);
        headInsert(alg, vex2, vex1);
    }
} // initGraph

/// @brief 深度优先遍历,计算结点遍历的次数,若一次优先遍历无法遍历所有结点,说明该无向图为非连通图
/// @param alg 
/// @param v 
void DFS(ALGraph &alg, int v) {
    
    
    visited[v] = true; // 说明该结点已被访问过
    cout << alg.vertices[v].data << " ";
    cnt++;
    for (ArcNode *an = alg.vertices[v].firstarc; an != NULL; an = an->nextarc) {
    
    
        if (!visited[an->adjvex]) {
    
    
            DFS(alg, an->adjvex);
        }
    }
} // DFS

int main() {
    
    
    ALGraph alg;
    initGraph(alg);
    DFS(alg, alg.startNode);
    cout << (cnt == alg.vexnum ? "" : "\n0");
}

另附东宝的Vector版:https://blog.csdn.net/qq_60755115/article/details/127741122

#include <iostream>
#include <vector>
using namespace std;

#define MAX_EDGE 20

vector<int> Adjvex[MAX_EDGE];
bool visited[MAX_EDGE];

int vex, edge, start, v1, v2, count;

void dfs(int v) {
    
    
    visited[v] = true;
    cout << v << " ";
    count++;
    for (int i = Adjvex[v].size() - 1; i >= 0; --i) {
    
    
        if (!visited[Adjvex[v][i]]) {
    
    
            dfs(Adjvex[v][i]);
        }
    }
}

int main() {
    
    
    cin >> vex >> edge >> start;
    for (int i = 0; i < edge; i++) {
    
    
        cin >> v1 >> v2;
        Adjvex[v1].push_back(v2);
        Adjvex[v2].push_back(v1);
        visited[i] = false;
    }
    dfs(start);
    cout << (count == vex ? "" : "\n0");
}

Problem B 图的先广搜索

输出无向图的给定起点的先广序列。

输入格式:

输入第一行给出三个正整数,分别表示无向图的节点数N(1<N≤10)、边数M(≤50)和探索起始节点编号S(节点从1到N编号)。

随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个节点的编号。

输出格式:

输出从S开始的无向图的先广搜索序列,用一个空格隔开,最后也有一个空格;如果为非连通图,再在结尾处另起一行输出一个0,表示此图非连通。

由于广度优先遍历的节点序列是不唯一的,为了使得输出具有唯一的结果,我们约定以表头插入法构造邻接表。

输入样例:

6 8 2
1 2
2 3
3 4
4 5
5 6
6 4
3 6
1 5

输出样例:

2 3 1 6 4 5 

思路

首先要明确无向图广度优先遍历的序列以及代码表示。

图的广度优先搜索需要注意使到队列进行操作。当遍历到该结点时先进行入队列操作,后续还需要通过它来找到其连通结点,因此可以利用队列对已经遍历的结点进行存储。和上题一样,需要判断图是否连通。

代码

#include <bits/stdc++.h>
using namespace std;

#define MAX_VERTEX_NUM 20
#define MAX_QUEUE_SIZE 30
#define VertexType int

int vex, arc, start, v1, v2, cnt;
bool visited[15];

typedef struct ArcNode {
    
    
    int adjvex; // 边的邻接点
    struct ArcNode *nextarc; // 下一条边指针
} ArcNode;

typedef struct VexNode {
    
    
    VertexType data; // 数据
    ArcNode *firstarc; // 邻接边
} VexNode, Adjvex[MAX_VERTEX_NUM];

typedef struct ALGraph {
    
    
    Adjvex vertices; // 邻接表
    int vexnum, arcnum; // 结点数和边数
    int startNode; // 开始结点
} ALGraph;

typedef struct Queue {
    
    
    int head, tail; // 头指针和尾指针
    int *base; // 队列的基指针
} Queue;

Queue q;

/// @brief 初始化循环队列
/// @param q 
void InitQueue(Queue &q) {
    
    
    q.head = 0;
    q.tail = 0;
    q.base = (int *)malloc(MAX_QUEUE_SIZE * sizeof(int));
}

/// @brief 向队尾添加元素
/// @param q 
/// @param elem 
void EnQueue(Queue &q, int elem) {
    
    
    q.base[q.tail] = elem;
    q.tail = (q.tail + 1) % MAX_QUEUE_SIZE;
}

/// @brief 将队首元素弹出队列
/// @param q 
void DeQueue(Queue &q) {
    
    
    q.head = (q.head + 1) % MAX_QUEUE_SIZE;   
}

/// @brief 获取队首元素
/// @param q 
/// @return 
int GetFront(Queue q) {
    
    
    return q.base[q.head];
}

/// @brief 判断循环队列是否为空
/// @param q 
/// @return 
bool QueueEmpty(Queue q) {
    
    
    return q.head == q.tail;
}

/// @brief 广度优先搜索
/// @param alg 
/// @param vex 
void BFS(ALGraph &alg, int vex) {
    
    
    InitQueue(q); // 初始化队列
    EnQueue(q, vex); // 首先将起始节点加入队列
    visited[vex] = true;
    cout << alg.vertices[vex].data << " ";
    cnt++;
    while (!QueueEmpty(q)) {
    
    
        vex = GetFront(q);
        DeQueue(q);
        for (ArcNode *an = alg.vertices[vex].firstarc; an != NULL; an = (*an).nextarc) {
    
    
            if (!visited[an->adjvex]) {
    
    
                EnQueue(q, an->adjvex);
                visited[an->adjvex] = true;
                cout << alg.vertices[an->adjvex].data << " ";
                cnt++;
            }
        }
    }
}

/// @brief 头插法
/// @param alg 
/// @param v1 
/// @param v2 
void HeadInsert(ALGraph &alg, int v1, int v2) {
    
    
    ArcNode *p, *q;
    p = (ArcNode *)malloc(sizeof(ArcNode));
    p->adjvex = v2;
    if (!alg.vertices[v1].firstarc) {
    
    
        alg.vertices[v1].firstarc = p;
        p->nextarc = NULL;
    } else {
    
    
        q = alg.vertices[v1].firstarc;
        p->nextarc = q;
        alg.vertices[v1].firstarc = p;
    }
}

/// @brief 初始化无向图
/// @param alg 
void InitALGraph(ALGraph &alg) {
    
    
    cin >> alg.vexnum;
    cin >> alg.arcnum;
    cin >> alg.startNode;
    for (int v = 1; v <= alg.arcnum; ++v) {
    
    
        alg.vertices[v].firstarc = NULL;
        alg.vertices[v].data = v;
    }
    for (int v = 1; v <= alg.arcnum; ++v) {
    
    
        cin >> v1 >> v2;
        HeadInsert(alg, v1, v2);
        HeadInsert(alg, v2, v1);
    }
}

int main() {
    
    
    ALGraph alg;
    InitALGraph(alg);
    BFS(alg, alg.startNode);
    cout << (cnt == alg.vexnum ? "" : "\n0");
}

也另附东宝的Vector版:https://blog.csdn.net/qq_60755115/article/details/127741122

#include <bits/stdc++.h>
using namespace std;

#define MAX_VERTEX_NUM 20
#define VertexType int

int vex, arc, start, v1, v2, cnt;
vector<int> adjvex[15];
queue<int> que;

bool visited[15];

typedef struct ArcNode {
    
    
    int adjvex;
    struct ArcNode *nextarc; // 下一条边指针
} ArcNode;

typedef struct VexNode {
    
    
    VertexType data;
    ArcNode *firstarc; // 邻接边
} VexNode, Adjvex[MAX_VERTEX_NUM];

typedef struct ALGraph {
    
    
    Adjvex vertices; // 邻接表
    int vexnum, arcnum; // 结点数和边数
    int startNode; // 开始结点
} ALGraph;


void bfs(int v) {
    
    
    que.push(v);
    while (!que.empty()) {
    
    
        v = que.front();
        que.pop();
        if (visited[v]) {
    
    
            continue;
        }
        cout << v << " ";
        visited[v] = true;
        cnt++;
        for (int i = adjvex[v].size() - 1; i >= 0; i--) {
    
    
            if (!visited[adjvex[v][i]]) {
    
    
                que.push(adjvex[v][i]);
            }
        }
    }
}

int main() {
    
    
    cin >> vex >> arc >> start;
    for (int i = 0; i < arc; i++) {
    
    
        cin >> v1 >> v2;
        adjvex[v1].push_back(v2);
        adjvex[v2].push_back(v1);
        visited[i] = false;
    }
    bfs(start);
    cout << (cnt == vex ? "" : "\n0");
}

猜你喜欢

转载自blog.csdn.net/Keikei419/article/details/128251202