首页 > 其他分享 >12月17日每日总结

12月17日每日总结

时间:2025-01-10 19:22:39浏览次数:1  
标签:总结 12 17 parent int weight Edge key numVertices

今日主要学习了图中寻找最小生成树的算法:克鲁斯卡尔算法和普利姆算法
克鲁斯卡尔算法:
构建边结构体:用于存储图中的边信息,包括边的两个端点以及边的权值。
typedef struct Edge {
int src;
int dest;
int weight;
} Edge;

对边进行排序:可以使用 C 语言标准库中的 qsort 函数来实现对边数组的排序,按照边的权值从小到大排序。排序比较函数示例如下:
int compareEdges(const void *a, const void *b) {
Edge *edgeA = (Edge *)a;
Edge *edgeB = (Edge *)b;
return edgeA->weight - edgeB->weight;
}

并查集操作:为了判断加入一条边是否会形成环,需要使用并查集数据结构。并查集用于维护元素的分组信息,每个组有一个代表元素,通过查找操作可以快速确定两个元素是否属于同一组。以下是并查集的一些基本操作函数:
// 初始化并查集,每个顶点的父节点初始化为自身
void initUnionFind(int parent[], int numVertices) {
for (int i = 0; i < numVertices; i++) {
parent[i] = i;
}
}

// 查找元素所在集合的代表元素(根节点)
int find(int parent[], int i) {
if (parent[i] == i) {
return i;
}
return find(parent, parent[i]);
}

// 合并两个集合,将一个集合的根节点的父节点设置为另一个集合的根节点
void unionSets(int parent[], int x, int y) {
int setX = find(parent, x);
int setY = find(parent, y);
if (setX!= setY) {
parent[setX] = setY;
}
}

执行克鲁斯卡尔算法:遍历排序后的边数组,对于每条边,判断其两个端点是否属于不同的集合(即加入这条边不会形成环),如果是,则将这条边加入最小生成树的边集合,并合并两个端点所在的集合。
void kruskal(Graph *graph) {
int numVertices = graph->numVertices;
Edge *result = (Edge *)malloc((numVertices - 1) * sizeof(Edge)); // 存储最小生成树的边
int e = 0; // 结果数组的索引
int i = 0; // 排序后的边数组的索引

// 对图中的边进行排序
Edge *edges = getEdges(graph);  // 假设已有函数获取图的所有边
qsort(edges, graph->numEdges, sizeof(Edge), compareEdges);

// 初始化并查集
int *parent = (int *)malloc(numVertices * sizeof(int));
initUnionFind(parent, numVertices);

while (e < numVertices - 1 && i < graph->numEdges) {
    Edge nextEdge = edges[i++];
    int x = find(parent, nextEdge.src);
    int y = find(parent, nextEdge.dest);
    if (x!= y) {
        e++;
        result[e - 1] = nextEdge;
        unionSets(parent, x, y);
    }
}

// 输出最小生成树的边及总权值
int totalWeight = 0;
for (int j = 0; j < e; j++) {
    printf("Edge: %d - %d, Weight: %d\n", result[j].src, result[j].dest, result[j].weight);
    totalWeight += result[j].weight;
}
printf("Total weight of minimum spanning tree: %d\n", totalWeight);

free(edges);
free(result);
free(parent);

}
普利姆算法:
定义辅助数组:用于记录每个顶点到已生成树的最小距离,以及标记顶点是否已被加入已生成树。

define INF 99999 // 表示无穷大,用于初始化未连接顶点的距离

int minKey(int key[], bool mstSet[], int numVertices) {
int min = INF, min_index;
for (int v = 0; v < numVertices; v++) {
if (mstSet[v] == false && key[v] < min) {
min = key[v];
min_index = v;
}
}
return min_index;
}

执行普利姆算法:从一个起始顶点开始,初始化距离数组和标记数组,然后重复选择距离最小的未加入顶点,更新与其相邻顶点的距离,直到所有顶点都被加入已生成树。
void prim(Graph *graph, int startVertex) {
int numVertices = graph->numVertices;
int parent[numVertices]; // 存储最小生成树中每个顶点的父节点
int key[numVertices]; // 存储每个顶点到已生成树的最小距离
bool mstSet[numVertices]; // 标记顶点是否已在最小生成树中

// 初始化
for (int i = 0; i < numVertices; i++) {
    key[i] = INF;
    mstSet[i] = false;
}

key[startVertex] = 0;
parent[startVertex] = -1;  // 起始顶点没有父节点

for (int count = 0; count < numVertices - 1; count++) {
    int u = minKey(key, mstSet, numVertices);
    mstSet[u] = true;

    AdjListNode *temp = graph->adjLists[u];
    while (temp!= NULL) {
        int v = temp->dest;
        int weight = temp->weight;
        if (mstSet[v] == false && weight < key[v]) {
            parent[v] = u;
            key[v] = weight;
        }
        temp = temp->next;
    }
}

// 输出最小生成树的边及总权值
int totalWeight = 0;
for (int i = 1; i < numVertices; i++) {
    printf("Edge: %d - %d, Weight: %d\n", parent[i], i, key[i]);
    totalWeight += key[i];
}
printf("Total weight of minimum spanning tree: %d\n", totalWeight);

}

标签:总结,12,17,parent,int,weight,Edge,key,numVertices
From: https://www.cnblogs.com/Genghao11/p/18664559

相关文章

  • vscode配latex,用于latex排版,花了几个小时终于研究明白了(已总结)
    掉坑里了,切记texlive必须要安装,不然就是下面这样子步骤一:texlive安装链接:https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/Images/下载完后双击ISO文件,再双击install-tl-windows.bat双击install-tl-windows.bat之后会自动跳转1-2个页面到下面这个页面......
  • 12月25日
    泛型的类型参数泛型的类型参数可以是任何有效的Java类型,但不能是基本数据类型。可以使用类、接口或自定义类型作为类型参数。类型参数的限制可以使用extends关键字对类型参数进行限制,表示类型参数必须是某个类的子类或实现某个接口的类。例如:javapublicclassBox{private......
  • 12月20日总结
    今日学习了队列的相关操作:定义:defineMAX_SIZE100//假设队列最大容量为100typedefstructQueue{intdata[MAX_SIZE];intfront;intrear;}Queue;初始化:voidinitQueue(Queue*q){q->front=0;q->rear=0;}入队操作:voidenqueue(Queue*q,intvalue){i......
  • 2024-12-1-#{}与¥{}的区别-response
    {}与¥{}的区别response实现重定向response响应字符数据response响应字节数据以及导入工具类实现响应......
  • 12月14日总结
    今日学习了,单链表的基本操作:定义:typedefstructListNode{intdata;structListNode*next;}ListNode;初始化:voidinsertAtHead(ListNode**head,intvalue){ListNode*newNode=(ListNode*)malloc(sizeof(ListNode));newNode->data=value;......
  • 12月15日总结
    今日全身心投入到数据结构中双链表的学习,相较于之前接触的单链表,双链表有着独特的魅力与复杂性。概念上,双链表的每个节点不仅包含指向后继节点的指针(如同单链表),还增设了指向前驱节点的指针。这一设计使得链表在双向遍历上独具优势,无论是从表头向表尾推进,还是反向操作,都能轻松实现......
  • 12月16日总结
    今日学习了双链表的相关操作:一、创建双链表创建双链表的第一步是定义节点结构体,它包含数据域、指向前驱节点的指针prev和指向后继节点的指针next。//双链表节点结构体定义typedefstructDoubleListNode{intdata;structDoubleListNode*prev;structDoubleListNode......
  • 12月17日总结
    今日重点学习数据结构中的栈,它遵循后进先出原则,类似单端进出的储物箱,顶部是唯一的数据出入口,这使其在处理特定顺序问题上优势显著。学习中探究了栈的基本操作,初始化时用结构体表示栈,含存储数据的数组(或链表)与指示栈顶的指针top,初始top设为-1代表空栈。入栈是先让top加1......
  • 12月22日
    今日深入学习了Java中的注解(Annotations)机制,这是Java语言的一个重要特性,用于为程序元素(如类、方法、字段等)提供元数据。注解不直接影响程序的直接运行,但可以被编译器、工具或运行时环境读取和处理,从而实现各种强大的功能,如代码生成、依赖注入、测试等。注解是Java语言中的一种特......
  • 12月18日总结
    今日学习了栈的相关操作:初始化:defineMAX_SIZE100//假设栈的最大容量为100typedefstructStack{intdata[MAX_SIZE];inttop;}Stack;//栈的初始化函数voidinitStack(Stack*s){s->top=-1;}一、增-入栈(Push)入栈操作是向栈顶添加一个新元素,使其成为新的......