数据结构——图

一、概念

  1. 图右顶点的有穷集合V和边的集合E组成
  2. 无向图(,)/有向图<,>:有无方向
  3. 度/入度出度:顶点边数
  4. 简单图:不存在某个顶点到其自身的边且不重复的边
  5. 完全图:任意两个顶点有边
  6. 带权图/网:边有权值
  7. 子图
  8. 路径:顶点到另一个顶点的序列
  9. 路径长度:路径上边的个数
  10. 环/回路
  11. 简单路径:顶点不重复出现
  12. 简单环:除第一和最后不重复
  13. 连通:有路径
  14. 连通图:任意一对顶点连通
  15. 连通分量:无向图中的极大连通子图,多个
  16. 强连通图:有向图

注:

​ 计算各顶点的度存在重复

在这里插入图片描述

无向连通图所有顶点的度之和为偶数(重复计算)

​ 矩阵Am各元素值的含义:i 经过(m-1)个中间结点到 j 的路径条数为Ai,j

二、存储结构

  1. 顺序存储结构

    若顶点不为数:用数组标记Vertex

    邻接矩阵:二维数组

    typedef struct{
    	int no;								//编号
    	Elemtype data;						//信息
    }VertexType;
    typedef struct{
    	int edges[MAXV][MAXV];				//邻接矩阵
    	int n;								//顶点数
    	int e;								//边数
    	VertexType vexs[MAXV];				//信息
    }MatGraph;
    
  2. 链式存储结构

    邻接表

    typedef ANode{
    	int adjvex;					//编号
    	struct ANode *nextarc;		//下个结点
    	int weight;					//权值
    }ArcNode;						//结点
    typedef struct Vnode{
    	Elemtype data;				//信息
    	ArcNode *firstarc;			//第一个边结点
    }VNode;							//头结点
    typedef struct{
    	VNode adjlist[MAXV];		//头结点数组
    	int n;						//顶点数
    	int e;						//边数
    }AdjGraph;						//邻接表
    

    在这里插入图片描述

    逆邻接表:头结点为边结点的终点

    十字链表:

    在这里插入图片描述

    邻接多重表

    在这里插入图片描述

三、遍历

  1. 深度优先遍历(DFS)

    void DFS(int v, AGraph *G){
    	visit[v] = l;							//标记已访问
    	Visit(v);
    	ArcNode* q = G->adjlist[v].firstarc;		//第一个边结点
    	while(q != NULL){
    		if(vist[q->adjvex] == 0){				//下个结点未访问
    			DFS(q->adjvex, G);
    		}
    		q = q->nextarc;
    	}
    }
    
  2. 广度优先遍历(BFS)

    void BFS(AGraph *G, int v, int visit[MaxSize]){
        ArcNode *p;
        int que[MaxSize], front = 0, rear = 0;		//队列
        int j;
        Visit(v);
        visit[v] = 1;								//标记已访问
        rear = (rear+1)%MaxSize;					//找到队尾
        que[rear] = v;								//入队
        while(front != rear){						//队不空
            front  = (front+1)%MaxSize;				//出队
            j = que[front];							//获得出队元素
            p = G->adjlist[j].first;				//取第一个边结点
            while(p != NULL){
                	if(visit[p->adjV] == 0){		//未访问
                        Visit(p->adjV);
                        visit[p->adjV] = 1;			//标记已访问
                        rear = (rear+1)%MaxSize;	
                        que[rear] = p->adjV;		//入队
                    }
                p = p->next;						//找到出队元素其他相邻结点
            }
        }
    }
    

四、最小生成树——带权值最小

  1. Prim算法——生成树的结点与未加入结点之间找最小且不会产生环

    void Prim(int n, float MGraph[][n], int v0, float &sum){
    	float lowCost[n],vSet[n];
    	int v, k, min;
    	for(int i = 0 ; i < n ; i++){			//初始化
    		lowCost[i] = MGraph[v0][i];			//以v0为根与其他顶点权值
    		vSet[i] = 0;
    	}
    	v = v0;									
    	vSet[v] = 1;							//已并入树中
    	sum = 0;
        	
        for(int i = 0; i < n-1 ; i++){			//访问其余顶点
            min = INF;
            for(int j = 0 ; j < n ; ++j){
                if(vSet[j] == 0 && lowCost[j] < min){	//从未访问中找最小
                    min = lowCost[j];
                    k = j;						//对应顶点
                }
            }
            vSet[k] = 1;						//加入树
            v = k;
            sum += min;
            for(int j = 0 ; j < n ; j++){		//更新与未接入之间的权值
                if(vSet[j] == 0 && MGraph[v][j] < lowCost[j]){
                    lowCost[j] = MGraph[v][j];
                }
            }
        }
    }
    
  2. Kruskal算法——找权值最小的边,且不产生环,边两端顶点生成树

    并查集

    相关存储结构

    typedef struct{
    	int a,b;				//两端顶点
    	int w;					//权值
    }Road;
    Road road[MaxSize];
    //找根结点,相同会产生环
    int getRoot(int p){
        while(p != v[p])
            p = v[p];
        return p;
    }
    
    void Kruskal(Road road[], int n, int e, int &sum){
        int a,b;
        sum = 0;
        for(int i = 0 ; i < n ; ++i){	//初始化
            v[i] = i;
        }
        sort(road,e);					//从小到大排序权值
    	for(int  i = 0 ; i < e ; ++i){	//遍历所有边
            a = getRoot(road[i].a);		//获得左右比较
            b = getRoot(road[i].b);
            if(a != b){					//不等不会产生环
                v[a] = b;
                sum += road[i].w;
            }
        }
    }
    

五、最短路径

  1. Dijkstra算法——某一顶点到其余顶点的最短路径

    如果加入顶点后到达其余点的路径比原来小,更新dist[]和path[]

    查找路径从终点找上一个顶点

    dist[]:存放到各点最短路径

    path[]:存放到达该点的上个顶点

    set[]:判断是否已加入路径

    void Dijkstra(int n, float MGraph[][n], int v0, int dist[], in path[]){
    	int set[MaxSize];
    	int min,v;
    	for(int i = 0; i < n ; i++){			//初始化
    		dist[i] = MGraph[v0][i];
            set[i] = 0;
            if(MGraph[v0][i] < INF)				//可达
                path[i] = v0;
            else								//不可达
                path[i] = -1;
    	}
        set[v0] = 1;							//已访问
        path[v0] = -1;
        
        for(int i = 0; i < n-1; ++i){			//查找其余结点
            min = INF;
            for(int j = 0; j < n; ++j){
                if(set[j] == 0 && dist[j] < min){//找到最短路径
                    v = j;
                    min = dist[j];
                }
            }
            set[v] = 1;							//已访问
            for(int j = 0; j < n; ++j){
                //更新最短路径
                if(set[j] == 0 && dist[v]+MGraph[v][j] < dist[j]){
    				dist[j] = dist[v]+MGraph[v][j];
                    path[j] = v;	
                }
            }
        }
    }
    
  2. Floyd算法——任意两个顶点之间最短路径

    对于每个顶点v,和任一顶点对(i,j), i≠j,v≠j,i≠v,

    如果A[i] [j] >A[i] [v]+A[v] [j],更新A[i] [j]的值,Path[i] [j] = v

    A[i] [j]:存储任意两个顶点之间当前最短路径

    Path[i] [j]:经过中间点

    void printPath(int u, int v, int path[][MAX]){
    	if(path[u][v] == -1){				//两顶点直接相连
    		printf();
    	}else{
    		int mid = path[u][v];			//获取中间点
    		printPath(u, mid, path);		//起点到中间点
    		printPath(mid, v, path);		//中间点到终点
    	}
    }
    
    void Floyd(int n, float MGraph[][n], int Paht[]){
        int i, j, v;
        int A[n][n];
        for(i = 0; i < n; ++i){					//初始化
            for(j = 0; j < n; ++j){
                A[i][j] = MGraph[i][j];
                Path[i][j] = -1;
            }
        }
        
        for(v = 0; v < n; ++v){
            for(i = 0; i < n; ++i){
            	for(j = 0; j < n; ++j){
            		if(A[i][j] > A[i][v] + A[v][j]){	//加入中间结点后有更小
                        A[i][j] = A[i][v] + A[v][j];
                        Path[i][j] = v;
                    }
        		}
        	}
        }
    }
    

六、关键路径——最后结束

  1. AOE网:源点:只有出度,汇点:只有入度
  2. 拓扑排序:后面结点必须要前面结点先发生
  3. 逆拓扑排序
  4. 事件最早发生时间ve:最长到达该顶点时间
  5. 事件最迟发生时间vl:后继事件-活动时间,多个时选取最小
  6. 活动最早发生时间e:之前已经过时间,选最大
  7. 活动最迟发生时间l:指向事件最迟-活动时间
  8. 关键活动:活动最早和最迟重合

在这里插入图片描述

缩短整个工期要缩短所有关键路径

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值