主要思路:
每次调换只用关注cur, cur -> next, cur -> next -> next, cur -> next -> next -> next这四个节点,其中cur是将要反转的两个节点前一个节点,每次操作
(1)先用tmpBefore保存cur -> next节点,用tmpAfter保存cur -> next -> next -> next节点
(2)将cur -> next指向cur -> next -> next,此时现在的 cur -> next就是原来的cur -> next -> next,所以将现在的cur -> next指向tmpBefore,即原来的 cur -> next,然后将tmp -> next指向tmpAfter
(3)最后将cur向后移动两个节点
易错点:
(1)循环终止条件必须考虑节点数是偶数与奇数,所以设为cur -> next(对应节点数是偶数) && cur -> next -> next (对应节点数是奇数)
代码实现:
struct ListNode* swapPairs(struct ListNode* head){
typedef struct ListNode ListNode;
struct ListNode* dummyHead = (struct ListNode* )malloc(sizeof(struct ListNode));
dummyHead -> next = head;
struct ListNode* cur = dummyHead;
while (cur -> next && cur -> next -> next) {
struct ListNode* tmpBefore = cur -> next;
ListNode* tmpAfter = cur -> next -> next -> next;
cur -> next = tmpBefore -> next; //第一步,将cur的下一位指向cur后面第二位
tmpBefore -> next -> next = tmpBefore; //第二步,将原cur后面的第二位(现在的下一位)的下一位指向cur原来的后一位
tmpBefore -> next = tmpAfter; //第三步,将原cur的后一位(现在的第二位)的下一位指向cur后面的第三位
cur = cur -> next -> next; //用这个而不用tmpBefore -> next是因为tmpBefor已经变成cur的下一位了
}
ListNode* ret = dummyHead -> next;
free(dummyHead);
return ret;
}
主要思路:
定义一个快指针与慢指针,其中快指针先移动n + 1个节点,之所以要额外加1是因为这样能使慢指针恰好停在待删除节点前一位,然后在快指针指向NULL时就意味着慢指针指向了待删除节点的前一个节点,然后就可以执行删除操作
易错点:
(1)n++与while(n--)搭配实现了快指针先移动n+1个节点
代码实现:
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
typedef struct ListNode ListNode;
ListNode* dummyHead = (ListNode* )malloc(sizeof(ListNode));
dummyHead -> next = head;
n++;
ListNode* fast = dummyHead, * slow = dummyHead;
while(n-- && fast) {
fast = fast -> next;
}
while(fast) {
fast = fast -> next;
slow = slow -> next;
}
ListNode* tmp = slow -> next;
slow -> next = slow -> next -> next;
free(tmp);
return dummyHead -> next;
}
主要思路:
(1)分别遍历两个链表,获得两个链表长度
(2)以l表示长链表,s表示短炼表
(3)将指向长链表的头指针移到与短链表同一个位置后分别比较这两个指针
易错点:
(1)比较的是链表指针不是值
代码实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
typedef struct ListNode ListNode;
ListNode* curA = headA, * curB = headB;
int cntA = 0, cntB = 0;
ListNode* l = NULL, * s = NULL;
//分别得到两个链表长度
while(curA) {
curA = curA -> next;
cntA++;
}
while(curB) {
curB = curB -> next;
cntB++;
}
//得到两个链表长度的差值
int gap = 0;
if(cntA >= cntB) {
l = headA;
s = headB;
gap = cntA - cntB;
}
else {
l = headB;
s = headA;
gap = cntB - cntA;
}
//将较长的链表头指针移到与短链表相同位置
while(gap-- && l) {
l = l -> next;
}
//比较链表指针
while(l) {
if(l == s) {
return l;
}
else {
l = l -> next;
s = s -> next;
}
}
return NULL;
}
主要思路:
(1)定义快指针与慢指针,快指针每次移动两位,慢指针每次移动一位,如果链表有环,那么快指针必在慢指针进入环且没有在环里转满一圈的时候追上
(2)在快指针追上慢指针后,以一个indexA记录此时位置,再一个indexB表示头节点,两个指针同时出发,相遇时既是入口
易错点·:
(1)为什么快指针走两步,慢指针走一步:因为fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。
(2)假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:
那么相遇时:
slow指针走过的节点数为: x + y
,
fast指针走过的节点数:x + y + n (y + z)
,n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。
因为fast指针是一步走两个节点,slow指针一步走一个节点,
所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:
(x + y) * 2 = x + y + n (y + z)
因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。
所以要求x ,将x单独放在左面:x = (n - 1)y + z
取n = 1,得到x = z
代码实现:
struct ListNode *detectCycle(struct ListNode *head) {
typedef struct ListNode ListNode;
ListNode* fast = head, * slow = head;
while(fast && fast -> next) {
fast = fast -> next -> next;
slow = slow -> next;
if(fast == slow) {
ListNode* indexA = fast, * indexB = head;
while(indexA != indexB) {
indexA = indexA -> next;
indexB = indexB -> next;
}
return indexA;
}
}
return NULL;
}