C/C++每日一练:合并K个有序链表
本篇博客将探讨如何 “合并K个有序链表” 这一经典问题。本文将从题目要求、解题思路、过程解析和相关知识点逐步展开,同时提供详细注释的代码示例。
链表(Linked List)
链表是一种线性数据结构,由一系列节点(Node)通过指针链接在一起。与数组不同,链表中的元素在内存中不需要连续存储,每个节点包含两部分:
- 数据部分:存储节点的值或数据。
- 指针部分:存储指向下一个节点的地址(单链表)或上一个和下一个节点的地址(双向链表)。
链表的类型主要有以下几种:
- 单链表:每个节点只指向下一个节点。
- 双向链表:每个节点既有指向下一个节点的指针,也有指向上一个节点的指针。
- 循环链表:链表的最后一个节点指向链表的头节点,形成循环。
链表的特点:
- 动态大小:可以根据需要动态地增加或减少元素,无需预分配存储空间。
- 插入/删除效率高:在链表的任意位置进行插入或删除操作只需修改指针,不涉及大量元素的移动,效率较高。
- 随机访问效率低:由于链表不支持直接访问任意位置的元素,需要通过遍历来查找特定位置的节点。
如下图所示:
题目要求
给定 k 个有序的链表,要求将它们合并成一个有序链表,并返回最终的结果链表。
输入
- 一个包含 k 个链表的数组,其中每个链表均已按升序排列。
输出
- 一个合并后的链表,且链表内元素按升序排列。
约束
k >= 1
- 每个链表的长度可能不同,且长度总和不超过 10^5。
- 链表节点的值范围在 [-10^4, 10^4]。
解题思路
方法一:逐一合并
- 将所有链表两两合并。
- 时间复杂度为 O(k⋅n),其中 n 为平均每个链表的长度。
方法二:使用最小堆(优先队列)
- 利用最小堆维护每个链表当前节点的最小值,逐步提取并加入结果链表。
- 时间复杂度为 O(n⋅logk)。
方法三:分治合并
- 将 k 个链表两两分组合并。
- 类似归并排序,时间复杂度为 O(n⋅logk)。
以下主要以 方法二:使用最小堆 和 方法三:分治合并 为例进行解析。
过程解析
方法一:逐一合并
思路
将第一个链表作为初始结果链表,然后逐个将剩余的链表与当前结果链表进行合并,直到所有链表合并完毕。
实现步骤
- 初始化结果链表为第一个链表。
- 遍历链表数组,调用合并两个链表的函数,将当前链表与结果链表合并。
- 返回最终结果链表。
优点
- 实现简单,逻辑清晰,适合入门时使用。
缺点
- 效率较低,尤其在链表数量较多或链表长度较大时。
方法二:使用最小堆
思路
利用最小堆(优先队列)维护链表当前节点的最小值,每次从堆中提取最小值并将其加入结果链表,同时推进对应链表的指针。
实现步骤
- 创建一个最小堆,将所有链表的头节点加入堆中。
- 反复提取堆顶最小值,将其加入结果链表。
- 如果提取的节点有下一个节点,将下一个节点加入堆中。
- 堆为空时,所有节点已处理完,返回结果链表。
优点
- 在链表数量较多时表现优秀。
- 保证结果链表的构建是高效的。
缺点
- 实现稍复杂,需要熟悉堆的数据结构。
方法三:分治合并
思路
通过分治思想,将链表分成两组,递归地合并每组链表,直到最终只剩一个合并后的链表。
实现步骤
- 将链表数组两两分组,递归合并每组链表。
- 每次合并两个链表使用合并两个有序链表的逻辑。
- 最终返回唯一的合并链表。
优点
- 效率高,适合大规模链表数量。
- 思路清晰,类似归并排序的合并逻辑。
缺点
- 实现需要递归,可能在栈深度受限的系统中受到限制。
三种方法的比较
方法 | 时间复杂度 | 空间复杂度 | 适用场景 | 实现复杂度 |
---|---|---|---|---|
逐一合并 | O(k⋅n)O(k \cdot n)O(k⋅n) | O(n)O(n)O(n) | 链表数量较少时 | 简单 |
使用最小堆 | O(n⋅logk)O(n \cdot \log k)O(n⋅logk) | O(k)O(k)O(k) | 链表数量较多时 | 中等 |
分治合并 | O(n⋅logk)O(n \cdot \log k)O(n⋅logk) | O(logk)O(\log k)O(logk) | 大规模链表合并 | 中等 |
- 如果链表数量很少,逐一合并的实现最简单,适合初学者练习。
- 如果链表数量较多且长度较短,最小堆法表现较优。
- 如果链表数量和长度都较多,分治合并法综合性能最好。
相关知识点
-
链表操作
- 基本操作:插入、删除、遍历。
- 注意指针的动态分配与释放。
-
优先队列
- C++ STL 中的std::priority_queue。
- 自定义排序方式。
-
分治策略
- 递归与归并思想。
示例代码
C代码实现:逐一合并
#include <stdio.h> // 包含标准输入输出头文件,提供 printf、scanf 等函数
#include <stdlib.h> // 包含动态内存分配和其他实用功能函数,如 malloc 和 free// 定义链表节点结构
typedef struct ListNode {int val; // 节点的值struct ListNode* next; // 指向下一个节点的指针
} ListNode;// 合并两个有序链表的函数
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {// 如果 l1 为空,直接返回 l2if (!l1) return l2;// 如果 l2 为空,直接返回 l1if (!l2) return l1;// 比较两个链表头节点的值,递归合并较小值的后续节点if (l1->val < l2->val) {l1->next = mergeTwoLists(l1->next, l2); // l1 较小,递归处理 l1->nextreturn l1; // 返回 l1 作为新的头节点} else {l2->next = mergeTwoLists(l1, l2->next); // l2 较小,递归处理 l2->nextreturn l2; // 返回 l2 作为新的头节点}
}// 合并 K 个有序链表的函数
ListNode* mergeKLists(ListNode** lists, int k) {// 如果链表数组为空,直接返回 NULLif (k == 0) return NULL;// 初始化结果链表为第一个链表ListNode* mergedList = lists[0];// 遍历链表数组,从第二个链表开始逐一合并for (int i = 1; i < k; ++i) {mergedList = mergeTwoLists(mergedList, lists[i]); // 合并当前结果链表和下一个链表}// 返回最终合并完成的结果链表return mergedList;
}// 辅助函数:打印链表
void printList(ListNode* head) {// 遍历链表,打印每个节点的值while (head) {printf("%d -> ", head->val); // 输出当前节点的值head = head->next; // 移动到下一个节点}printf("NULL\n"); // 链表结束后打印 NULL
}// 辅助函数:创建链表节点
ListNode* createNode(int val) {// 动态分配内存为新节点ListNode* node = (ListNode*)malloc(sizeof(ListNode));node->val = val; // 设置节点值node->next = NULL; // 初始化下一个节点指针为空return node; // 返回新节点的指针
}// 主函数测试
int main()
{// 构建测试链表 1:1 -> 4 -> 5ListNode* list1 = createNode(1);list1->next = createNode(4);list1->next->next = createNode(5);// 构建测试链表 2:1 -> 3 -> 4ListNode* list2 = createNode(1);list2->next = createNode(3);list2->next->next = createNode(4);// 构建测试链表 3:2 -> 6ListNode* list3 = createNode(2);list3->next = createNode(6);// 创建链表数组存储所有链表ListNode* lists[] = {list1, list2, list3};// 合并链表ListNode* mergedList = mergeKLists(lists, 3);// 输出结果链表printf("Merged List: ");printList(mergedList);return 0; // 程序正常结束
}
C代码实现:最小堆解法
示例代码
#include <stdio.h> // 包含标准输入输出头文件
#include <stdlib.h> // 包含动态内存分配函数// 定义链表节点结构
typedef struct ListNode {int val; // 节点值struct ListNode* next; // 指向下一个节点的指针
} ListNode;// 定义小顶堆结构
typedef struct MinHeap {ListNode** array; // 存储链表节点的指针数组int size; // 当前堆的大小int capacity; // 堆的容量
} MinHeap;// 创建小顶堆
MinHeap* createMinHeap(int capacity) {// 分配堆结构和数组的内存MinHeap* heap = (MinHeap*)malloc(sizeof(MinHeap));heap->array = (ListNode**)malloc(sizeof(ListNode*) * capacity);heap->size = 0; // 初始化堆的大小为 0heap->capacity = capacity; // 设置堆容量return heap; // 返回堆的指针
}// 交换两个链表节点的指针
void swap(ListNode** a, ListNode** b) {ListNode* temp = *a;*a = *b;*b = temp;
}// 堆化调整函数(维护堆的性质)
void heapify(MinHeap* heap, int i) {int smallest = i; // 假设当前节点为最小值int left = 2 * i + 1; // 左子节点索引int right = 2 * i + 2; // 右子节点索引// 如果左子节点更小,更新最小值索引if (left < heap->size && heap->array[left]->val < heap->array[smallest]->val)smallest = left;// 如果右子节点更小,更新最小值索引if (right < heap->size && heap->array[right]->val < heap->array[smallest]->val)smallest = right;// 如果最小值不是当前节点,交换并递归调整if (smallest != i) {swap(&heap->array[i], &heap->array[smallest]);heapify(heap, smallest);}
}// 将节点插入到堆中
void insertHeap(MinHeap* heap, ListNode* node) {// 将新节点放在堆末尾heap->array[heap->size++] = node;int i = heap->size - 1; // 当前节点的索引// 向上调整节点位置以维护堆的性质while (i && heap->array[(i - 1) / 2]->val > heap->array[i]->val) {swap(&heap->array[i], &heap->array[(i - 1) / 2]);i = (i - 1) / 2; // 移动到父节点}
}// 从堆中提取最小值节点
ListNode* extractMin(MinHeap* heap) {if (heap->size == 0) return NULL; // 堆为空时返回 NULL// 获取堆顶最小值ListNode* root = heap->array[0];// 将堆末尾的节点移到堆顶heap->array[0] = heap->array[--heap->size];// 调整堆以恢复堆的性质heapify(heap, 0);return root; // 返回提取的最小值节点
}// 合并 K 个有序链表的函数
ListNode* mergeKLists(ListNode** lists, int k) {// 创建最小堆MinHeap* heap = createMinHeap(k);// 将每个链表的头节点加入堆中for (int i = 0; i < k; ++i) {if (lists[i]) insertHeap(heap, lists[i]);}// 创建结果链表的哑节点(dummy 节点)ListNode dummy;ListNode* tail = &dummy; // 结果链表的尾部指针dummy.next = NULL;// 从堆中逐一提取最小值节点并加入结果链表while (heap->size > 0) {// 提取堆中的最小值节点ListNode* minNode = extractMin(heap);// 将最小值节点连接到结果链表tail->next = minNode;tail = tail->next; // 更新尾部指针// 如果提取的节点还有下一个节点,将其加入堆中if (minNode->next) insertHeap(heap, minNode->next);}// 释放堆的内存free(heap->array);free(heap);return dummy.next; // 返回结果链表的头节点
}// 辅助函数:打印链表
void printList(ListNode* head) {while (head) {printf("%d -> ", head->val); // 打印节点值head = head->next; // 移动到下一个节点}printf("NULL\n"); // 链表结束
}// 辅助函数:创建链表节点
ListNode* createNode(int val) {ListNode* node = (ListNode*)malloc(sizeof(ListNode)); // 分配内存node->val = val; // 设置节点值node->next = NULL; // 初始化下一个指针return node;
}// 主函数测试
int main()
{// 构建测试链表 1:1 -> 4 -> 5ListNode* list1 = createNode(1);list1->next = createNode(4);list1->next->next = createNode(5);// 构建测试链表 2:1 -> 3 -> 4ListNode* list2 = createNode(1);list2->next = createNode(3);list2->next->next = createNode(4);// 构建测试链表 3:2 -> 6ListNode* list3 = createNode(2);list3->next = createNode(6);// 创建链表数组ListNode* lists[] = {list1, list2, list3};// 合并链表ListNode* mergedList = mergeKLists(lists, 3);// 输出结果链表printf("Merged List: ");printList(mergedList);return 0; // 程序结束
}
补充
小顶堆性质
小顶堆(Min-Heap) 是一种完全二叉树,它具有以下性质:
堆的结构性质:
- 小顶堆是一棵完全二叉树,即树是从左到右逐层填满的,只有最后一层可能不满,但节点必须从左向右连续排列。
堆的值性质:
- 每个节点的值都小于或等于其子节点的值。
- 即:对于任意节点
i
,有:
A[i] ≤ A[2i+1](左子节点值)
A[i] ≤ A[2i+2](右子节点值)
由于这两个性质,堆的最小值始终存储在根节点(即数组的第一个位置)。
数组表示:堆可以使用数组表示,将完全二叉树的节点按层序遍历的顺序存储:
索引: 0 1 2 3 4 5
值: 10 15 20 30 40 25
在数组中,可以通过以下规则找到父子节点的关系:
- 父节点索引: parent(i) = (i−1) / 2
- 左子节点索引: left(i) = 2i+1
- 右子节点索引: right(i) = 2i+2
示例:一个满足小顶堆性质的完全二叉树:
10/ \15 20/ \ /30 40 25
heapify 函数的作用
void heapify(MinHeap* heap, int i);
- i: 要调整的节点在堆数组中的索引。
- heap: 表示一个小顶堆,包含节点数组和堆的大小。
heapify 函数是小顶堆的调整函数,用来维护堆的性质(即每个节点的值都不大于其子节点的值)。它的作用是:
- 从索引 i 开始,将子树调整为满足小顶堆性质。
- 如果某节点不满足小顶堆性质,则通过交换该节点和其较小子节点的值,并递归调整子树,直到整个堆满足小顶堆性质。
实现逻辑
- 假设当前节点的值是最小的(设索引为 smallest)。
- 比较当前节点和其左、右子节点的值:
- 如果左子节点更小,更新 smallest为左子节点索引。
- 如果右子节点更小,更新 smallest为右子节点索引。
- 如果 smallest 发生变化(当前节点不是最小值),交换当前节点和 smallest的值。
- 递归调用 heapify,确保调整后的子树也满足小顶堆性质。
示例说明
假设有以下堆数组,表示一个不完全满足小顶堆性质的堆:
索引: 0 1 2 3 4 5
值: 40 15 20 30 10 25
对应的堆结构:
40/ \15 20/ \ /30 10 25
调用 heapify(heap, 0)
- 当前节点:
40
(索引 0)。 - 左子节点:
15
(索引 1)。 - 右子节点:
20
(索引 2)。 - 最小值为左子节点
15
,交换40
和15
。
调整后堆数组:
索引: 0 1 2 3 4 5
值: 15 40 20 30 10 25
对应堆结构:
15/ \40 20/ \ /30 10 25
递归调用 heapify(heap, 1):
- 当前节点:
40
(索引 1)。 - 左子节点:
30
(索引 3)。 - 右子节点:
10
(索引 4)。 - 最小值为右子节点
10
,交换40
和10
。
调整后堆数组:
索引: 0 1 2 3 4 5
值: 15 10 20 30 40 25
对应堆结构:
15/ \10 20/ \ /30 40 25
heapify(heap, 4) 不再需要调整,因为 40 没有子节点。
最终堆数组:
索引: 0 1 2 3 4 5
值: 15 10 20 30 40 25
最终堆结构:
15/ \10 20/ \ /30 40 25
C++代码实现:分治解法
#include <stdio.h> // 包含标准输入输出头文件
#include <stdlib.h> // 包含动态内存分配函数// 定义链表节点结构
typedef struct ListNode {int val; // 节点值struct ListNode* next; // 指向下一个节点的指针
} ListNode;// 合并两个有序链表的函数
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {// 如果 l1 为空,直接返回 l2if (!l1) return l2;// 如果 l2 为空,直接返回 l1if (!l2) return l1;// 比较两个链表的头节点,选择较小值作为合并后链表的头节点if (l1->val < l2->val) {l1->next = mergeTwoLists(l1->next, l2); // 递归处理 l1->next 和 l2return l1; // 返回 l1 作为当前链表头} else {l2->next = mergeTwoLists(l1, l2->next); // 递归处理 l1 和 l2->nextreturn l2; // 返回 l2 作为当前链表头}
}// 分治法合并 K 个有序链表
ListNode* mergeKListsDivideAndConquer(ListNode** lists, int left, int right) {// 如果左边界等于右边界,表示只剩下一个链表if (left == right) return lists[left];// 如果左边界大于右边界,返回 NULLif (left > right) return NULL;// 计算中间位置int mid = left + (right - left) / 2;// 递归地合并左半部分链表ListNode* l1 = mergeKListsDivideAndConquer(lists, left, mid);// 递归地合并右半部分链表ListNode* l2 = mergeKListsDivideAndConquer(lists, mid + 1, right);// 合并左右两部分链表return mergeTwoLists(l1, l2);
}// 主函数入口,用于调用分治法合并链表
ListNode* mergeKLists(ListNode** lists, int k) {// 如果链表数组为空,直接返回 NULLif (k == 0) return NULL;// 调用分治法进行合并return mergeKListsDivideAndConquer(lists, 0, k - 1);
}// 辅助函数:打印链表
void printList(ListNode* head) {while (head) {printf("%d -> ", head->val); // 打印当前节点的值head = head->next; // 移动到下一个节点}printf("NULL\n"); // 链表结束
}// 辅助函数:创建链表节点
ListNode* createNode(int val) {ListNode* node = (ListNode*)malloc(sizeof(ListNode)); // 分配内存node->val = val; // 设置节点值node->next = NULL; // 初始化指针return node; // 返回新节点
}// 主函数测试
int main()
{// 构建测试链表 1:1 -> 4 -> 5ListNode* list1 = createNode(1);list1->next = createNode(4);list1->next->next = createNode(5);// 构建测试链表 2:1 -> 3 -> 4ListNode* list2 = createNode(1);list2->next = createNode(3);list2->next->next = createNode(4);// 构建测试链表 3:2 -> 6ListNode* list3 = createNode(2);list3->next = createNode(6);// 创建链表数组ListNode* lists[] = {list1, list2, list3};// 调用分治法合并链表ListNode* mergedList = mergeKLists(lists, 3);// 打印结果链表printf("Merged List: ");printList(mergedList);return 0; // 程序正常结束
}
相关文章:
C/C++每日一练:合并K个有序链表
本篇博客将探讨如何 “合并K个有序链表” 这一经典问题。本文将从题目要求、解题思路、过程解析和相关知识点逐步展开,同时提供详细注释的代码示例。 链表(Linked List) 链表是一种线性数据结构,由一系列节点(Node&…...
jmeter基础07_组件的层级
课程大纲 1. 优先级/执行顺序(一般情况) 同级组件:按组件先后顺序执行。如:同一层的线程组、同一层的http请求。 上下级组件:先执行外层(上级),再执行内层(下级ÿ…...
【QNX+Android虚拟化方案】125 - 如何创建android-spare镜像
【QNX+Android虚拟化方案】125 - 如何创建android-spare镜像 1. Android侧创建 (ext4 / sparse) test_img.img 镜像 方法一2. Android侧创建 (ext4 / sparse) test_img.img 镜像 方法二3. qnx 侧 分区透传Android 配置3.1 配置分区透传3.2 Android 侧分区 rename3.3 创建挂载目…...
大R玩家流失预测在休闲社交游戏中的应用
摘要 预测玩家何时会离开游戏为延长玩家生命周期和增加收入贡献创造了独特的机会。玩家可以被激励留下来,战略性地与公司组合中的其他游戏交叉链接,或者作为最后的手段,通过游戏内广告传递给其他公司。本文重点预测休闲社交游戏中高价值玩家…...
使用Postman搞定各种接口token实战
现在许多项目都使用jwt来实现用户登录和数据权限,校验过用户的用户名和密码后,会向用户响应一段经过加密的token,在这段token中可能储存了数据权限等,在后期的访问中,需要携带这段token,后台解析这段token才…...
【C++】printf 函数详解与格式化输出控制
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯printf 基础用法1.1 printf 的常见占位符1.2 占位符与参数的对应关系1.3 换行控制示例: 💯格式化输出控制2.1 输出宽度控制2.1.1 指定最小宽度 2.2 …...
在21世纪的我用C语言探寻世界本质——字符函数和字符串函数(1)
人无完人,持之以恒,方能见真我!!! 共同进步!! 文章目录 一、字符分类函数二、字符转换函数三、strlen的使用和模拟实现四、strcpy的使用和模拟实现五、strcat的使用和模拟实现六、strcmp的使用和…...
【专题】存储器管理
1. 存储器的层次结构 在计算机执行时,几乎每一条指令都涉及对存储器的访问,因此要求对存储器的访问速度能跟得上处理机的运行速度。 存储器的速度必须非常快,能与处理机的速度相匹配,否则会明显地影响到处理机的运行。 此外还要求…...
python股票数据分析(Pandas)练习
需求: 使用pandas读取一个CSV文件,文件内容包括股票名称、价格和交易量。完成以下任务: 找出价格最高的股票; 计算总交易量; 绘制价格折线图。 代码实现: import pandas as pd import matplotlib.pyplot …...
Hadoop生态圈框架部署(八)- Hadoop高可用(HA)集群部署
文章目录 前言一、部署规划二、Hadoop HA集群部署(手动部署)1. 下载hadoop2. 上传安装包2. 解压hadoop安装包3. 配置hadoop配置文件3.1 虚拟机hadoop1修改hadoop配置文件3.1.1 修改 hadoop-env.sh 配置文件3.3.2 修改 core-site.xml 配置文件3.3.3 修改 …...
抗干扰设计的检查细则
抗干扰设计是确保电子系统或设备在复杂电磁环境中稳定运行的重要环节,涉及多个方面的设计和实施。以下是对抗干扰设计的检查细则的详细归纳: 一、电源线与地线设计 电源线设计:选择合适的电源,尽量加宽电源线,保证电源…...
[Redis#12] 常用类型接口学习 | string | list
目录 0.准备 1.string get | set set_with_timeout_test.cpp set_nx_xx_test.cpp mset_test.cpp mget_test.cpp getrange_setrange_test.cpp incr_decr_test.cpp 2.list lpush_lrange_test.cpp rpush_test.cpp lpop_rpop_test.cpp blpop_test.cpp llen_test.cpp…...
React的ts文件中通过createElement拼接一段内容出来
比如接口返回一个值 const values [23.00, 40.00/kg];想做到如下效果, 如果单纯的用render渲染会很简单, 但是在ts文件中处理,所以采用了createElement拼接 代码如下: format: (values: string[]) > {if (!values || !val…...
【Git系列】Git 提交历史分析:深入理解`git log`命令
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
深度学习笔记——生成对抗网络GAN
本文详细介绍早期生成式AI的代表性模型:生成对抗网络GAN。 文章目录 一、基本结构生成器判别器 二、损失函数判别器生成器交替优化目标函数 三、GAN 的训练过程训练流程概述训练流程步骤1. 初始化参数和超参数2. 定义损失函数3. 训练过程的迭代判别器训练步骤生成器…...
《地球科学与环境学报》
《地球科学与环境学报》报道范围涵盖基础地质、矿产地质、水资源与环境、工程地质、地球物理、地球信息科学等领域,刊载国内外未公开发表的有创新性或意义重大的研究论文和综述文章。 来稿必须包括以下项目:题名(尽可能不要超过20字&…...
k8s 1.28 聚合层部署信息记录
–requestheader-client-ca-file –requestheader-allowed-namesfront-proxy-client –requestheader-extra-headers-prefixX-Remote-Extra- –requestheader-group-headersX-Remote-Group –requestheader-username-headersX-Remote-User –proxy-client-cert-file –proxy-cl…...
组件化设计的意义
鸿蒙操作系统(HarmonyOS)是华为公司开发的一款面向未来、面向全场景的分布式操作系统。它不仅能够支持多种智能终端设备,还能够实现跨设备之间的协同工作。为了满足不同设备的资源能力和业务需求,鸿蒙操作系统采用了组件化的设计方…...
2025 年河北省职业院校大数据应用与 服务(中职组)赛项样题
— 1 — 2025 年河北省职业院校大数据应用与 服务(中职组)赛项样题 一、背景描述 近年来,随着旅游业的快速发展和社交媒体的普及,一 些目的地因其独特的魅力或者事件而迅速走红,吸引了大量 游客涌入,使得当…...
宏集eXware物联网网关在水务管理系统上的应用
一、前言 水务管理系统涵盖了对城市水网、供水、排水、污水处理等多个环节的监控与管理。随着物联网(IoT)技术的快速发展,物联网网关逐渐成为水务管理系统中的关键组成部分。 宏集物联网网关以其高效的数据采集、传输和管理功能,…...
【看海的算法日记✨优选篇✨】第三回:二分之妙,寻径中道
🎬 个人主页:谁在夜里看海. 📖 个人专栏:《C系列》《Linux系列》《算法系列》 ⛰️ 一念既出,万山无阻 目录 📖一、算法思想 细节问题 📚左右临界 📚中点选择 📚…...
yolov5 解决:export GIT_PYTHON_REFRESH=quiet
当我们在第一次运行YOLOv5中的train.py程序时:可能会出现以下报错: This initial warning can be silenced or aggravated in the future by setting the $GIT_PYTHON_REFRESH environment variable. Use one of the following values: - quiet|q|silen…...
MongoDB聚合操作
1.聚合操作 聚合操作处理数据记录并返回计算结果。聚合操作组值来自多个文档,可以对分组数据执行各种操作以返回单个结果。聚合操作包含三类:单一作用聚合、聚合管道、MapReduce。 单一作用聚合:提供了对常见聚合过程的简单访问,…...
Apple雷电5到底有多快?
在科技日新月异的今天,苹果公司始终走在技术创新的前沿。2023年9月12日,随着英特尔发布雷电5(Thunderbolt 5)规范,苹果迅速跟进,将其应用于自家的产品中。雷电5接口以其卓越的性能,彻底颠覆了我…...
项目快过:知识蒸馏 | 目标检测 |FGD | Focal and Global Knowledge Distillation for Detectors
公开时间:2022年3月9号 项目地址:https://github.com/yzd-v/FGD 论文地址:https://arxiv.org/pdf/2111.11837 知识蒸馏已成功地应用于图像分类。然而,目标检测要复杂得多,大多数知识蒸馏方法都失败了。本文指出&#…...
Spring Boot日志总结
文章目录 1.我们的日志2.日志的作用3.使用日志对象打印日志4.日志框架介绍5.深入理解门面模式(外观模式)6.日志格式的说明7.日志级别7.1日志级别分类7.2配置文件添加日志级别 8.日志持久化9.日志文件的拆分9.1官方文档9.2IDEA演示文件分割 10.日志格式的配置11.更简单的日志输入…...
PostgreSQL最常用数据类型-重点说明自增主键处理
简介 PostgreSQL提供了非常丰富的数据类型,我们平常使用最多的基本就3类: 数字类型字符类型时间类型 这篇文章重点介绍这3中类型,因为对于高并发项目还是推荐:尽量使用简单类型,把运算和逻辑放在应用中,…...
androidstudio 最新继承 proto kts 方式
在Android Studio中,如果你使用的是Kotlin DSL(.kts文件)来配置你的Gradle项目,并且你想集成Protocol Buffers(Proto),你需要稍微调整你的配置方式。以下是如何在Kotlin DSL中配置Proto集成的步…...
【STM32学习】TB6612FNG驱动芯片的学习,驱动电路的学习
目录 1、TB6612电机驱动芯片 1.1如下是芯片的引脚图: 1.2如下图是电机的控制逻辑: 1.3MOS管运转逻辑 1.3典型应用电路 2、H桥驱动电路 2.1、单极模式 2.2、双极模式 2.3、高低端MOS管导通条件 2.4、H桥电路设计 2.5、自举电路 3、电气特性 3…...
【AI战略思考13】克服懒惰,保持专注,提升效率,不再焦虑
【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 引言 我发现自己最近非常懒惰,浪费了很多时间,也容易分心,不够专注,效率低下,且每天都有点焦虑,因此制定了下面的要求和作息时间表。 目…...
基于Vue3+Element Plus 实现多表单校验
使用场景 表单校验在日常的开发需求中是一种很常见的需求,通常在提交表单发起请求前校验用户输入是否符合规则,通常只需formRef.value.validate()即可校验,但是,例如一些多步骤表单、动态表单、以及不同的用户角色可能看到不同的表…...
“岗位复合化、技能层次化” 高职大数据技术专业人才培养实践
在全球数字化浪潮的推动下,大数据技术已经成为引领社会进步和经济发展的核心动力。随着《关于深化现代职业教育体系建设改革的意见》等系列指导问文件的发布,我国高职大数据技术专业的教育正迎来全新机遇与挑战。这些政策不仅明确了职业教育改革的方向&a…...
Day2 生信新手笔记: Linux基础
一、基础知识 1.1 服务器 super computer 或 server 1.2 组学数据分析 组学数据:如基因组学、转录组学、蛋白质组学等; 上游分析:主要涉及原始数据的获取和初步处理,计算量大,消耗的资源较多,在服务器完…...
AI开发-数据可视化库-Seaborn
1 需求 概述 Seaborn 是一个基于 Python 的数据可视化库,它建立在 Matplotlib 之上。其主要目的是使数据可视化更加美观、方便和高效。它提供了高层次的接口和各种美观的默认主题,能够帮助用户快速创建出具有吸引力的统计图表,用于数据分析和…...
如何把Qt exe文件发送给其他人使用
如何把Qt exe文件发送给其他人使用 1、先把 Debug改成Release2、重新构建项目3、运行项目4、找到release文件夹5、新建文件夹,存放exe文件6、打开qt控制台串口7、下载各种文件8、压缩,发送压缩包给别人 1、先把 Debug改成Release 2、重新构建项目 3、运行…...
力扣103.二叉树的锯齿形层序遍历
题目描述 题目链接103. 二叉树的锯齿形层序遍历 给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。 示例 1ÿ…...
MOH: MULTI-HEAD ATTENTION AS MIXTURE-OFHEAD ATTENTION
当前的问题 多头注意力使用多个头部可以提高模型的精度。然而,并不是所有的注意力头都具有同样的重要性。一些研究表明,许多注意力头可以被修剪而不影响准确性。 此外,在多头注意中,每个注意头并行操作,最终输出是所…...
Linux的文件系统
这里写目录标题 一.文件系统的基本组成索引节点目录项文件数据的存储扇区三个存储区域 二.虚拟文件系统文件系统分类进程文件表读写过程 三.文件的存储连续空间存放方式缺点 非连续空间存放方式链表方式隐式链表缺点显示链接 索引数据库缺陷索引的方式优点:多级索引…...
力扣78题详解:C语言实现子集问题
力扣78题详解:C语言实现子集问题 题目描述 给定一个不含重复元素的整数数组 nums,返回其所有可能的子集(幂集)。 说明:解集不能包含重复的子集,顺序无关。 示例 输入:nums [1,2,3] 输出&am…...
按行数据拆分到工作表-Excel易用宝
有这样一份工作表,现在要对工作表按指定行数进行拆分,如果你还在选择数据区域复制粘贴到每个工作表中,那这样的效率也太低了。 按指定行数拆分工作表,就用易用宝。 单击Excel易用宝,合并与拆分,拆分工作表…...
.net core 创建linux服务,并实现服务的自我更新
目录 创建服务创建另一个服务,用于执行更新操作给你的用户配置一些systemctl命令权限 创建服务 /etc/systemd/system下新建服务配置文件:yourapp.service,内容如下: [Unit] Descriptionyourapp Afternetwork.target[Service] Ty…...
无人机的起降装置:探索起飞和降落的秘密 !
一、起降系统的运行方式 起飞方式 垂直起飞:小型无人机通常采用垂直起飞方式,利用螺旋桨产生的升力直接从地面升起。这种方式适用于空间有限或需要快速起飞的场景。 跑道起飞:大型无人机或需要较长起飞距离的无人机,可能会采用…...
Apache Airflow 快速入门教程
Apache Airflow已经成为Python生态系统中管道编排的事实上的库。与类似的解决方案相反,由于它的简单性和可扩展性,它已经获得了普及。在本文中,我将尝试概述它的主要概念,并让您清楚地了解何时以及如何使用它。 Airflow应用场景 …...
数学题转excel;数学题库;数学试卷转excel;大风车excel
一、数学试卷转excel 有些需要刷题的朋友,需要将题库数学题转为excel格式,便于管理 前端时间帮一位朋友实现了数学题转excel,包括选择题、填空题、分析题 示例: 二、问题 数学题是最难以处理的试题,理由如下 1、有…...
【C++】类和对象(下)
目录 前言 一、再探构造函数 二、类型转换 三、static 成员 四、友元 五、内部类 六、匿名对象 七、对象拷贝时的编译器优化 总结 前言 本文主要内容:构造函数的再探--初始化列表、内置类型与自定义类型之间的转换、类的static成员、友元、内部类、匿名对…...
vue多页面应用集成时权限处理问题
在多页面应用(MPA)中,权限管理通常会涉及到每个页面的访问控制、身份验证、以及权限校验。以下是几种常见的权限处理方式: 1. 前端路由权限控制 原理:虽然是多页面应用,通常每个页面会独立加载和渲染&…...
输出保留3位小数的浮点数
输出保留3位小数的浮点数 C语言代码C代码Java代码Python代码 💐The Begin💐点点关注,收藏不迷路💐 读入一个单精度浮点数,保留3位小数输出这个浮点数。 输入 只有一行,一个单精度浮点数。 输出 也只有一…...
openssl的运用
一、概述 Opssl是一个用于TLS/SSL协议的工具包,也是一个通用密码库。 包含了国密sm2 sm3 sm4,包含了对称加密,非对称加密,单项散列,伪随机、签名,密码交换,证书等一些算法库。 为了深层次的学习…...
C++STL之vector(超详细)
CSTL之vector 1.vector基本介绍2.vector重要接口2.1.构造函数2.2.迭代器2.3.空间2.3.1.resize2.3.2.capacity 2.4.增删查找 3.迭代器失效4.迭代器分类 🌟🌟hello,各位读者大大们你们好呀🌟🌟 🚀Ὠ…...
RabbitMQ消息可靠性保证机制5--消息幂等性处理
RabbitMQ层面有实现“去重机制”来保证“恰好一次”吗?答案是没并没有,而且现在主流的消息中间件都没有实现。 一般解决重复消息的办法是:在消费端让我们消费消息操作具有幂等性。 幂等性问题并不是消息系统独有,而是࿰…...