21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
AC
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
if(list1 == NULL)
{
return list2;
}
if(list2 == NULL)
{
return list1;
}
struct ListNode* head = NULL;
struct ListNode* tail = NULL;
while(list1 && list2)
{
if(list1->val < list2->val)
{
if(tail == NULL)
{
head = tail = list1;
}
else{
tail->next = list1;
tail = tail->next;
}
list1 = list1->next;
}
else{
if(tail == NULL)
{
head = tail = list2;
}
else{
tail->next = list2;
tail = tail->next;
}
list2 = list2->next;
}
}
if(list1)
{
tail->next = list1;
}
if(list2)
{
tail->next = list2;
}
return head;
}
代码思路
最简单的方法就是比较两个链表中的值,将小的尾插到新链表中。
创建一个新链表,一个指针head指向头结点,一个指针tail进行尾插操作,然后开始进行比较,结束条件是其中一方的值为空,如果tail还是NULL,说明新链表还没有值,就将head和tail都指向小的那一个,如果tail不为空了,那么继续尾插,让他的next去存这个小的的地址,然后让tail前移。小的值所在的list也前移。当其中一个已经结束后,还不为空的那一个链表后面的所有节点都尾插到新链表即可,最后返回头结点地址head。
不建议使用将一个链表的值直接插入另一个链表的方法,写起来非常复杂。
138. 随机链表的复制(中等)
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
返回复制链表的头节点。
用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
- val:一个表示 Node.val 的整数。
- random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。
AC
struct Node* copyRandomList(struct Node* head) {
struct Node* cur = head;
while(cur)
{
struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
copy->val = cur->val;
copy->next = cur->next;
cur->next = copy;
//cur = copy->next; //两者相等
cur = cur->next->next;
}
cur = head;
while(cur)
{
struct Node* copy = cur->next;
if(cur->random == NULL) // 特殊情况处理
{
copy->random = NULL;
}
else
{
copy->random = cur->random->next; // 关键
}
cur = cur->next->next;
}
cur = head;
struct Node* newhead = NULL,*tail = NULL;
while(cur)
{
struct Node* copy = cur->next;
struct Node* next = copy->next;
if(tail == NULL)
{
newhead = tail = copy;
}
else{
tail->next = copy;
tail = tail->next;
}
cur->next = next;
cur = next;
}
return newhead;
}
代码思路
这道题思路比较难想,且实现起来也较为复杂。
先解读题目,要做一个深拷贝,完成链表的复制并不复杂,但是此链表中不止存着next
节点,还存着random
随机节点,也就是说,每一个节点还存在一个random
数值,存放着某一个节点的地址,这个地址是随机的,此处的随机可以是整条链表的任一个节点,可以是空指针,可以是下一个或上一个节点,甚至可以是自己,如图:
这道题的方法很难想到,这里直接讲解,在每一个节点后都创建一个copy
节点,复制改节点,所有的copy
节点就是新链表的组成部分,如图:
这样做的理由是什么呢,请看下一张图,这张图展现了random的关系:
这里看copy13,以他为例,copy13
的random
按照原链表的random
来讲应该指向节点值为7的节点,也就是图中的蓝色线条,指向copy7,那这样如何找到呢。使用cur
记录原链表的13
,使用copy
记录新链表的13
,那么copy13
的random
,通过cur13
的random
找到了cur7
,此刻cur7
和copy7
是链接起来的,那么他的next
就是copy7
,因此有等式 copy->random = cur->random->next
。
找到新链表的random后,移动cur指针和copy指针就可以将整条由原链表和新链表组成的链表的节点都实现复制,接着就将原链表和新链表拆分最后返回新链表的地址就可以了,具体的代码讲解在此不做赘述,只写关键思路,可根据上文中的代码自己分析。