一、合并两个有序链表
(Leetcode21)
1.1 题目


1.2 解题思路
创建新的空链表,用两个指针变量l1、l2分别遍历原链表,将节点值小的节点拿到新链表中进行尾插操作。 一个指针变量走到空之后,把另一个链表剩下的元素也全都尾插到新链表中。在开始遍历之前要判空。
需要注意的是,如果给出的两个链表中有空链表,那么无法进入遍历的循环。因此在遍历之前要判空,如果一个链表为空,就返回另一个链表。
1.3 代码实现
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
//判空
if(list1 == NULL)
{
return list2;
}
if(list2 == NULL)
{
return list1;
}
ListNode* l1 = list1;
ListNode* l2 = list2;
ListNode* newHead, *newTail;
newHead = newTail = NULL;
while(l1 && l2)
{
if(l1->val < l2->val)
{
//把l1拿下来尾插
if(newHead == NULL)
{
newHead = newTail = l1;
}
else
{
newTail->next = l1;
newTail = newTail->next;
}
l1 = l1->next;
}
else
{
//把l2拿下来尾插
if(newHead == NULL)
{
newHead = newTail = l2;
}
else
{
newTail->next = l2;
newTail = newTail->next;
}
l2 = l2->next;
}
}
//跳出循环,要么是l1走到空了,要么是l2走到空了
if(l2)
{
newTail->next = l2;
}
if(l1)
{
newTail->next = l1;
}
return newHead;
}
我们发现判断新链表的头结点是否为空的代码写了两遍,有重复。我们可以通过以下方法进行优化:
我们在创建新链表的头节点的时候可以不让它们为空,而是向系统申请一块空间。这样我们不需要判断新链表是否为空,拿过来直接尾插即可。
不过这样的话,最后返回的就不是newHead,而 是newHead的下一个节点。
记得代码写完后要手动释放申请的空间。不过,释放空间后就找不到newHead的下一个节点,因此要先创建一个变量把newHead的下一个节点记录下来。
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
//判空
if(list1 == NULL)
{
return list2;
}
if(list2 == NULL)
{
return list1;
}
ListNode* l1 = list1;
ListNode* l2 = list2;
ListNode* newHead, *newTail;
newHead = newTail = (ListNode*)malloc(sizeof(ListNode));
while(l1 && l2)
{
if(l1->val < l2->val)
{
//把l1拿下来尾插
newTail->next = l1;
newTail = newTail->next;
l1 = l1->next;
}
else
{
//把l2拿下来尾插
newTail->next = l2;
newTail = newTail->next;
l2 = l2->next;
}
}
//跳出循环,要么是l1走到空了,要么是l2走到空了
if(l2)
{
newTail->next = l2;
}
if(l1)
{
newTail->next = l1;
}
ListNode* ret = newHead->next;
free(newHead);
return ret;
}
这个优化代码中新加的一个没有用的节点,是带头链表中的"哨兵位"或叫"头节点"(单链表中的第一个节点称为"头节点",其实是不标准的)。
二、 环形链表的约瑟夫问题
1.1 题目

1.2 解题思路
在一个环形链表中,定义两个指针变量顺着链表走,每走到m就销毁掉这个节点。到最后,链表会只剩下一个节点,指向它自己。
销毁节点的时候,要先让被销毁节点的前一个节点的next指针指向被销毁节点的下一个节点,否则就找不到被销毁节点的下一个节点了。
1.3 代码实现
假设节点的结构体已经定义好了。
cpp
typedef struct ListNode ListNode;
//创建节点
ListNode* buyNode(int x)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
if (node == NULL)
{
exit(1);
}
node->val = x;
node->next = NULL;
return node;
}
//创建带环链表
ListNode* createCircle(int n)
{
ListNode* phead = buyNode(1);
ListNode* ptail = phead;
for(int i = 2; i<=n; i++)
{
ptail->next = buyNode(i);
ptail = ptail->next;
}
//首尾相连,链表成环
ptail->next = phead;
return ptail;
}
int ysf(int n, int m )
{
ListNode* prev = createCircle(n);
ListNode* pcur = prev->next;
int count = 1;
while(pcur->next != pcur)
{
if(count == m)
{
//销毁pcur节点
prev->next = pcur->next;
free(pcur);
pcur = prev->next;
count = 1;
}
else
{
prev = pcur;
pcur = pcur->next;
count++;
}
}
//此时剩下的一个节点就是要返回的值所在的节点
int ret = pcur->val;
free(pcur);
pcur = NULL;
return ret;
}
三、分割链表
(Leetcode面试题02.04)
3.1 题目

3.2 解题思路
思路一:在原链表上修改。
用一个指针变量pcur遍历原链表,遇到大于等于x的节点就尾插到原链表后面并删除旧节点。
思路二:创建一个新链表。
用指针变量pcur遍历原链表,如果pcur节点的值小于x,头插在新链表中;如果pcur的值大于或等于x,尾插在新链表中。
思路三:创建两个新链表。
创建一个大链表和一个小链表。遍历原链表,小于x的节点尾插到小链表里,大于等于x的节点尾插到大链表里。最后把小链表的尾结点和大链表的第一个节点首尾相连。这样就不用写头插的代码。
3.3 代码实现
由于思路三最简单,所以按照思路三实现。
创建新链表的过程中,可以用带头链表(有哨兵位的链表),来避免写判断新链表是否为空的代码。
要注意,给出的原链表为空的情况需要单独处理。
此外,我们还要考虑大链表中没有元素的问题。此时greaterTail的next指针指向的是一个随机地址,不能把这个值赋给其他变量。因此我们需要对这个变量初始化一下,将其置为空。如果不加这个语句,还会出现另一个问题,因为最后没有规定大链表尾结点的next指针指向哪里,它仍会指向原链表中排在它后面的那个节点,造成死循环。
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x)
{
if (head == NULL)
{
return head;
}
//创建两个带头链表
ListNode* lessHead, *lessTail;
ListNode* greaterHead, *greaterTail;
lessHead = lessTail = (ListNode*)malloc(sizeof(ListNode));
greaterHead = greaterTail = (ListNode*)malloc(sizeof(ListNode));
if(lessHead ==NULL || lessTail == NULL || greaterHead == NULL || greaterTail == NULL)
{
perror("malloc");
}
//遍历原链表,将原链表中的节点尾插到大小链表中
ListNode* pcur = head;
while(pcur)
{
if(pcur->val < x)
{
//尾插到小链表中
lessTail->next = pcur;
lessTail = lessTail->next;
}
else
{
//尾插到大链表中
greaterTail->next = pcur;
greaterTail = greaterTail->next;
}
pcur = pcur->next;
}
//修改大链表的尾结点的next指针指向+greatertail的next指针初始化
greaterTail->next = NULL;
//小链表的尾结点和大链表的第一个有效节点首尾相连
lessTail->next = greaterHead->next;
ListNode* ret = lessHead->next;
free(lessHead);
free(greaterHead);
lessHead = greaterHead = NULL;
return ret;
}