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)
{//结点数为0if(head==NULL)return NULL;//结点数为1if(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;
}
链表部分的题目就到此为止了,后续就对栈队列等数据结构进行练习。