24. 两两交换链表中的节点
解析:
基础题,主要是要把握边界条件:由题可得,交换的节点两两一组,每交换完成一对,问题规模减2,也就是只剩一个或不剩节点时交换便结束。
在这里,我们引入一个虚拟头节点,这样所有的交换操作便得到统一。
每一对节点交换一次,不开辟额外空间,因此时间复杂度O(n),空间复杂度O(1)。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummy_head = new ListNode(-1, head); //虚拟头节点
if(head == nullptr || head->next == nullptr) return head; //边界条件先判断
ListNode* cur2 = head->next;
ListNode* cur1 = head;
ListNode* pre = dummy_head; //两个需要交换的节点,及前节点,共三个节点
while(cur1 && cur2){
cur1->next = cur2->next;
cur2->next = cur1;
pre->next = cur2;
pre = cur1;
if(cur1->next && cur1->next->next){ //若只剩1个或不剩 则退出交换
cur2 = cur1->next->next;
cur1 = cur1->next;
}
else {
break;
}
}
head = dummy_head->next; //头节点还原
delete dummy_head;
return head;
}
};
19.删除链表的倒数第N个节点
解析:
本题以普通办法处理的话就是先遍历一遍,确定链表长度,再遍历一遍,实施删除。虽然不会到O(n^2)的复杂度,仍为线性,但是连续遍历两遍不免让人觉得不快。
因此此类问题,同样可以借鉴双指针法,一前一后不回溯的遍历链表,让前后指针距离保持 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) {
ListNode* dummy_head = new ListNode(-1,head);
ListNode* p = dummy_head;
ListNode* q = dummy_head;
int kick_off = 0;
while(kick_off < n && p->next){ //先让p指针先行,确定q指针何时行动
p = p->next;
kick_off++;
}
while(p->next){ //同步行动,直至p指针到最后一个节点,q指针离p n 个位置远
p = p->next;
q = q->next;
}
ListNode *tmp = q->next;
q->next = q->next->next; //实施删除
if(dummy_head->next){ //判断一下是否链表就一个节点,且已被删除
head = dummy_head->next;
}
else{
head = nullptr;
}
delete tmp; // 回收要删除的节点
delete dummy_head; //回收虚拟头
return head;
}
};
160.链表相交
解析:
这道题需要巧妙利用到一个思维,也就是链表尾对齐的思维。
如图:若两个链表有相交点,则该交点后的节点均为同一个节点,也就是它们是尾部相等的。所以如果我们能找到长表较之于短表表头的中间位置(等长的位置),然后顺序同步一对一对节点的比较是否相等,便能找到第一个相同的节点,且时间复杂度能控制在O(m+n)。
/**
* 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) {
ListNode* qq = headA;
int lenA = 1;
int lenB = 1;
ListNode* pp = headB;
while(qq->next){ //计算A的长度
qq = qq->next;
lenA++;
}
while(pp->next){ //计算B的长度
pp = pp->next;
lenB++;
}
ListNode* q = headA;
ListNode* p = headB;
if(lenA < lenB){ // 让q指向较长的链表的表头
int tmp = lenA;
ListNode* _tmp = q;
lenA = lenB;
lenB = tmp;
q = p;
p = _tmp;
}
int offset = lenA - lenB;
while(offset > 0 ){ //想象两个不等长的链表,由末尾对齐,将较长的链表的指针遍历到与较短的链表表头节点对齐
q = q->next;
offset--;
}
while(q != p && q){ //开始判等
q = q->next;
p = p->next;
}
return q;
}
};
142.环形链表II
解析:
该题涉及到一个数学推理,详情见卡哥:代码随想录
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* p = head;
ListNode* q = head;
while( q && q->next){
p = p->next;
q = q->next->next;
if(p == q){
ListNode* pp = head;
ListNode* qq = q;
while(pp != qq){
pp = pp->next;
qq = qq->next;
}
return qq;
}
}
return NULL;
}
};