数据结构知识点系列二

本文是数据结构知识点系列的第二部分,重点讲解了图的定义、存储结构,包括邻接矩阵、邻接表等。接着介绍了图的遍历方法如深度优先遍历和广度优先遍历。文章还详细阐述了如何找到图的最小生成树,如Prim和Kruskal算法,并探讨了最短路径问题,提到了Dijkstra和Floyd算法。此外,还讨论了拓扑排序及其在工程活动优先级中的应用以及关键路径的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据结构知识点系列二

1、图

相关定义

图是由顶点的有穷非空集合和顶点之间边的集合组成,记为 G(V,E) ,其中 V 为顶点集合,E (vi,vj) (无向边)或 <vi,vj> <script type="math/tex" id="MathJax-Element-5"> </script>(有向边 vivj vi 为尾, vj 为头)的集合。 完全图:任意两个顶点间均有边,无向完全图有 n(n1)2 条边;有向完全图有 n(n1) 条边。

无向图中,顶点的度 TD(v) 为与顶点v相关的边的数目;有向图中,度=入度+出度:入度 ID(v) 为以v为头的边的数目;出度 OD(v) 为以v为尾的边的数目。

在无向图中,任意两个顶点都有路径,称为“连通图”。“连通分量”无向图中极大连通分量。

存储结构

邻接矩阵

arc[i][j]={1,0,if (vi,vj)Eor<vi,vj>E;otherwise.

入度:所在列的和。出度:所在行的和。

//1. 邻接矩阵创建图
typedef char VertexType;
typedef int EdgeType;
#define MaxVex 100
#define Inf 65535

typedef struct{
    VertexType vexs[MaxVex];
    EdgeType arc[MaxVex][MaxVex];
    int numVertexes,numEdges;
}MGraph;

void CreateMGraph(MGraph* G){
    int i,j,k,w;
    printf("输入顶点数和边数:\n");
    scanf("%d%d",&G->numVertexes,%G->numEdges);
    for (i=0;i<G->numVertexes;i++){
        scanf("%d",&G->vexs[i]);
    }
    for (i=0;i<G->numVertexes;i++)
        for(j=0;j<G->numEdges;j++)
            G->arc[i][j] = Inf;
    for (k=0;k<G->numEdges;k++){
        printf("输入边的下标i,j和权重w:\n");
        scanf("%d%d%d",&i,&j,&w);
        G->arc[i][j] = w;
        G->arc[j][i] = G->arc[i][j];
    }   
}

邻接表

以顶点为尾的相关边的顶点链表。无向图:n个表头结点,2e个表结点

typedef struct EdgeNode{
    int adjvex;
    EdgeType weight;
    struct EdgeNode *next;
}EdgeNode;
typedef struct VertexNode{
    VertexType data;
    EdgeNode *firstedge;
}VertexNode,AdjList[MaxVex];
typedef struct{
    AdjList adjList;
    int numVertexes,numEdges;
}GraphAdjList;
void CreateALGraph(GraphAdjList* G){
    int i,j,k;
    EdgeNode *e;
    //输入顶点数和边数
    for (i=0;i<G->numVertexes;i++){
        scanf("%d",&G->adjList[i].data);
        G->adjList[i].firstedge = NULL;
    }
    for (k=0;k<G->numEdges;k++){
        //输入下标i和j
        e = (EdgeNode)malloc(sizeof(EdgeNode));
        e->adjvex = j;
        e->next = G->adjList[i].firstedge;
        G->adjList[i].firstedge = e;
        e = (EdgeNode)malloc(sizeof(EdgeNode));
        e->adjvex = i;
        e->next = G->adjList[j].firstedge;
        G->adjList[j].firstedge = e;
    }
}

十字链表for有向图;邻接多重表for无向图。

图的遍历

**深度优先遍历:**DFS使用栈。类似于树的前序遍历。

**广度优先遍历:**BFS使用队列。类似于树的层序遍历。

/深度优先遍历DFS
//邻接矩阵
void DFSTraverse(MGraph G){
    int i;
    int visited[MaxVex];
    for (i=0;i<G.numVertexes;i++){
        visited[i] = 0;
    }
    for (i=0;i<G.numVertexes;i++){
        if (visited[i] == 0){
            DFS1(G,i);
        }
    }
}
void DFS1(MGraph G,int i){
    int j;
    visited[i] = 1;
    printf("%c",G.vexs[i]);
    for (j=0;j<G.numVertexes;j++)
        if (G.arc[i][j] ==1 && visited[j]==0)
            DFS1(G,j);
}
//邻接表
void DFSTraverse(GraphAdjList G){
    int i;
    int visited[MaxVex];
    for (i=0;i<G.numVertexes;i++){
        visited[i] = 0;
    }
    for (i=0;i<G.numVertexes;i++){
        if (visited[i] == 0){
            DFS2(G,i);
        }
    }
}
void DFS2(GraphAdjList G,int i){
    VertexNode* p = G.adjList[i].firstedge;
    visited[i] = 1;
    printf("%c",G.adjList[i].data);
    while (p){
        if (visited[p->adjvex]==0)
            DFS2(G,p->adjvex);
        p = p->next;
    }
}
//广度优先遍历BFS
//邻接矩阵
void BFSTraverse(MGraph G){
    int i,j;
    queue<int> q;
    int visited[MaxVex];
    for (i=0;i<G.numVertexes;i++){
        visited[i] = 0;
    }
    for (i=0;i<G.numVertexes;i++){
        if (visited[i]==0){
            visited[i] = 1;
            printf("%c",G.vexs[i]);
            q.push(i);
            while (!q.empty()){
                int k = q.front();
                q.pop();
                for (j=0;j<G.numVertexes;j++){
                    if (G.arc[i][j]==1 && visited[j]==0){
                        visited[j] = 1;
                        printf("%c",G.vexs[j]);
                        q.push(j);
                    }
                }
            }
        }
    }
}
//邻接表
void BFSTraverse2(GraphAdjList G){
    int i,j;
    queue<int> q;
    EdgeNode* p;
    int visited[MaxVex];
    for (i=0;i<G.numVertexes;i++){
        visited[i] = 0;
    }
    for (i=0;i<G.numVertexes;i++){
        if (visited[i]==0){
            visited[i] = 1;
            printf("%c",G.adjList[i].data);
            q.push(i);
            while (!q.empty()){
                int k = q.front();
                q.pop();
                p = G.adjList[i].firstedge;
                while (p){
                    if (visited[p->adjvex]==0){
                        visited[p->adjvex] = 1;
                        printf("%c",G.adjList[p->adjvex].data);
                        p.push(p->adjvex);
                    }
                    p = p->next;
                }
            }
        }
    }
}
最小生成树

构造连通网的最小代价生成树。

Prim算法:假设 N={V,{E}} 是连通网,TE是N上最小生成树边的集合。算法从 U={u0},u0V,TE={} 开始。重复执行下述操作:在所有 uU,v{VU} 的边 (u,v)E 中找到一条代价最小的边加入集合TE,同时将对应顶点并入 U ,直到U=V为止。

void Prim(MGraph G){
    int min,i,j,k;
    int adjvex[MaxVex],lowcost[MaxVex];
    adjvex[0] = 0,lowcost[0] = 0;
    for (i=1;i<G.numVertexes;i++){
        lowcost[i] = G.arc[0][i];
        adjvex[i] = 0;
    }
    for (i=1;i<G.numVertexes;i++){
        min = Inf;
        j=1,k=0;
        while (j<G.numVertexes){
            if (lowcost[j]!= 0 && lowcost[j] < min){
                min = lowcost[j];
                k = j;
            }
            j++;
        }
        printf("%d%d",adjvex[k],k);
        lowcost[k] = 0;
        for (j=1;j<G.numVertexes;j++)
            if (lowcost[j]!=0 && G.arc[k][j]<lowcost[j]){
                lowcost[j] = G.arc[k][j];
                adjvex[j] = k;
            }
    }
}

Kruskal算法:假设 N={V,{E}} 是连通网,则令最小生成树的初始状态为只有n个顶点而无边的非连通图 T={V,{}} 。重复执行下述操作:在 E 中选择代价最小的边,若这条边依附再得顶点落在T中不同的连通分量上,则将此边加入T中;否则舍去此边选择下一条代价最小的边,直到 T <script type="math/tex" id="MathJax-Element-25">T</script>中所有定点都在同一连通分量上为止。

typedef struct{
    int begin,end,weight;
}Edge;
void Kruskal(MGraph G){
    int i,n,m;
    Edge edges[MaxEdge];
    int parents[MaxVex];//定义一个数组判断边与边是否形成环路
    //将邻接矩阵转为边集数组并按权重排序
    for (i=0;i<G.numVertexes;i++)
        parents[i] = 0;
    for (i=0;i<G.numEdges;i++){
        n = Find(parents,edges[i].begin);
        m = Find(parents,edges[i].end);
        if (n!=m){
            parents[n] = m;
            printf("(%d,%d)%d",edges[i].begin,edges[i].end,edges[i].weight);
        }
    }
}
int Find(int* parents,int f){
    while (parents[f]>0)
        f = parents[f];
    return f;
}
最短路径

Dijkstra算法:基于已求出的最短路径求更远顶点的最短路径。

Floyd算法:动态规划方法。维护两个矩阵:D顶点到顶点的最短路径权值和矩阵;P对应顶点的最短路径的前驱。

//最短路径:Dijkstra算法(贪心)
typedef int Patharc[MaxVex];//最短路径下标的数组
typedef int ShortPathTable[MaxVex];//到各顶点最短路径的权重和
void Dijkstra(MGraph G,int s, Patharc* P,ShortPathTable* D){
    int v,w,k,min;
    int find[MaxVex];
    for (v=0;v<G.numVertexes;v++){
        find[v] = 0;
        (*D)[v] = G.arc[s][v];
        (*P)[v] = 0;
    }
    (*D)[s] = 0,find[s] = 1;
    for (v=1;v<G.numVertexes;v++){
        min = Inf;
        for (w=0;w<G.numVertexes;w++){
            if (find[w] == 0 && (*D)[w]<min){
                min = (*D)[w];
                k = w;
            }
        }
        find[k] = 1;
        for (w=0;w<G.numVertexes;w++){
            if (find[w] == 0 && (min+G.arc[k][w])<(*D)[w]){
                (*D)[w] = min+G.arc[k][w];
                (*D)[w] = k;
            }
        }
    }
}
//最短路径:Folyd算法(动态规划)
typedef int Pathmatrix[MaxVex][MaxVex];
typedef int ShortPathTable[MaxVex][MaxVex];
void Folyd(MGraph G,Pathmatrix* P,ShortPathTable* D){
    int v,w,k;
    for (v=0;v<G.numVertexes;v++){
        for (w=0;w<G.numVertexes;w++){
            (*P)[v][w] = w;
            (*P)[v][w] = G.arc[v][w];
        }
    }
    for (k=0;k<G.numVertexes;k++){
        for (v=0;v<G.numVertexes;v++){
            for (w=0;w<G.numVertexes;w++){
                if ((*D)[v][w]>(*D)[v][k]+(*D)[k][w]){
                    (*D)[v][w] = (*D)[v][k]+(*D)[k][w];
                    (*P)[v][w] = (*P)[v][k];
                }
            }
        }
    }
}
拓扑排序

AOV网:在一个表示工程的有向图中,用顶点表示活动,用边表示活动之间的优先关系,所构成的网。

拓扑排序算法:从AOV网中选择一个入度为0的顶点输出,然后删除以该顶点为尾的弧,重复直到输出所有的顶点或不存在入度为0的顶点为止。

int ToplogicalSort(GraphAdjList G){
    EdgeNode* e;
    int i,k,gettop;
    int top=count=0;
    stack<int> s;
    for (i=0;i<G.numVertexes;i++){
        if (G.adjList[i].in == 0)
            s.pust[i];
        while (!s.empty()){
            gettop = s.top();
            s.pop();
            printf("%c",G.adjList[gettop].data);
            count++;
            for (e=G.adjList[gettop].firstedge;e;e=e->next){
                k = e->adjvex;
                if (!(--G.adjList[k].in))
                    s.push(k);
            }
        }
        if (count<G.numVertexes)
            return 0;
        return 1;
    }
}
关键路径

AOE网=*AOV网+边的权值,用边表示活动,权值表示活动持续时间。没有入边的顶点为源点,没有出边的顶点为汇点。关键路径:从源点到汇点具有最大路径长度*的路径。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值