24两两交换链表中的节点
题目链接/文章讲解/视频讲解: 代码随想录
1.代码展现
//24 两两交换链表中的节点
ListNode* swapPairs(ListNode* head) {
//创建虚拟头节点
ListNode* dummyHead = new ListNode();
dummyHead->next = head;
ListNode* cur = dummyHead;
//注意顺序
while (cur->next != nullptr && cur->next->next != nullptr) {
//交换前储存丢失节点,这样就不会丢失了
ListNode* temp = cur->next;
ListNode* temp1 = cur->next->next->next;
//开始交换,分三步走
cur->next = cur->next->next;
//注意:不是cur->next->next->next,这个时候已经更新了
cur->next->next = temp;
temp->next = temp1;
//交换完毕,更新cur
cur = cur->next->next;
}
return dummyHead->next;
}
2.本题小节
设计到处理头节点的算法,用虚拟头节点会简单很多,因此首先要创建虚拟头节点;明确交换步骤,共有三步,画图分析,同时注意交换时要保存临时节点,否则会出现找不到需要指向的节点;明确每次遍历的节点cur的位置,为将要处理的节点对的前一个,因此每次循环为cur=cur->next->next,初始为虚拟头节点;明确遍历结束的条件,当节点个数为偶数时,cur->next = nullptr时结束遍历,当节点个数为奇数时,cur->next->next = nullptr时结束遍历,返回值为虚拟头节点后的节点。
19.删除链表的倒数第N个节点
题目链接/文章讲解/视频讲解:代码随想录
1.代码展现
//19 删除链表倒数第n个节点
ListNode* removeNthFromEnd(ListNode* head, int n) {
//构建虚拟头节点
ListNode* dummyHead = new ListNode();
dummyHead->next = head;
//创建快慢指针
ListNode* nodeSlow = dummyHead;
ListNode* nodeFast = dummyHead;
//for (int i = 0; i <= n; i++) {
// nodeFast = nodeFast->next;
//}
//?注意nodeFast不能为nullptr
while (n-- && nodeFast != nullptr) {
nodeFast = nodeFast->next;
}
nodeFast = nodeFast->next;
//开始移动快慢指针,直到快指针指向为空
//注意不是nodeFast->next
while (nodeFast != nullptr) {
nodeSlow = nodeSlow->next;
nodeFast = nodeFast->next;
}
//当快指针指向为空时,满指针此时的位置为要移除元素的前一位
//修改指针指向,并释放内存
ListNode* temp = nodeSlow->next;
nodeSlow->next = nodeSlow->next->next;
delete temp;
temp = nullptr;
//返回头节点
return dummyHead->next;
}
2.本题小节
本题使用的是双指针的解法,使用双指针的目的是寻找到倒数第n个节点的位置。因为会处理到头节点,因此还是需要构建虚拟头指针;构建快慢指针,初始值都为虚拟头指针,然后将快指针往后移动n+1次,这样是为了保证快指针指向nullptr时,慢指针指向寻找的节点的前一处,保证删除能正常进行。明白了这些,这题就很好做了。
160.链表相交
题目链接/文章讲解:代码随想录
1.代码展现
//160 链表相交
ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
//step1:统计两个链表的长度,并计算长度差
int nSizeA = 0;
int nSizeB = 0;
ListNode* curA = headA;
ListNode* curB = headB;
//注意:这里不可以用head遍历
while (curA != nullptr) {
nSizeA++;
curA = curA->next;
}
while (curB != nullptr) {
nSizeB++;
curB = curB->next;
}
cout << "sizeA:" << nSizeA << endl;
cout << "sizeB:" << nSizeB << endl;
//计算长度差
//重新指向头部
curA = headA;
curB = headB;
//这里swap的应用避免了分情况讨论
if (nSizeB > nSizeA) {
swap(headA, headB);
swap(curA, curB);
}
int nLength = abs(nSizeA - nSizeB);
//step2: 将A链表的curA往左移动nLength位
while (nLength--) {
curA = curA->next;
}
//step3: 从curA和curB处开始遍历,检查curA是否等于curB,相同则返回curA或curB,为相交的起始节点
while (curA != nullptr) {
//注意,这里是ListNode相同,而不是val相同
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
//没遍历到,则返回NULL
return NULL;
}
2.本题小节
求两个链表长度之差n;将长的链表的cur往后移动n次,此时两个链表遍历次数相同;同时对两个链表进行遍历获取,如果存在curA=curB,那么该节点则为相交的初始节点。
142.环形链表II
题目链接/文章讲解/视频讲解:代码随想录
1.代码展现
//142 环形列表
ListNode* detectCycle(ListNode* head) {
//本题用快慢指针来求解
ListNode* fastNode = head;
ListNode* slowNode = head;
//step1: 判断是否有环,这里的fastNode->next != nullptr是为了保证
//fastNode->next->next有效,否则会报错
while (fastNode != nullptr && fastNode->next != nullptr) {
//step2: 移动指针,慢指针每次移动一个节点,快指针次移动两个节点
fastNode = fastNode->next->next;
slowNode = slowNode->next;
if (fastNode == slowNode) {
//此时相遇了
//step3:记下这个相遇点,由数学公式2(x+y)= x+y+n(y+z)
// 推导出x = (n - 1)(z+y) + z
//其中x为头节点到入环节点的距离,n表示fast节点转圈圈数,y表示入环节点到相遇点的距离
// z表示相遇点到入环节点的距离,
// 可知
//从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点,
//那么当这两个指针相遇的时候就是 环形入口的节点。
ListNode* curA = head;
ListNode* curB = fastNode;
while (curA != curB) {
curA = curA->next;
curB = curB->next;
}
//此时找到环形入口节点
return curA;
}
}
//没有环
return NULL;
}
2.本题小节
本题的重点在于两处,一处是如何判断有环,通过快慢指针判断,快指针追上慢指针后就可以判断有环;一处是如何计算环入口的位置,这里设计到数学推导,将数学公式推导一遍后,可以知道,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
链表总结
总结:代码随想录