图的基本操作

来源:www.rxwcv.cn
图的存储结构主要分两种,一种是邻接矩阵,一种是邻接表。

基本概念

邻接矩阵

图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息。
下图就是一个无向图的邻接矩阵
无向图的邻接矩阵
从上面可以看出,无向图的边数组是一个对称矩阵。所谓对称矩阵就是n阶矩阵的元满足aij = aji。即从矩阵的左上角到右下角的主对角线为轴,右上角的元和左下角相对应的元全都是相等的。
从这个矩阵中,很容易知道图中的信息。
(1)要判断任意两顶点是否有边无边就很容易了;
(2)要知道某个顶点的度,其实就是这个顶点vi在邻接矩阵中第i行或(第i列)的元素之和;
(3)求顶点vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接点;

下图是一个有向图的邻接矩阵
有向图的邻接矩阵
顶点数组为vertex[4]={v0,v1,v2,v3},弧数组arc[4][4]为上图的矩阵。
主对角线上数值依然为0.但因为是有向图,所以此矩阵并不对称,比如从v1到v0有弧,得到arc[1][0]=1,而v0到v1没有弧,因此arc[0][1]=0。有向图论入度和出度,顶点v1的入度为1,正好是第v1列上的数之和。顶点v1的出度为2,即第v1行的各数之和。与无向图同样的办法,判断顶点vi到vj是否存在弧,只需要查找矩阵中arc[i][j]是否为1即可。要想知道vi的所有邻接点就是将矩阵第i行元素扫描一遍,查找arc[i][j]为1的顶点。

邻接表

对于边数相对顶点较少的图,邻接矩阵对存储空间的极大浪费。
邻接表的处理方法是这样的:
(1)图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。 另外,对于顶点数组中,每个数据元素还需要存储指向第一个邻接点的指针,以便于查找该顶点的边信息。
(2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。

下图是一个无向图的邻接表结构
无向图邻接表
从图中我们知道,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息。firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针,比如v1顶点与v0、v2互为邻接点,则在v1的边表中,adjvex分别为v0的0和v2的2.如果想知道某个顶点的度,就去查找这个顶点的边表中结点的各数。若要判断顶点vi和vj是否存在边,只需要测试顶点vi的边表adjvex中是否存在结点vj的下标就行了。若求顶点的所有邻接点,其实就是对此顶点的边表进行遍历,得到的adjvex域对应的顶点就是邻接点。

两种基本结构

邻接矩阵

#define MAXV 100 //最大顶点个数
#define INF 32767       //INF表示∞
//邻接矩阵
typedef struct {
    int no;//顶点编号
    int info;//顶点其他信息,可存放带权图的权值
}VertexType; //顶点类型

typedef struct {
    int edges[MAXV][MAXV]; //邻接矩阵
    int n, e;//顶点数,边数
    VertexType vexs[MAXV];//顶点表
}MGraph;

邻接表

//边表结点
typedef struct EdgeNode {
    int adjvex;//该弧的终点位置
    struct EdgeNode *nextarc;//指向下一条弧的指针
    int info;//该弧的相关信息,存放权值
}EdgeNode;
//顶点表节点
typedef int Vertex;
typedef struct VextexNode {
    Vertex data;//顶点信息
    int count; //顶点的入度
    bool visited;//表示该节点是否被访问
    EdgeNode *firstEdge;//指向第一条弧
}VextexNode,AdjList[MAXV];
//邻接表
typedef struct {
    AdjList adjList;//邻接表
    int n, e;//图中顶点数和边数
}ALGraph;

用普通数组构造图的邻接矩阵

void arrayToMat(int *arr, int n, MGraph &g) {
    int i, j, count = 0;//count用于统计边数,即矩阵中非0元素的个数
    g.n = n;
    for (i = 0; i < g.n; ++i) {
        for (j = 0; j < g.n; ++j) {
            g.edges[i][j] = arr[i*n + j];//将Arr看作n×n的二维数组,Arr[i*n+j]即是Arr[i][j],计算存储位置的功夫在此应用
            if (g.edges[i][j] != 0 && g.edges[i][j] != INF) {
                ++count;
            }
        }
    }
    g.e = count;
}

用普通数组构造图的邻接表

void arrayToList(int* arr, int n, ALGraph &g) {
    int i, j, count = 0;
    g.n = n;
    EdgeNode *ep;
    //输入顶点信息,将边表置为0
    for (i = 0; i < g.n; ++i) {
        g.adjList[i].data = i;
        g.adjList[i].firstEdge = nullptr;
    }
    //建立边表
    for (i = 0; i < n; ++i) {//检查邻接矩阵中每个元素
        for (j = n - 1; j >= 0; --j) {
            if (arr[i*n + j] != 0) {  //存在一条边,将Arr看作n×n的二维数组,Arr[i*n+j]即是Arr[i][j]
                ep = (EdgeNode*)malloc(sizeof(EdgeNode));//创建一个节点*ep
                ep->adjvex = j;
                ep->info = arr[i*n + j];
                ep->nextarc = g.adjList[i].firstEdge;//采用头插法插入*ep
                g.adjList[i].firstEdge = ep;
                ++count;
            }
        }
    }
    g.e = count;
}

邻接矩阵转邻接表

void MatToList(MGraph g, ALGraph &G) {
    int i, j;
    EdgeNode* ep;
    for (i = 0; i < g.n; ++i) {
        G.adjList[i].firstEdge = nullptr;
    }
    for (i = 0; i < g.n; ++i) {
        for (j = g.n - 1; j >= 0; --j) {
            if (g.edges[i][j] != 0) {
                ep = (EdgeNode*)malloc(sizeof(EdgeNode));
                ep->adjvex = j;
                ep->info = g.edges[i][j];
                ep->nextarc = G.adjList[i].firstEdge;
                G.adjList[i].firstEdge = ep;
            }
        }
    }
    G.n = g.n;
    G.e = g.e;
}

邻接表转邻接矩阵

void ListToMat(ALGraph G, MGraph &g) {
    int i, j;
    EdgeNode *ep;
    g.n = G.n;
    g.e = G.e;
    for (i = 0; i < G.n; ++i) {
        for (j = 0; j < G.n; ++j) {
            g.edges[i][j] = 0;
        }
    }
    for (i = 0; i < G.n; ++i) {
        ep = G.adjList[i].firstEdge;
        while (ep) {
            g.edges[i][ep->adjvex] = ep->info;
            ep = ep->nextarc;
        }
    }
}

输出邻接矩阵

void dispMat(MGraph g) {
    int i, j;
    for (i = 0; i < g.n; ++i) {
        for (j = 0; j < g.n; ++j) {
            if (g.edges[i][j] == INF) {
                cout << "∞ ";
            }
            else {
                cout << g.edges[i][j] << " ";
            }
        }
        cout << endl;
    }
}

输出邻接表

void dispAdj(ALGraph g) {
    int i;
    EdgeNode* ep;
    for (i = 0; i < g.n; ++i) {
        ep = g.adjList[i].firstEdge;
        cout << i << ":";
        while (ep) {
            cout << "->" << ep->adjvex << "/" << ep->info;
            ep = ep->nextarc;
        }
        cout << endl;
    }
}

初始化邻接表节点的访问

初始化为0,表示还没有被访问过

void initVisted(ALGraph &G) {
    for (int i = 0; i < G.n; ++i) {
        G.adjList[i].visited = 0;
    }
}

图的宽度搜索

void BFS(ALGraph G, int start, vector<int>& v) {
    queue<int> q;
    q.push(start);
    G.adjList[start].visited = 1;

    while (!q.empty()) {
        int current = q.front();
        q.pop();
        v.push_back(current);
        EdgeNode *ep;
        ep = (EdgeNode*)malloc(sizeof(EdgeNode));
        ep = G.adjList[current].firstEdge;//ep指向头结点,遍历边表
        while (ep) {
            if (!G.adjList[ep->adjvex].visited) {
                q.push(ep->adjvex);
                G.adjList[ep->adjvex].visited = 1;
            }
            ep = ep->nextarc;
        }
    }
}

图的深度搜索

void DFS(ALGraph G, int start, vector<int>& v) {
    stack<int> s;
    s.push(start);
    G.adjList[start].visited = 1;
    v.push_back(start);
    while (!s.empty()) {
        int current = s.top();
        s.pop();
        EdgeNode *ep;
        ep = (EdgeNode*)malloc(sizeof(EdgeNode));
        ep = G.adjList[current].firstEdge;
        while (ep) {
            if (!G.adjList[ep->adjvex].visited) {
                s.push(current);
                s.push(ep->adjvex);
                G.adjList[ep->adjvex].visited = 1;
                v.push_back(ep->adjvex);
                break;
            }
            ep = ep->nextarc;
        }
    }
}
全部评论

相关推荐

hanliu:1. 排版与格式问题字体与对齐问题:标题和内容的字体大小差异不够明显,无法迅速吸引目光。某些文字看起来有些拥挤(比如校园经历中的“班委成员”部分)。2. 内容逻辑性模块顺序问题:实习经历放在较靠后的位置,实际上这部分内容对应聘来说更重要,建议提前突出。细节表述不够突出:比如教育背景部分的专业课程仅仅列出名字,没有说明自己在这些课程中表现如何或者掌握了什么技能,缺乏量化描述。多余内容:例如“班委成员”和“宣传委员”这类校园经历,叙述过于普通,缺乏和岗位相关的实质性贡献。,建议简写。3. 措辞专业性表达不够精准:例如“协助班长与团支书更好地为同学服务”显得较为笼统,没有实际成果的体现。用词重复:如“学习了焊接”“学习了光检”等重复词语较多,缺乏丰富的动词来展示个人能力(如“负责”“优化”“改进”等)。技能展示不足:虽然列出了UG和CAD证书,但没有明确提到这些技能如何在实际工作中发挥作用。4. 技能匹配度技能深度不足:虽然列出了掌握的软件和技术,但没有描述技能水平(如“熟练掌握”“精通”),也没有具体案例支持这些技能。缺乏岗位导向性:比如针对机械设计与制造方向,实习经历提到了“E6尾灯项目”,但没有详细说明自己在其中的技术贡献,可能会显得经验描述泛泛而谈。5. 自我评价问题表达空泛:如“具有良好的沟通协调能力”“责任心强”之类的描述太常见,没有让人眼前一亮的特点。缺乏成果支持:自我评价中的能力没有用具体项目、经历或成就来验证,可信度较弱。 兄弟加油
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务