文章目录
一、LeetCode-160. 相交链表
题目
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
struct ListNode *p, *q;
int cnt = 0, m = 0, n = 0;
p = headA;
q = headB;
//记录A和B的节点数
while(p) {
p = p->next;
m++;
}
while(q) {
q = q->next;
n++;
}
p = headA;
q = headB;
//移动到从后往数数目相同的位置
if(m > n) {
for(int i=0; i<m-n; i++) {
p = p->next;
}
}
else {
for(int i=0; i<n-m; i++) {
q = q->next;
}
}
while(p != q) {
p = p->next;
q = q->next;
if(p == q) { return p; }
if(p == NULL || q == NULL) { return NULL; }
}
return p;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
struct ListNode *p, *q;
p = headA;
q = headB;
while(p != q) {
p = p == NULL ? headB : p->next;
q = q == NULL ? headA : q->next;
}
return p;
}
};
思路一
首先看初始的图:
为了更方便找到相交位置,我们可以使用双指针,同时移动两个指针headA和headB,若两指针相等,则说明链表相交
但出现了两个问题
- 题目要求不能改变原链表
- headA和headB到达相交点的时间可能不同
第一个问题很好解决,只需要另外声明p和q代替headA和headB即可
第二个问题也很好解决,假设A不相交的节点有a个,B不相交的节点有b个,那么只要将a或b往前移动a-b或b-a即可
也就变成下图:
这时再同时移动p和q,若相等则返回其中一个节点,若到达NULL,则返回NULL
思路二
第二个思路很巧妙,p和q会分别从headA和headB开始走,若走到链表今头,则换成另一个链表开始走
下面为过程演示:
当p第一次到链表结尾:
此时p换到headB,并且此时q到链表结尾(不一定此时q刚好到链表结尾):
此时q换到headA,p向前继续移动:
会发现此时q和p位于同一竖直线上,又回到思路一的最后一步
分析
设A不相交的节点有a个,B不相交的节点有b个,相交的节点有c个
当p走完headA时走了 a + c
步,当q走完headB时走了b + c
步
当p走到相交处节点时走了a + c + b
步,当q走到相交处节点时走了b + c + a
步,发现走的步数一致了
说明走到节点处一定可以位于同一竖直线上
复杂度
两个思路的时间复杂度都为O(m + n),空间复杂度O(1)
二、LeetCode-19. 删除链表的倒数第 N 个结点
题目
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
struct ListNode*p, *p0, *q;
p0 = p = q = head;
for(int i=0; i<n; i++) { q = q->next; }
while(q) {
q = q->next;
p0 = p;
p = p->next;
}
p == p0 ? head = head->next : p0->next = p->next;
delete p;
return head;
}
};
思路
这道题本质就是找倒数第n个节点,方法一就是计算链表长度,扫描两边,方法二就是双指针(本题思路)
很显然,要找到倒数第n个节点,先找到第n个节点,例如样例1:
设p, q, p0三个指针,先初始化为head,q为快指针
找倒数第二个节点,则先让q向前移动两次,则可以得到下图
接下来同时移动p、q和p0,注意:当p不为头节点时,保证p0为p前一个节点
得到下图
此时只需要将p0指向p的下一个节点,别忘了delete掉p就解决了问题
这里需要注意一个特殊情况:删除的是头节点
这种情况下解决办法之一是判断p是否等于p0,如果等于,说明p和q指针并未移动,删除的是第n个节点,也就是头节点
这种方法不需要多增加行数,只要将结尾的p0->next = p->next;
替换成
p == p0 ? head = head->next : p0->next = p->next;
三、LeetCode-206. 反转链表
题目
代码
//递归
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL || head->next == NULL) {
return head;
}
struct ListNode* newhead = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return newhead;
}
};
//双指针
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL || head->next == NULL) {
return head;
}
struct ListNode *p0, *p, *p1;
p0 = NULL;
p = head;
p1 = head->next;
while(p) {
p->next = p0;
p0 = p;
p = p1;
if(p1) { p1 = p1->next; }
}
return p0;
}
};
思路
已经写四遍了,烂熟了,倒背如流,懒得写了。