1. 分割链表(OJ链接)
题目描述:给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有小于 x 的节点都出现在大于或等于 x 的节点之前。
本题做法是:遍历链表将链表分为两部分,一部分的结点值小于特定值,另一部分的结点值大于或等于特定值,最后将两个链表链接起来。为了方便,下面将两部分链表称为大小链表。(值大于等于或小于特定值的链表)
为了便于将结点链接到大小链表中,采用带头结点的方式解决,可以避免大小链表为空或者不为空的讨论。
当遍历完链表后,可能出现链表成环的情况,因此需要将大链表的尾指针置空。
题解代码如下
typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x)
{
if(head==NULL)
{
return NULL;
}
//创建两个带头链表
ListNode* lesshead,*lesstail,*morehead,*moretail,*cur;
lesshead=lesstail=(ListNode*)malloc(sizeof(ListNode));
morehead=moretail=(ListNode*)malloc(sizeof(ListNode));
cur=head;
//遍历原链表
while(cur)
{
if(cur->val<x)
{
lesstail->next=cur;
lesstail=lesstail->next;
}
else
{
moretail->next=cur;
moretail=moretail->next;
}
cur=cur->next;
}
//防止成环
moretail->next=NULL;
//首尾相连
lesstail->next=morehead->next;
ListNode* ret=lesshead->next;
free(lesshead);
free(morehead);
return ret;
}
lesshead,lesstail,morehead,moretail分别是小链表和大链表的头尾指针。最初都指向头结点。
2. 旋转链表(OJ链接)
题目描述:给你一个链表的头节点head ,旋转链表,将链表每个节点向右移动 k 个位置。
示例:
结果可以看作将后2个结点原封不动的挪到前面。因此需要找到倒数第k个结点,方法就是快慢指针法。
本题需要注意k的值,k可能很大,比如远大于结点的个数,当k等于结点的个数时,所有结点向右移动k个位置相当于不用移动,因此需要对k进行求余操作。
此题做法为:快慢指针法。首先先计算链表结点的数目,对k进行求余。fast指针先走k步,之后和慢指针以相同速度走,快指针为NULL时,慢指针指向的结点就是倒数第k个结点。为了避免成环,需要将倒数第k+1个结点的指针域置空,最后链接两部分链表即可。
一些需要注意的小的点就不多赘述了,比如链表为空或者只有一个结点,那么直接返回NULL或head即可。
题解代码如下:
struct ListNode* rotateRight(struct ListNode* head, int k)
{
//结点数为0
if(head==NULL)
return NULL;
//结点数为1
if(head->next==NULL)
return head;
struct ListNode* cur=head,*fast=head,*prev=head,*tail=head;
int num=0;
//计算链表结点个数
while(cur)
{
num++;
cur=cur->next;
}
k%=num;
if(k==0)
return head;
//fast先走k步
cur=head;
while(k--)
{
fast=fast->next;
}
while(fast)
{
prev=cur;
cur=cur->next;
tail=fast;
fast=fast->next;
}
//解环并链接首尾
prev->next=NULL;
tail->next=head;
return cur;
}
指针含义:cur——指向新链表的头,prev——cur的前一个结点,tail——指向最后一个结点
对上述示例进行画图帮助理解代码,过程如下图
本题到此就结束咯
3. 环形链表(OJ链接)
题目描述:给你一个链表的头节点 head ,判断链表中是否有环。
此题解法:快慢指针法,快指针一次走两步,慢指针一次走一步。两个指针指向同一个结点时,说明链表有环,快指针为NULL时说明链表没有环。
代码如下
bool hasCycle(struct ListNode *head) {
struct ListNode* fast=head,*slow=head;
while(fast!=NULL&&fast->next!=NULL)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
return true;
}
}
return false;
}
本题代码比较简单,但是这个思路是否正确需要验证,本质是一道数学题,验证过程如下图。
4. 环形链表2(OJ链接)
题目描述:给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
这个题可以看成上一题的后续,有环则找入环点。
这里先证明一个式子再写代码:有两个指针,一个指针从链表的头走,一个指针从相遇点走,并且同速走,那么当两个指针相遇时,相遇的那个结点就是入环点。
证明过程如下图
那么有了上述的结论再加上上一题的思路,本题就比较简单了。
struct ListNode* detectCycle(struct ListNode* head)
{
struct ListNode *fast = head, *slow = head;
while (fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if (fast == slow)
{
struct ListNode* meet = slow;
while (meet != head)
{
meet = meet->next;
head = head->next;
}
return meet;
}
}
return NULL;
}
链表部分的题目就到此为止了,后续就对栈队列等数据结构进行练习。