大家好,欢迎来到无限大的频道,有些日子没更新了(其实是因为懒)
言归正传,开始分享今日的题解。
题目描述:
合并零之间的节点
给一个链表的头节点 head ,该链表包含由 0 分隔开的一连串整数。链表的 开端 和 末尾 的节点都满足 Node.val == 0 。
对于每两个相邻的 0 ,请你将它们之间的所有节点合并成一个节点,其值是所有已合并节点的值之和。然后将所有 0 移除,修改后的链表不应该含有任何 0 。
返回修改后链表的头节点 head 。
解题思路:
设定一个虚拟节点,通过使用虚拟头节点便于开始新链表的构建,避免了复杂的链表操作。
要解决这个问题,我们需要遍历给定的链表,并对链表中相邻的 0 之间的节点进行合并。
1.初始化:
-
创建一个虚拟头节点 dummy,用于构建合并后的新链表。
-
使用一个指针 tail 指向新链表的当前尾部。
-
使用一个 sum 变量来累加 0 之间的节点值。
2.遍历链表:
- 从 head->next 开始遍历链表(跳过开头的 0)。
-
对于每个节点:
-
如果节点的值为 0:
-
检查 sum 是否大于 0。如果是,说明在这两个 0 之间有值需要合并:
-
-
创建一个新的节点,将 sum 赋值给这个新节点。
-
将新节点链接到 tail 的 next。
-
更新 tail 指向新节点。
-
重置 sum 为 0。
-
-
-
-
如果节点的值不为 0,将其值累加到 sum。
-
-
3.结束遍历:
- 当遍历完成后,dummy->next 就是合并后的新链表的头节点。
- 释放 dummy 节点的内存
4.返回结果:
- 返回新链表的头节点。
参考代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
题目中已经假设有了一个ListNode的结构体,实际上我更喜欢在定义的时候去使用
typedef关键字,如下:
typedef struct ListNode {
int val;
struct ListNode *next;
} ListNode;
还是看大家的个人习惯就好
*/
struct ListNode* mergeNodes(struct ListNode* head){
struct ListNode* current = head->next;
struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* tail = dummy;
int sum = 0;
while(current != NULL){
if(current->val == 0){
if(sum > 0){
tail->next = (struct ListNode*)malloc(sizeof(struct ListNode));
tail->next->val = sum;
tail->next->next = NULL;
tail = tail->next;
sum = 0;
}
}
else{
sum += current->val;
}
current = current->next;
}
struct ListNode* newhead = dummy->next;
free(dummy);
return newhead;
}
时间复杂度和空间复杂度
时间复杂度:O(n)
代码中有一个 `while` 循环,该循环遍历链表中的每个节点。`current` 从 `head->next` 开始,直到 `NULL` 结束,意味着每个节点都被访问一次。
在循环内部,处理每个节点的操作(如累加、条件判断和分配新节点)都是常数时间操作(O(1))。
因此,遍历整个链表的时间复杂度为 O(n),其中 n 是链表中节点的数量。
空间复杂度:O(n)
在代码中,使用了一个虚拟头节点 `dummy`,这占用 O(1) 的空间。
但是在合并节点的过程中,每当遇到 `0` 并且 `sum > 0` 时,会动态地分配一个新节点并将其添加到结果链表中。最坏情况下,如果链表中的所有节点都是非零值,那么新链表将包含 n/2 个节点(因为每两个 `0` 之间的节点会合并成一个节点)。
因此,最终新链表的节点数与原链表的节点数成正比,导致空间复杂度为 O(n)。
另外,虽然使用了一个常数空间的变量 `sum`,但这不会影响总体空间复杂度的计算。
总结
时间复杂度:O(n) - 因为每个节点都被访问一次。
空间复杂度:O(n) - 因为可能需要为合并后的链表分配新的节点,最坏情况下节点数与原链表相当。