> 文档中心 > 学会了链表还不赶紧来刷题《二》

学会了链表还不赶紧来刷题《二》


回文链表题目分析:

代码实现:

相交链表的题目分析:

代码实现:

思路一:

思路2:(浪漫又难理解)

环形链表的题目分析:

代码实现:

环形链表 || 的题目解析:

代码实现:

思路1:(示意图如下)

思路2:(示意图如下)

复制带随机指针的链表题目分析:

代码实现:


昨天我们看的是链表比较基础的题,今天我们来看下比较新颖的题。

234. 回文链表

回文链表题目分析:

这道题我们看起来比较简单,就是判断是不是回文,我们用的答题思路是:

先找到链表的中间节点

然后从中间节点向后的链表逆置,

最后两个,一个头指针,一个中间节点的指针,向后遍历,为空停止。

示意图如下:

代码实现:

首先我们找中间节点,然后逆置,这都已经学过了。

struct ListNode* reverseList(struct ListNode* head){    struct ListNode* Newnode = NULL;    struct ListNode* cur = head;    while(cur)    { struct ListNode *tmp = cur->next; cur->next = Newnode; Newnode = cur; cur = tmp;    }    return Newnode;}struct ListNode* middleNode(struct ListNode*head){    struct ListNode* fast = NULL;    struct ListNode* slow = NULL;    fast = slow = head;    while(fast->next && fast->next->next)    { slow = slow->next; fast = fast->next->next;    }    return slow;}bool isPalindrome(struct ListNode* head){    struct ListNode* mid = middleNode(head);    struct ListNode* rhead = reverseList(mid);    while(rhead && head)    { if(rhead->val != head->val) {     return false; } else {    rhead = rhead->next;     head = head->next; }    }    return true;}

1.首先找中间节点。

2.逆置中间节点到最后的结点之间的链表。

3.两个指针一个指向头指针,一个指向中间节点的指针,进行遍历,任何一个结点为空停止。

注意:结点数奇数和偶数都是适用的,我们可以通过上述示意图看出,我们还是注意循环的结束条件,我们想的是结束条件,但是写在循环的限定条件处的是继续条件

160. 相交链表

相交链表的题目分析:

 我们看到这个题目样式比较简单,就是两个链表求交点,并且为无环链表,思路如下:

思路一:我们可以算出链表长度,求差值,然后让长的链表先走,然后在一起走,如果相等就返回。

思路二(浪漫又抽象):定义两个指针,一个先走长链表,再走短链表,一个先走短链表,再走长链表,最后会在交点处相遇。

(你走过我走的路,我们最终会相遇,愿天下有情人都返回 true )

思路三:我们可以把其中一个链表链接起来,变成判断链表带环问题(自己实现哦)

代码实现:

思路一:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB){    int lenA = 1, lenB = 1;    struct ListNode* curA = headA, *curB = headB;    //计算链表长度    while(curA)    { ++lenA; curA = curA->next;    } while(curB)     { ++lenB; curB = curB->next;    } int gap = abs(lenA-lenB);    struct ListNode* longList = headA, *shortList = headB;    if(lenA next;    }    //两个链表同时走,直到遇到相同的节点    while(longList && shortList)    { if(longList == shortList) {     return longList; } else  {     longList = longList->next;     shortList = shortList->next; }    } return NULL;}

1.首先我们先遍历一遍链表,得到两个链表的长度。

2.算出两个链表的差值。

3.使用假设法,求出链表中较长的,然后让长链表走差值步。

4.让两个链表同时走,节点值相等就返回节点,不相等返回NULL,说明没交点

注意:思路一虽然思想简单,但是实现的过程中很多细节需要注意,比如假设法求较长的链表,比如循环结束条件。

思路2:(浪漫又难理解)

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB){    struct ListNode *pA = headA;    struct ListNode *pB = headB;    if(headA == NULL  || headB == NULL)    { return NULL;    }    while(pA != pB)    { pA = pA == NULL ? headB : pA->next; pB = pB == NULL ? headA : pB->next;    } return pA;}

我们看上述图示来理解,就会方面很多了。

 

注意:我们先要判断这两个链表都要不为空,并且在一个链表走到NULL时,要直接走下一个链表。 

141. 环形链表

环形链表的题目分析:

我们依然可以使用快慢指针追赶的形式来进行解答,快指针一次走2步,慢指针一次走一步,然后快指针先入环,然后慢指针在入环,然后让快慢指针再环内追赶,最后相遇。 

代码实现:

bool hasCycle(struct ListNode *head) {    struct ListNode* fast = head;    struct ListNode* slow = head;    while(fast && fast->next)    { fast = fast->next->next; slow = slow->next; if(fast == slow) {     return true; }    }    return false;}

代码上没什么难点,我们要注意循环的限定条件是快指针为空或者快指针的next为空。

为什么要走两步呢?

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚 进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。 此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情 况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。

必须快指针走2步才可以,走3步可以吗?

142. 环形链表 II

环形链表 || 的题目解析:

这个题目实际上要我们返回链表的入环点,如果没环就返回NULL。

 

这个题的整体思路是先用快慢指针判断是否链表有环,然后顺带求出快慢指针在环的相遇点,接着我们就分为两种思路:

思路一:

我们把相遇节点保存,在保存它的下一个节点,然后从相遇节点断开,问题转化为求两个链表的相交,求交点。

思路二:我们把相遇点保存,然后遍历,让头结点,和相遇点同时向前走,最后会在入环点相遇。

代码实现:

思路1:(示意图如下)

struct ListNode *detectCycle(struct ListNode *head) {    struct ListNode* fast = head; struct ListNode* slow = head;    while(fast && fast->next)     { slow = slow->next; fast = fast->next->next; if(fast == slow) {     struct ListNode* phead = fast->next;     struct ListNode* p1 = phead;     struct ListNode* p2 = head;     fast->next = NULL;     while(p1 != p2)     {  p1 = p1 == NULL? head: p1->next;  p2 = p2 == NULL? phead: p2->next;     }     return p1; }    }return NULL;}

1.先求相遇点。

2.保存相遇点的next

3.求两个链表相交。

思路2:(示意图如下)

 

 我们根据上图的逻辑,以及结论是可以写出思路二的代码的。

struct ListNode *detectCycle(struct ListNode *head) {    struct ListNode* fast = head;    struct ListNode* slow = head;    while(fast && fast->next)    { fast = fast->next->next; slow = slow->next; if(fast == slow) {     struct ListNode* meet = fast;   while(head != meet)     {  meet = meet->next;  head = head->next;     }     return meet; }    }     return NULL;    }

138. 复制带随机指针的链表

复制带随机指针的链表题目分析:

这道题也叫做链表的深拷贝,就是有一个链表,他有两个指针,一个指向随机值,一个指向下一个,要求我们拷贝一份这样的链表。 

我们的答题思路是,先在创建每个节点,链接到原节点的后面,然后它的随机指针就是源节点的随机指针的next,然后把拷贝的节点尾插就好啦。

代码实现:

 

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 = head;    //链接随机指针 copy->random = cur->random->next    while(cur)    { struct Node* copy = cur->next; if(cur->random == NULL) {     copy->random = NULL; } else {     copy->random = cur->random->next; } cur = copy->next;    }    cur = head;    //尾插    struct Node* copyhead = NULL;    struct Node* copytail = NULL;    while(cur)    { struct Node* copy = cur->next; struct Node* next = copy->next; if(copytail == NULL) {     copyhead = copytail = copy; } else {     copytail->next = copy;     copytail = copytail->next; } cur->next = next; cur = next;    }    return copyhead;}

1.先创建节点,链接到原链表每个节点的后面。

2.然后就是 copy->random = cur->random->next 的理解到位,如果原节点的 random 指为NULL,则 copy->random 也指为 NULL。

3.把 copy 的节点拿下来尾插一个新链表,返回就可以啦。

我们最重要的是明白 copy->random = cur->random->next,这个的由来。