数据结构-图的邻接表的定义与实现

这篇具有很好参考价值的文章主要介绍了数据结构-图的邻接表的定义与实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、引言

二、图的基本概念

三、图的存储方式

1. 邻接矩阵

2. 邻接表

3. 十字链表

4. 邻接多重表

四、邻接表的实现

1. 邻接表的定义

2. 邻接表的构建

3. 邻接表的遍历

五、邻接表的优缺点

六、总结


一、引言

在计算机科学中,图是一种非常重要的数据结构,它是由节点和边组成的一种数据结构,可以用来表示各种实际问题,如社交网络、路线规划等。在图的存储方式中,邻接表是一种常用的方式,它可以有效地表示图的结构,并且具有较高的效率。本文将介绍图的基本概念、存储方式以及邻接表的实现方法,希望能够帮助读者更好地理解和应用图的相关知识。

二、图的基本概念

在图的定义中,有两个基本概念:节点和边。节点也称为顶点,是图中的基本元素,通常用一个圆圈或方框表示。边是节点之间的连接关系,通常用一条线段表示。图可以分为有向图和无向图两种类型,有向图中的边是有方向的,而无向图中的边是无方向的。

图的表示方法有多种,其中最常用的是邻接矩阵和邻接表。邻接矩阵是一个二维数组,其中每个元素表示两个节点之间是否有边相连。如果节点i和节点j之间有边相连,则邻接矩阵中第i行第j列的元素为1,否则为0。邻接矩阵的缺点是空间复杂度较高,当图的节点数较大时,会占用大量的内存空间。

三、图的存储方式

1. 邻接矩阵

邻接矩阵是一种常用的图的存储方式,它可以用一个二维数组表示图的结构。在邻接矩阵中,每个元素表示两个节点之间是否有边相连。如果节点i和节点j之间有边相连,则邻接矩阵中第i行第j列的元素为1,否则为0。

邻接矩阵的优点是可以快速地判断两个节点之间是否有边相连,时间复杂度为O(1)。但是邻接矩阵的缺点是空间复杂度较高,当图的节点数较大时,会占用大量的内存空间。

以下是C++实现邻接矩阵的构造、析构、深度优先遍历、广度优先遍历的代码:

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

const int MAXN = 100; // 最大顶点数
const int INF = 0x3f3f3f3f; // 无穷大

class Graph {
private:
    int n; // 顶点数
    int e; // 边数
    int **matrix; // 邻接矩阵

public:
    Graph(int n, int e); // 构造函数
    ~Graph(); // 析构函数
    void addEdge(int u, int v, int w); // 添加边
    void dfs(int u, bool visited[]); // 深度优先遍历
    void bfs(int u); // 广度优先遍历
};

Graph::Graph(int n, int e) {
    this->n = n;
    this->e = e;
    matrix = new int*[n];
    for (int i = 0; i < n; i++) {
        matrix[i] = new int[n];
        for (int j = 0; j < n; j++) {
            matrix[i][j] = INF; // 初始化为无穷大
        }
    }
}

Graph::~Graph() {
    for (int i = 0; i < n; i++) {
        delete[] matrix[i];
    }
    delete[] matrix;
}

void Graph::addEdge(int u, int v, int w) {
    matrix[u][v] = w;
    matrix[v][u] = w; // 无向图需要反向也加一次
}

void Graph::dfs(int u, bool visited[]) {
    visited[u] = true;
    cout << u << " ";
    for (int v = 0; v < n; v++) {
        if (matrix[u][v] != INF && !visited[v]) {
            dfs(v, visited);
        }
    }
}

void Graph::bfs(int u) {
    bool visited[MAXN] = {false};
    queue<int> q;
    visited[u] = true;
    q.push(u);
    while (!q.empty()) {
        int v = q.front();
        q.pop();
        cout << v << " ";
        for (int w = 0; w < n; w++) {
            if (matrix[v][w] != INF && !visited[w]) {
                visited[w] = true;
                q.push(w);
            }
        }
    }
}

int main() {
    int n = 5, e = 6;
    Graph g(n, e);
    g.addEdge(0, 1, 1);
    g.addEdge(0, 2, 1);
    g.addEdge(1, 3, 1);
    g.addEdge(2, 3, 1);
    g.addEdge(2, 4, 1);
    g.addEdge(3, 4, 1);
    bool visited[MAXN] = {false};
    cout << "DFS: ";
    g.dfs(0, visited);
    cout << endl;
    cout << "BFS: ";
    g.bfs(0);
    cout << endl;
    return 0;
}

其中,构造函数和析构函数分别用于创建和销毁邻接矩阵。addEdge函数用于添加边,其中u和v表示边的两个顶点,w表示边的权值。dfs函数用于深度优先遍历,其中u表示当前遍历的顶点,visited数组表示顶点是否被访问过。bfs函数用于广度优先遍历,其中u表示起始顶点。在主函数中,我们创建了一个5个顶点6条边的图,并分别进行了深度优先遍历和广度优先遍历。

2. 邻接表

邻接表是一种常用的图的存储方式,它可以用链表表示图的结构。在邻接表中,每个节点都对应一个链表,链表中存储了与该节点相连的所有节点。邻接表的优点是空间复杂度较低,当图的节点数较大时,占用的内存空间较小。

3. 十字链表

十字链表是一种常用的有向图的存储方式,它可以用链表表示图的结构。在十字链表中,每个节点都对应一个链表,链表中存储了该节点的入边和出边。十字链表的优点是可以快速地遍历图中的所有节点和边。

以下是C++实现的图十字链表,附带注释解释每个函数的作用:

#include <iostream>
#include <vector>

using namespace std;

// 十字链表节点
struct OLNode {
    int row, col; // 行列坐标
    int weight; // 权重
    OLNode* right; // 右指针
    OLNode* down; // 下指针
};

// 十字链表头结点
struct HeadNode {
    int row, col; // 行列坐标
    OLNode* right; // 右指针
    HeadNode* down; // 下指针
};

// 图十字链表
class GraphOL {
public:
    GraphOL(int n) {
        // 初始化头结点数组
        headNodes.resize(n);
        for (int i = 0; i < n; i++) {
            headNodes[i].row = i;
            headNodes[i].right = nullptr;
            headNodes[i].down = nullptr;
        }
    }

    // 添加边
    void addEdge(int from, int to, int weight) {
        // 创建新节点
        OLNode* node = new OLNode();
        node->row = to;
        node->col = from;
        node->weight = weight;
        node->right = nullptr;
        node->down = nullptr;

        // 找到from对应的头结点
        HeadNode* head = &headNodes[from];

        // 如果头结点的右指针为空,直接将新节点挂在右指针上
        if (head->right == nullptr) {
            head->right = node;
        }
        else {
            // 否则,找到最后一个节点,将新节点挂在其右侧
            OLNode* p = head->right;
            while (p->right != nullptr) {
                p = p->right;
            }
            p->right = node;
        }

        // 找到to对应的头结点
        head = &headNodes[to];

        // 如果头结点的下指针为空,直接将新节点挂在下指针上
        if (head->down == nullptr) {
            head->down = node;
        }
        else {
            // 否则,找到最后一个节点,将新节点挂在其下侧
            OLNode* p = head->down;
            while (p->down != nullptr) {
                p = p->down;
            }
            p->down = node;
        }
    }

    // 打印图
    void print() {
        for (int i = 0; i < headNodes.size(); i++) {
            HeadNode* head = &headNodes[i];
            OLNode* p = head->right;
            while (p != nullptr) {
                cout << "(" << p->col << ", " << p->row << ", " << p->weight << ") ";
                p = p->right;
            }
            cout << endl;
        }
    }

private:
    vector<HeadNode> headNodes; // 头结点数组
};

4. 邻接多重表

邻接多重表是一种基于邻接表的存储方式,它将每条边表示为一个结构体,结构体中包含了两个指针,分别指向这条边的两个顶点。这种存储方式可以有效地避免重复边的出现,同时也可以方便地实现图的遍历。

四、邻接表的实现

1. 邻接表的定义

邻接表是一种基于链表的存储方式,它将每个顶点的所有邻接点存储在一个链表中。具体来说,邻接表由一个顶点数组和一个边链表组成,顶点数组中的每个元素表示一个顶点,边链表中的每个结点表示一条边。

以下是C++中图的邻接表的定义:

#include <iostream>
#include <vector>

using namespace std;

// 邻接表中的边结构体
struct Edge {
    int to;         // 目标节点
    int weight;     // 权重
    Edge(int t, int w) : to(t), weight(w) {}
};

// 邻接表中的节点结构体
struct Node {
    int val;                // 节点的值
    vector<Edge> neighbors; // 邻居节点
    Node(int v) : val(v) {}
};

// 图的邻接表结构体
struct Graph {
    vector<Node> nodes;     // 节点集合
    Graph(int n) {
        for (int i = 0; i < n; i++) {
            nodes.push_back(Node(i));
        }
    }
};

int main() {
    Graph g(5);
    g.nodes[0].neighbors.push_back(Edge(1, 2));
    g.nodes[0].neighbors.push_back(Edge(2, 3));
    g.nodes[1].neighbors.push_back(Edge(2, 4));
    g.nodes[2].neighbors.push_back(Edge(3, 5));
    g.nodes[3].neighbors.push_back(Edge(4, 6));
    return 0;
}

邻接表是一种表示图的数据结构,它通过将每个节点的邻居节点列表存储在该节点的数据结构中来表示图。在邻接表中,每个节点都是一个包含邻居节点列表的结构体,邻居节点列表中存储了该节点与其它节点之间的边。邻接表的优点是可以快速访问每个节点的邻居节点,因此它在处理稀疏图时比邻接矩阵更有效。在邻接表中,每个节点的邻居节点列表通常是一个动态数组或链表。

2. 邻接表的构建

邻接表的构建需要遍历图中的所有顶点和边,将它们按照一定的规则存储在顶点数组和边链表中。具体来说,可以使用一个数组来存储顶点,数组中的每个元素包含了该顶点的信息和一个指针,指向该顶点的邻接点链表。邻接点链表中的每个结点包含了该邻接点的信息和一个指针,指向下一个邻接点。

邻接表是一种常用的图的存储方式,它通过链表的方式存储每个节点的邻居节点,可以有效地节省空间。下面是一个简单的C++实现:

#include <iostream>
#include <vector>

using namespace std;

// 邻接表节点
struct AdjListNode {
    int dest; // 目标节点
    AdjListNode* next; // 指向下一个邻居节点的指针
};

// 图类
class Graph {
private:
    int V; // 图的顶点数
    vector<AdjListNode*> adjList; // 邻接表

public:
    Graph(int V) {
        this->V = V;
        adjList.resize(V, nullptr); // 初始化邻接表
    }

    // 添加边
    void addEdge(int src, int dest) {
        // 创建新的邻接表节点
        AdjListNode* newNode = new AdjListNode;
        newNode->dest = dest;
        newNode->next = nullptr;

        // 将新节点插入到源节点的邻接表中
        if (adjList[src] == nullptr) {
            adjList[src] = newNode;
        } else {
            AdjListNode* cur = adjList[src];
            while (cur->next != nullptr) {
                cur = cur->next;
            }
            cur->next = newNode;
        }

        // 由于是无向图,所以还需要将目标节点的邻接表中插入源节点
        newNode = new AdjListNode;
        newNode->dest = src;
        newNode->next = nullptr;

        if (adjList[dest] == nullptr) {
            adjList[dest] = newNode;
        } else {
            AdjListNode* cur = adjList[dest];
            while (cur->next != nullptr) {
                cur = cur->next;
            }
            cur->next = newNode;
        }
    }

    // 打印邻接表
    void printAdjList() {
        for (int i = 0; i < V; i++) {
            cout << i << ": ";
            AdjListNode* cur = adjList[i];
            while (cur != nullptr) {
                cout << cur->dest << " ";
                cur = cur->next;
            }
            cout << endl;
        }
    }
};

int main() {
    Graph g(5);
    g.addEdge(0, 1);
    g.addEdge(0, 4);
    g.addEdge(1, 2);
    g.addEdge(1, 3);
    g.addEdge(1, 4);
    g.addEdge(2, 3);
    g.addEdge(3, 4);
    g.printAdjList();
    return 0;
}

在这个实现中,我们使用了一个结构体`AdjListNode`来表示邻接表中的每个节点,其中`dest`表示目标节点的编号,`next`表示指向下一个邻居节点的指针。我们还定义了一个`Graph`类来表示整个图,其中`V`表示图的顶点数,`adjList`是一个`vector`,用来存储每个节点的邻接表。

在`addEdge`函数中,我们首先创建一个新的邻接表节点,并将其插入到源节点的邻接表中。如果源节点的邻接表为空,则直接将新节点作为邻接表的头节点;否则,我们需要遍历邻接表,找到最后一个节点,然后将新节点插入到其后面。由于是无向图,所以我们还需要将目标节点的邻接表中插入源节点。

最后,我们定义了一个`printAdjList`函数来打印整个邻接表。对于每个节点,我们遍历其邻接表中的所有节点,并打印出目标节点的编号。

以上就是一个简单的图的邻接表的构建的C++实现。

3. 邻接表的遍历

邻接表的遍历可以通过遍历顶点数组和边链表来实现。具体来说,可以使用深度优先搜索或广度优先搜索算法来遍历邻接表中的所有顶点和边。

以下是C++代码,实现了图的深度优先遍历和广度优先遍历,注释中有详细的解释:

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

// 定义图的邻接表结构体
struct Graph {
    int V; // 顶点数
    vector<vector<int>> adj; // 邻接表
};

// 初始化图
Graph initGraph(int V) {
    Graph G;
    G.V = V;
    G.adj.resize(V);
    return G;
}

// 添加边
void addEdge(Graph& G, int u, int v) {
    G.adj[u].push_back(v);
    G.adj[v].push_back(u);
}

// 深度优先遍历
void DFS(Graph& G, int v, vector<bool>& visited) {
    visited[v] = true;
    cout << v << " ";
    for (int u : G.adj[v]) {
        if (!visited[u]) {
            DFS(G, u, visited);
        }
    }
}

// 广度优先遍历
void BFS(Graph& G, int v, vector<bool>& visited) {
    queue<int> q;
    visited[v] = true;
    q.push(v);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        cout << u << " ";
        for (int w : G.adj[u]) {
            if (!visited[w]) {
                visited[w] = true;
                q.push(w);
            }
        }
    }
}

int main() {
    int V = 5;
    Graph G = initGraph(V);
    addEdge(G, 0, 1);
    addEdge(G, 0, 2);
    addEdge(G, 1, 2);
    addEdge(G, 2, 3);
    addEdge(G, 3, 4);

    vector<bool> visited(V, false);

    cout << "DFS: ";
    DFS(G, 0, visited);
    cout << endl;

    visited.assign(V, false);

    cout << "BFS: ";
    BFS(G, 0, visited);
    cout << endl;

    return 0;
}

在主函数中,我们首先初始化了一个5个顶点的图,并添加了5条边。然后分别进行了深度优先遍历和广度优先遍历,并输出了遍历结果。

深度优先遍历的实现使用了递归,从起点开始,依次遍历与其相邻的未访问过的顶点,直到所有顶点都被访问过为止。

广度优先遍历的实现使用了队列,从起点开始,依次将其相邻的未访问过的顶点加入队列中,并依次访问队列中的顶点,直到所有顶点都被访问过为止。

五、邻接表的优缺点

邻接表的优点在于它可以有效地存储稀疏图,同时也可以方便地实现图的遍历。缺点在于它无法快速地判断两个顶点之间是否存在边,需要遍历链表才能确定。

六、总结

邻接表是一种基于链表的图存储方式,它可以有效地存储稀疏图,并且可以方便地实现图的遍历。但是它无法快速地判断两个顶点之间是否存在边,需要遍历链表才能确定。文章来源地址https://www.toymoban.com/news/detail-774468.html

到了这里,关于数据结构-图的邻接表的定义与实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构上机实验——图的实现(以无向邻接表为例)、图的深度优先搜索(DFS)、图的广度优先搜索(BFS)

      图采用邻接表存储结构,编程实现图的深度优先搜索和广度优先搜索算法。              2.1创建图 2.1.1定义图的顶点、边及类定义   我们定义一个 邻接表类(ALGraph) 。这里实现一些基础的数据结构。要注意结构体的嵌套。    Edge: 用于表示图中的边

    2024年02月04日
    浏览(42)
  • 【数据结构】邻接矩阵和邻接图的遍历

    本篇文章开始学习数据结构的图的相关知识,涉及的基本概念还是很多的。 本文的行文思路: 学习图的基本概念 学习图的存储结构——本文主要介绍邻接矩阵和邻接表 对每种结构进行深度优先遍历和广度优先遍历 话不多说,狠活献上 等等,先别急,正式学习之前先认识几个

    2024年02月04日
    浏览(42)
  • 24考研数据结构-图的存储结构邻接矩阵

    【1】顶点的结点结构 ——————— | data | firstarc | ——————— data数据域:储存顶点vi firstarc链域:指向链表中第一个结点 【2】弧的结点结构 —————————— | adjvex | info | nextarc | —————————— adjvex邻接点域:与顶点vi邻接的点在图中的位置 info数据域

    2024年02月14日
    浏览(44)
  • 数据结构--图的存储邻接表法

    邻接矩阵: 数组实现的顺序存储,空间复杂度高,不适合存储稀疏图 邻接表: 顺序+链式存储 无向图: 边结点的数量是 2|E|, 整体空间复杂度为 O(|V| + 2|E|) 有向图: 边结点的数量是 |E|, 整体空间复杂度为 O(|V| + |E|) 图的邻接表表示方式并不唯一 color{red}图的邻接表表示方

    2024年02月16日
    浏览(36)
  • 【数据结构】图的创建(邻接矩阵,邻接表)以及深度广度遍历(BFS,DFS)

    图是由顶点集合及顶点间的关系组成的一种数据结构:G = (V, E),其中: 顶点集合V = {x|x属于某个数据对象集}是有穷非空集合; E = {(x,y)|x,y属于V}或者E = {x, y|x,y属于V Path(x, y)}是顶点间关系的有穷集合,也叫 做边的集合。 完全图:在有n个顶点的无向图中,若有n * (n-1)/2条边,

    2024年02月04日
    浏览(34)
  • 数据结构--5.1图的存储结构(十字链表、邻接多重表、边集数组)

    目录 一、十字链表(Orthogonal List) 二、邻接多重表 三、边集数组 四、深度优先遍历   重新定义顶点表结点结构:  data firstIn firstOut 重新定义边表结构结点: tailVex headVex headLink tailLink        十字链表的好处就是因为把邻接表和逆邻接表整合在了一起,这样既容易找到Vi为

    2024年02月10日
    浏览(31)
  • 【数据结构与算法】图的基本概念 | 邻接矩阵和邻接表 | 广度优先遍历和深度优先遍历

    🌠 作者:@ 阿亮joy. 🎆 专栏:《数据结构与算法要啸着学》 🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 图是由顶点集合及顶点间的关系组成的一种数据结构:G = (V, E) ,其中: 顶点集合V = {x|x属于某

    2024年02月04日
    浏览(60)
  • 数据结构学习记录——如何建立图(邻接矩阵、邻接表-图节点的结构、创建并初始化、插入变、完整图的建立)

    目录 邻接矩阵 图节点的结构 创建并初始化 插入边 完整的图的建立  邻接表 图节点的结构 创建并初始化 插入边  完整的图的建立  定义结构体GNode,其中包含以下成员变量: Nv:表示图中的顶点数。 Ne:表示图中的边数。 二维数组表示图的邻接矩阵。它的大小是MaxVertexN

    2024年02月06日
    浏览(36)
  • C/C++语言 数据结构 创建邻接表存储的无向图及其邻接表的输出

    目录 1.邻接表相关知识补充  2. 图的邻接存储表示 3.测试输入与输出样例 4.代码实现 4.1 创建无向图邻接表 4.2 输入无向图的邻接表 定义: 对于图中每个顶点 vi,把所有邻接于 vi的顶点(对有向图是将从vi出发的弧的弧头顶点链接在一起)链接成一个带头结点的单链表,将所

    2024年02月05日
    浏览(39)
  • 【数据结构】图的定义、存储

    对王道数据结构选择题做错和不清楚的题的简单纠错 一个有n个顶点和n条边的无向图一定是 有环的 一个无向图有n个顶点和n-1条边,可以使它连通单没有环,若再加一条边,则会形成环 若图中顶点数为n,则它的生成树有n-1条边,去掉一条边变成非连通图;加上一条边变成一

    2024年02月08日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包