24. 两两交换链表中的节点(中等)
题目链接:24.两两交换链表节点
题目描述:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
注:类似这种涉及节点交换的务必使用虚拟头节点,否则对首个节点要进行特殊处理。
/**
* 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* swapPairs(ListNode* head) {
ListNode* _dummyhead = new ListNode();//声明一个虚拟头节点指向head
_dummyhead->next = head;//虚拟头节点指向head
ListNode* pre = _dummyhead;//前指针指向虚拟头节点
ListNode* cur = pre->next;//后指针
ListNode* temp = nullptr;//用于存放cur->next
while(cur != nullptr && cur->next != nullptr) {//若当前节点和下一个节点均为非空节点则开始交换
temp = cur->next;
pre->next = temp;
cur->next = temp->next;
temp->next = cur;
pre = cur;
cur = cur->next;
}
return _dummyhead->next;
}
};
注意:while中的判断条件不能仅仅判断cur->next非空,必须cur和cur->next同时非空才进行交换。试想一下若是cur为空又何来cur->next呢?这里就会出现一个bug。
19.删除链表的倒数第N个节点(中等)
题目链接:删除链表的倒数第 N 个结点
题目描述: 给你一个链表,删除链表的倒数第 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) {}
* };
*/
//双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
//我自己的理解是这样的,比如链表中为_summyhead->1->2->3->4->5->null,slow和fast指针初始指向_summyhead
//若要删除倒数第2个节点,即n=2,此时我们要让slow移动到倒数第3个节点,易知slow要走sum-n步,即5-2=3步
//本例中链表长度为sum=5, 先让fast指针走n步,再多走一步,此时fast走了n+1步,再让slow和fast走到fast=null,此时fast要再走num-n(5-2)步才能到null。
//故让slow和fast同时向后遍历,恰好fast和slow都走num-n步,此时slow恰好指向要删除节点的前一个节点。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* _dummyhead = new ListNode();
_dummyhead->next = head;
ListNode* slow = _dummyhead;
ListNode* fast = _dummyhead;
while(n-- && fast != nullptr) {//让fast指针走n步
fast = fast->next;
}
fast = fast->next;//再多走一步
while(fast != nullptr) {//让slow和fast走num-n步
slow = slow->next;
fast = fast->next;
}
//进行删除操作
ListNode* temp = slow->next;
slow->next = slow->next->next;
delete temp;
return _dummyhead->next;//返回修改后的链表头节点
}
};
面试题 02.07. 链表相交(中等)
题目链接:面试题 02.07. 链表相交
题目描述: 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
思路:找交点容易想到快慢指针法,要考虑到两个单链表的长度,从而知道需要两个变量lenA和lenB,接着为了使两个工作指针能从后半段长度一致处开始同时后移,自然就需要求lenA和lenB的差值gap,让长链表的指针先走gap。
/**
* 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* curA = headA;//curA指向A链表首节点
ListNode* curB = headB;//curB指向B链表首节点
int lenA = 0;//A链表长度
int lenB = 0;//B链表长度
while (curA != NULL) {//计算A链表长度
curA = curA->next;
lenA++;
}
while (curB != NULL) {//计算B链表长度
curB = curB->next;
lenB++;
}
curA = headA;//算完链表长度后再调回到首节点
curB = headB;
if (lenB > lenA) {//让curA指向长度更大的链表,同理lenA和lenB也得swap
swap (lenA,lenB);
swap (curA,curB);
}
int gap = lenA-lenB;//链表差值
while(gap--) {//将长的链表中的指针先走gap步
curA = curA->next;
}
while(curA != NULL) {//寻找链表交点
if(curA == curB){//找到交点
return curA;//直接return
}
curA = curA->next;//AB各自的指针都往后移动
curB = curB->next;
}
return NULL;//没找到交点return NULL
}
};
142.环形链表Ⅱ (中等)
题目链接:142.环形链表Ⅱ
题目描述: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
思路: 显然要使用快慢指针法,定义快慢指针slow和fast。要解决两个问题
1.判断链表是否有环。2.若有环,找到这个环的入口。
1.判断链表是否有环: 从头结点出发,fast每次移动两个结点,slow每次移动一个结点,如果fast和slow指针在途中相遇,说明链表一定有环。
容易知道,若链表有环,则fast指针一定先进入环中,如果fast指针和slow指针相遇的话,必然在环中相遇。由于fast每次都比slow多移动一个节点,所以对于slow指针而言,fast指针是一步一步在追赶slow指针的,因此有环存在的话,fast总能在某一时刻恰好追上slow指针。至此,便可判断链表是否有环。
如下图所示:
2.判断环的入口是链表的第几个结点。
如图所示,头结点到环形入口节点的节点数为x,入口节点到相遇节点的节点数为y,相遇节点到入口节点的节点数为z。
则在相遇时:slow指针移动了x+y个节点,fast指针移动了x+y+n(y+z) 个节点,n为fast走的环数,显然n>=1。
又由于fast走的步数为slow 的两倍,从而有2*(x+y) = x+y+n(y+z),要求的是x,两边将x+y消去可得x+y = n(y+z),进而有x = (n-1)(y+z) + z。
不妨设n=1,此时有x=z,即从相遇节点到入口节点和头节点到入口节点的距离相等,故再声明两个指针,一个指向头节点,一个指向相遇节点,同时后移,当两指针指向同一个节点屎,就得到了环形入口节点。
代码如下:
/**
* 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* slow = head;//声明两个指针,slow和fast
ListNode* fast = head;
while (fast != NULL && fast->next != NULL) {//由于fast步长更大因此只需判断fast和fast->next是否为空
slow = slow->next;//慢指针每次走一个结点
fast = fast->next->next;//快指针每次走两个结点
if (slow == fast) {//指针相遇,证明有环
ListNode* index1 = fast;//index1指向相遇结点
ListNode* index2 = head;//index2指向头结点
while (index1 != index2) {//未相遇则向后移动
index1 = index1->next;
index2 = index2->next;
}
return index1;//返回相遇处的节点
}
}//fast为NULL或者指针永不相遇说明链表无环
return NULL;
}
};