Detailed explanation of graph operation algorithm

1. Figure

Basic concept:

  1. Directed Graph - A graph in which each edge has a direction, such as the following graph on a social media site is a directed graph.
  2. Undirected graph - Each edge in the graph has no direction, for example, a mutual acquaintance relationship graph between friends can be an undirected graph.
  3. Simple Graph - An undirected or directed graph without self-loops and repeated edges, for example a graph of friendships that does not allow multiple friendships between two people is a simple graph.
  4. Multigraph - An undirected or directed graph that allows for duplicate edges, for example, a comment relation graph where the same user comments on the same article multiple times at different times is a multigraph.
  5. Complete Graph - A graph in which there is an edge between any two vertices, eg a complete graph with five nodes has 10 edges.
  6. Subgraph - A graph formed by selecting a part of nodes and their edges from the original graph, for example, a subgraph formed by selecting several users and their friendships from a social network.
  7. Connected Graphs and Connected Components − In an undirected graph, a graph is connected if there exists at least one path between any two nodes. If all connected subgraphs in a connected graph are extracted, each connected subgraph obtained is called a connected component.
  8. Strongly Connected Graph − In a directed graph, a graph is strongly connected if there exists at least one directed path between any two nodes.
  9. Spanning tree - a tree in a connected graph, which contains all the nodes in the original graph, and only a minimum of edges are preserved, for example, a city's road system can be seen as an undirected graph, and its minimum spanning tree representation the shortest path connecting the cities.
  10. Degree - The number of edges a node has, for an undirected graph it is the number of nodes adjacent to the node, for a directed graph it is divided into in-degree and out-degree.
  11. Weight and network - the weight of the edges in the graph or the degree of association between nodes, such as the distance between a city can be regarded as a weighted undirected graph.
  12. Dense graph and sparse graph - used to describe the nature of the number of edges in the graph relative to the number of nodes. If the number of edges is close to the square of the number of nodes, then the graph is a dense graph; otherwise, it is a sparse graph.
  13. Path and path length − The sequence of edges traversed from a start node to an end node is called a path, and the number of edges traversed on a path is called the path length.
  14. Distance - The shortest path length between two nodes.
  15. Loop - A path from a start node to an end node where the start node and end node are the same node. Also known as a cycle in an undirected graph.

2. Image storage

1. Adjacency matrix

An adjacency matrix is ​​a data structure that represents a graph as a two-dimensional array, where each element in the matrix indicates whether an edge exists between two nodes. For undirected graphs, the adjacency matrix is ​​symmetric; for directed graphs, it is not necessarily symmetric.

In C language, you can use a two-dimensional array to implement an adjacency matrix, for example:

#define MAX_NODES 100
int graph[MAX_NODES][MAX_NODES];

Among them, indicatesgraph[i][j] whether there is an edge from node to node . The value is 1 if present, 0 otherwise.ij

2. Adjacency list

The adjacency list is a linked list composed of vertices and the edges connected to them, and the neighbor node information of each node is stored in the linked list. For sparse graphs, the adjacency list is more space-saving than the adjacency matrix, but to query the neighbor nodes of a node needs to traverse the entire linked list.

In C language, you can use structures and pointers to implement adjacency lists, for example:

#define MAX_NODES 100
struct AdjNode {
    
    
    int dest;
    struct AdjNode* next;
};
typedef struct AdjNode AdjNode;

struct Graph {
    
    
    AdjNode* adjLists[MAX_NODES];
};
typedef struct Graph Graph;

Among them, Graphthe structure contains an array adjLists, and each element is a pointer to the AdjNodestructure . In AdjNodethe structure , destit represents the number of the target node, nextand it is a pointer to the next neighbor node.

3. Incidence matrix

An incidence matrix is ​​a data structure that represents a graph as a two-dimensional array, where each element in the matrix represents the relationship between a vertex and an edge. In an incidence matrix, rows represent vertices and columns represent edges, with a value of 1 if the vertex is connected to that edge, and 0 otherwise.

In C language, a two-dimensional array can be used to implement an incidence matrix, for example:

#define MAX_NODES 100
#define MAX_EDGES 200
int matrix[MAX_NODES][MAX_EDGES];

Among them, matrix[i][j]indicates iwhether jis connected to the edge . The value is 1 if connected; otherwise, 0.

4. Cross linked list

The cross-linked list is a data structure further extended on the basis of the adjacency list. It can represent the structure of the directed graph, and at the same time avoids the shortcomings of querying the source and target points in the adjacency list. In the cross-linked list, each node has two fields, pointing to the outgoing edge starting from the node and the incoming edge ending at the node.

In C language, you can use structures and pointers to implement cross-linked lists, for example:

#define MAX_NODES 100

struct Edge {
    
    
    int from;
    int to;
    struct Edge* nextOut;
    struct Edge* nextIn;
};
typedef struct Edge Edge;

struct Graph {
    
    
    Edge* edges[MAX_NODES];
};
typedef struct Graph Graph;

Among them, Graphthe structure contains an array edges, and each element is a pointer to the Edgestructure . In Edgethe structure , fromit indicates the starting point of the edge, and toindicates the end point of the edge. nextOutIt is a pointer to the next outgoing edge starting from the node, and a nextInpointer to the next incoming edge starting from the node.

5. Forward star

The forward star is also extended on the basis of the adjacency list, which can support both directed and undirected graphs, and also solves the efficiency problem of querying all outgoing or incoming edges of a node in the adjacency list. In the forward star, each node no longer only stores a pointer to the first neighbor node, but stores a set of pointers to all neighbor nodes, and these pointers are arranged into a linked list in a certain order.

In C, forward stars can be implemented using structures and arrays, for example:

#define MAX_NODES 100
#define MAX_EDGES 200

struct Edge {
    
    
    int to;
    int weight; // 可选
    int next; // 指向下一条出边或入边
};
typedef struct Edge Edge;

struct Graph {
    
    
    Edge edges[MAX_EDGES];
    int head[MAX_NODES]; // 存储每个节点的第一条边的编号
    int edgeCount;
};
typedef struct Graph Graph;

Among them, Graphthe structure contains two arrays, edgesthe array stores the information of all edges, and headthe array stores the number of the first edge of each node, edgeCountindicating the number of edges. In Edgethe structure , toit indicates the end point of the edge, weightthe weight of the edge (optional), nextand the number of the next edge that is the same as the starting point of the edge.

6. Adjacency multiple tables

An adjacency multilist is a data structure used to represent undirected graphs, which stores each edge as an edge between two vertices, and for each node stores information about all the edges connected to that node. In an adjacency multilist, each node has a pointer to the first edge connected to that node, and each edge contains pointers to the next edge to its start and end points.

In C language, you can use structures and pointers to implement adjacency multiple lists, for example:

#define MAX_NODES 100
#define MAX_EDGES 200

struct Edge {
    
    
    int from;
    int to;
    int nextFrom; // 指向起点相同的下一条边
    int nextTo; // 指向终点相同的下一条边
};
typedef struct Edge Edge;

struct Graph {
    
    
    Edge edges[MAX_EDGES];
    int head[MAX_NODES]; // 存储每个节点的第一条边的编号
    int edgeCount;
};
typedef struct Graph Graph;

Among them, Graphthe structure contains two arrays, edgesthe array stores the information of all edges, and headthe array stores the number of the first edge of each node, edgeCountindicating the number of edges. In Edgethe structure , fromand represent the start point and end point of the edge torespectively , andnextFrom point to the number of the next edge with the same start point and the same end point as the edge respectively.nextTo

3. Basic operation of graph

In the scope of the postgraduate entrance examination, you need to master the basic operations of the following diagrams:

  1. Create a graph: Create an empty graph data structure according to actual needs, and initialize the relevant information of each node and edge.
  2. Add Node: Add a new node to the graph and update the information of all edges connected to the node.
  3. Add Edge: Add a new edge to the graph and update the neighbor node list of the corresponding node.
  4. Delete Node: Deletes a node from the graph and removes all its connected edges.
  5. Delete Edge: Delete an edge from the graph and update the corresponding node's neighbor node list.
  6. Find Node: Find whether a specific node is contained in the graph and return its related information.
  7. Find Edges: Find out whether a specific edge is contained in the graph, and return its related information.
  8. Traverse Graph: Traverse all nodes and edges in the graph for further analysis and processing.
  9. Topological Sort: Perform a topological sort on a directed acyclic graph to determine the execution order of all nodes.
  10. Shortest Path: Computes the shortest path between two nodes in order to find an optimal solution.
  11. Minimum spanning tree: Find the minimum spanning tree of an undirected graph to obtain the least cost connection.

4. Realization of core functions

1. Create a graph

// 创建邻接矩阵表示的无向图
void CreateGraph(Graph *G) {
    
    
	int i, j, k, weight;
	
	printf("请输入图的顶点数和边数(用空格隔开): ");
	scanf("%d %d", &G->vertex_num, &G->edge_num);
	
	printf("请输入%d个顶点的信息: ", G->vertex_num);
	for (i = 0; i < G->vertex_num; ++i)
		scanf("%s", &G->vertex[i]);
	
	for (i = 0; i < G->vertex_num; ++i)
		for (j = 0; j < G->vertex_num; ++j)
			G->edge[i][j] = 0;  // 初始化邻接矩阵
	
	printf("请输入%d条边的信息(起点 终点 权值):\n", G->edge_num);
	for (k = 0; k < G->edge_num; ++k) {
    
    
		scanf("%d %d %d", &i, &j, &weight);
		// 因为是无向图,所以需要对称赋值
		G->edge[i][j] = weight;
		G->edge[j][i] = weight;  
		//如果这里是创建有向图,那么就需要我们应该会改
	}
}

2. Insert vertices

// 在图G中插入顶点x
void InsertVertex(Graph *G, char x){
    
    
	if(G->vertex_num == MAX_VERTEX_NUM){
    
      // 图已满
		printf("Error: 超出最大结点范围\n");
		return;
	}
	G->vertex[G->vertex_num] = x;
	G->vertex_num++;
	//更新边信息
	for(int i=0;i<G->vertex_num-1;++i){
    
    
		G->edge[i][G->vertex_num-1] = 0; //初始化新顶点的出边
		G->edge[G->vertex_num-1][i] = 0; //初始化新顶点的入边
	}
}

3. Delete vertices

// 将数组中从 start 开始的元素向前移动一位
void moveArray(char arr[], int start, int end) {
    
    
	for (int i = start; i < end; ++i) {
    
    
		arr[i] = arr[i+1];
	}
}

// 在图G中删除顶点x
void DeleteVertex(Graph *G,char x){
    
    
	int n = -1;      //待删除结点在数组里的下标
	for(int i=0;i<G->vertex_num;i++){
    
    
		if(G->vertex[i]==x) {
    
    
			n=i;
			break;
		}
	}
	if(n == -1){
    
      // 如果节点不存在
		printf("Error: 结点不存在\n");
		return;
	}
	// 删除该节点对应的行和列
	for(int i=n;i<G->vertex_num-1;++i){
    
    
		moveArray(G->vertex, i, G->vertex_num - 1); // 将后面的节点前移
		for(int j=0;j<G->vertex_num;++j){
    
    
			G->edge[i][j] = G->edge[i+1][j]; // 前移一位
			G->edge[j][i] = G->edge[j][i+1];
		}
	}
	G->vertex_num--;
	// 更新最后一行和最后一列
	for(int i=0;i<G->vertex_num;++i){
    
    
		G->edge[G->vertex_num][i] = 0; // 删除最后一列
		G->edge[i][G->vertex_num] = 0; // 删除最后一行
	}
}

4. Find the first adjacent point

//求图G中顶点x的第一个邻接点
char FirstNeighbor(Graph *G,char x){
    
    
	int n = -1;      //待查询结点在数组里的下标
	for(int i=0;i<G->vertex_num;i++){
    
    
		if(G->vertex[i]==x) {
    
    
			n=i;
			break;
		}
	}
	if(n == -1){
    
      // 如果查询节点不存在
		printf("Error: 结点不存在\n");
	}
	int count=0;
	int nx;        //第一个邻接点在数组里的下标
	//现在知道了他在第n个,所以我们查询边矩阵第n行中第一个1在哪就知道第一个邻接点是谁
	for(int j=0;j<G->vertex_num;j++){
    
    
		if(G->edge[n][j]==1) count++;
		if(count==1){
    
    
			nx=j;
			break;
		}
	}
	return G->vertex[nx];
}

5. Find the remaining adjacent points

//返回图G中顶点x除顶点y的下一个邻接点
char NextNeighbor(Graph *G,char x,char y){
    
    
	int n = -1;      //待查询结点在数组里的下标
	for(int i=0;i<G->vertex_num;i++){
    
    
		if(G->vertex[i]==x) {
    
    
			n=i;
			break;
		}
	}
	if(n == -1){
    
      // 如果查询节点不存在
		printf("Error: 结点不存在\n");
	}
	int count=0;
	int nx;   //存放返回邻接点数组下标
	for(int j=0;j<G->vertex_num;j++){
    
    
		if(G->edge[n][j]==1){
    
    
			if(G->vertex[j]!=y){
    
    
				j=nx;
			}
		}	
	}
	return G->vertex[nx];
}

6. Judgment side

//判断图G中x和y之间是否有边
int Adjacent(Graph *G,char x,char y){
	int n1,n2;   //分别存储x和y的下标
	for(int i=0;i<G->vertex_num;i++){
		if(G->vertex[i]==x) {
			n1=i;
			break;
		}
	}
	for(int i=0;i<G->vertex_num;i++){
		if(G->vertex[i]==x) {
			n2=i;
			break;
		}
	}
	if(G->edge[n1][n2]==0 && G->edge[n2][n1]==0)
		return -1;
	else
		return 1;
}

All the above parts are implemented by selecting the more difficult algorithm in the third part

5. Complete code of C language

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_VERTEX_NUM 20  // 最大顶点数

typedef struct {
    
    
	char vertex[MAX_VERTEX_NUM];  // 存放顶点信息
	int edge[MAX_VERTEX_NUM][MAX_VERTEX_NUM];  // 存放边信息
	int vertex_num;  // 顶点数
	int edge_num;  // 边数
} Graph;

// 创建邻接矩阵表示的无向图
void CreateGraph(Graph *G) {
    
    
	int i, j, k, weight;
	
	printf("请输入图的顶点数和边数(用空格隔开): ");
	scanf("%d %d", &G->vertex_num, &G->edge_num);
	
	printf("请输入%d个顶点的信息: ", G->vertex_num);
	for (i = 0; i < G->vertex_num; ++i)
		scanf("%s", &G->vertex[i]);
	
	for (i = 0; i < G->vertex_num; ++i)
		for (j = 0; j < G->vertex_num; ++j)
			G->edge[i][j] = 0;  // 初始化邻接矩阵
	
	printf("请输入%d条边的信息(起点 终点 权值):\n", G->edge_num);
	for (k = 0; k < G->edge_num; ++k) {
    
    
		scanf("%d %d %d", &i, &j, &weight);
		// 因为是无向图,所以需要对称赋值
		G->edge[i][j] = weight;
		G->edge[j][i] = weight;  
		//如果这里是创建有向图,那么就需要我们应该会改
	}
}

// 在图G中插入顶点x
void InsertVertex(Graph *G, char x){
    
    
	if(G->vertex_num == MAX_VERTEX_NUM){
    
      // 图已满
		printf("Error: 超出最大结点范围\n");
		return;
	}
	G->vertex[G->vertex_num] = x;
	G->vertex_num++;
	//更新边信息
	for(int i=0;i<G->vertex_num-1;++i){
    
    
		G->edge[i][G->vertex_num-1] = 0; //初始化新顶点的出边
		G->edge[G->vertex_num-1][i] = 0; //初始化新顶点的入边
	}
}

// 将数组中从 start 开始的元素向前移动一位
void moveArray(char arr[], int start, int end) {
    
    
	for (int i = start; i < end; ++i) {
    
    
		arr[i] = arr[i+1];
	}
}

// 在图G中删除顶点x
void DeleteVertex(Graph *G,char x){
    
    
	int n = -1;      //待删除结点在数组里的下标
	for(int i=0;i<G->vertex_num;i++){
    
    
		if(G->vertex[i]==x) {
    
    
			n=i;
			break;
		}
	}
	if(n == -1){
    
      // 如果节点不存在
		printf("Error: 结点不存在\n");
		return;
	}
	// 删除该节点对应的行和列
	for(int i=n;i<G->vertex_num-1;++i){
    
    
		moveArray(G->vertex, i, G->vertex_num - 1); // 将后面的节点前移
		for(int j=0;j<G->vertex_num;++j){
    
    
			G->edge[i][j] = G->edge[i+1][j]; // 前移一位
			G->edge[j][i] = G->edge[j][i+1];
		}
	}
	G->vertex_num--;
	// 更新最后一行和最后一列
	for(int i=0;i<G->vertex_num;++i){
    
    
		G->edge[G->vertex_num][i] = 0; // 删除最后一列
		G->edge[i][G->vertex_num] = 0; // 删除最后一行
	}
}

//求图G中顶点x的第一个邻接点
char FirstNeighbor(Graph *G,char x){
    
    
	int n = -1;      //待查询结点在数组里的下标
	for(int i=0;i<G->vertex_num;i++){
    
    
		if(G->vertex[i]==x) {
    
    
			n=i;
			break;
		}
	}
	if(n == -1){
    
      // 如果查询节点不存在
		printf("Error: 结点不存在\n");
	}
	int count=0;
	int nx;        //第一个邻接点在数组里的下标
	//现在知道了他在第n个,所以我们查询边矩阵第n行中第一个1在哪就知道第一个邻接点是谁
	for(int j=0;j<G->vertex_num;j++){
    
    
		if(G->edge[n][j]==1) count++;
		if(count==1){
    
    
			nx=j;
			break;
		}
	}
	return G->vertex[nx];
}

//返回图G中顶点x除顶点y的下一个邻接点
char NextNeighbor(Graph *G,char x,char y){
    
    
	int n = -1;      //待查询结点在数组里的下标
	for(int i=0;i<G->vertex_num;i++){
    
    
		if(G->vertex[i]==x) {
    
    
			n=i;
			break;
		}
	}
	if(n == -1){
    
      // 如果查询节点不存在
		printf("Error: 结点不存在\n");
	}
	int count=0;
	int nx;   //存放返回邻接点数组下标
	for(int j=0;j<G->vertex_num;j++){
    
    
		if(G->edge[n][j]==1){
    
    
			if(G->vertex[j]!=y){
    
    
				j=nx;
			}
		}	
	}
	return G->vertex[nx];
}

//判断图G中x和y之间是否有边
int Adjacent(Graph *G,char x,char y){
    
    
	int n1,n2;   //分别存储x和y的下标
	for(int i=0;i<G->vertex_num;i++){
    
    
		if(G->vertex[i]==x) {
    
    
			n1=i;
			break;
		}
	}
	for(int i=0;i<G->vertex_num;i++){
    
    
		if(G->vertex[i]==x) {
    
    
			n2=i;
			break;
		}
	}
	if(G->edge[n1][n2]==0 && G->edge[n2][n1]==0)
		return -1;
	else
		return 1;
}

int main() {
    
    
	Graph G;
	CreateGraph(&G);
	return 0;
}



6. Running results

9HRC.jpg

I just recently reviewed data structures and algorithms, and by the way, I participated in Yingjie's Rising Star Program. This is the first submission this week.

Guess you like

Origin blog.csdn.net/weixin_51496226/article/details/131280250