24.两两交换链表中的节点
题目链接:24.两两交换链表中的节点
开始做这道题的时候人是懵的,主要是不明白对于奇数个节点怎么进行两两交换,后来想明白了最直接的办法就是不处理最后多出来的那个节点就行了,对于原本的顺序
1(Head)->2->3->4
如果直接两两交换会导致交换完成后没有东西指向新的头节点,因此我们引入一个虚拟头节点dummy的方式,保证这个节点始终指向这个链表的头节点,然后对后续的节点进行操作
接下来是交换的逻辑,这也是重点。首先用两个临时变量保存cur节点的后一个以及后面第三个节点,这样做的原因是后续我们要将cur指向后面第二个节点,从而丢失对后一个节点的访问;同样在进行后面第二个节点next的指向调整时,会丢失对原第三个节点的访问,因此使用两个临时变量进行保存
dummy->1->2->3->4
dummy->2->3->4 1
dummy->2->1 3->4
dummy->2->1->3->4
//以上为交换过程,交换一次达到的效果是把cur后两个节点的位置交换并且保持cur节点本身的位置不变
完整代码如下
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* cur = dummy;
while(cur->next && cur->next->next)
{
ListNode* tmp1 = cur->next;
ListNode* tmp2 = cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = tmp1;
cur->next->next->next = tmp2;
cur = cur->next->next;
}
return dummy->next;
}
};
19.删除链表的倒数第N个节点
题目链接:19.删除链表的倒数第N个节点
卡哥提示这道题目需要采用双指针法,在这个思路的指引下完成了题目,但是代码不够简洁,而且需要分删除的是否为整个链表的第一个节点两种情况进行处理,显得非常臃肿,原因在于我没有使用虚拟头节点进行辅助以及一些细节处理不够到位
while(n-- && fast != NULL) {
fast = fast->next;
}
fast = fast->next;// fast再提前走一步,因为需要让slow指向删除节点的上一个节点
//相较于下面的代码就极其简洁,仅一个while循环加上一个单独的步骤就替代了下面的代码
for(int i = 1;i < n;i++)
{
fast = fast->next;
}
while(fast->next && fast->next->next)
{
fast = fast->next;
slow = slow->next;
}
完整代码如下:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
while(n-- && fast != NULL) {
fast = fast->next;
}
fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
// ListNode *tmp = slow->next; C++释放内存的逻辑
// slow->next = tmp->next;
// delete nth;
return dummyHead->next;
}
};
面试题 02.07. 链表相交
题目链接:面试题 02.07. 链表相交
这题的思路和上一题有些类似,就是找到两个链表(在上一题中体现为快慢指针距离之差)长度之差,然后调整起点后再一同进行遍历,其中有几步可以减少代码的书写量
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
//避免了对于两链表长短比较的讨论;同时while循环中gap--的判断方式是我需要学习的
完整代码如下:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while (curA != NULL) { // 求链表A的长度
lenA++;
curA = curA->next;
}
while (curB != NULL) { // 求链表B的长度
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
142.环形链表II
题目链接:142.环形链表II
这题难度较高,考虑过使用双指针的方法,但是没有一个可以具体进行实操的做法,看完解析之后豁然开朗。
创建fast和slow双指针,其中fast在链表中遍历的速度比slow快一格,即每次遍历两个节点,这样遍历知道两个指针在环中相遇(如果存在环),相遇即代表环存在;根据数学推导可以算出头节点到环的入口的距离一定等于fast和slow的相遇节点向后移动到环入口的距离与环总长度的整数倍之和,也就是说头节点和相遇节点同时开始向后逐个遍历直到相遇,这时的相遇处即为环的入口
代码如下:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
// 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
if (slow == fast) {
ListNode* index1 = fast;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2; // 返回环的入口
}
}
return NULL;
}
};
环形链表的详细解析: 142.环形链表II