24.两两交换链表中的结点
这道题我自己是用递归做出来的,感觉代码比较简单,但是代码随想录给的答案是设了一个虚拟头结点,然后再仔细地进行操作。
递归代码:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head == nullptr || head->next == nullptr)
return head;
ListNode* tmp = head->next;
head->next = swapPairs(tmp->next);
tmp->next = head;
return tmp;
}
};
这种里面主要就是这些next的顺序特别容易搞乱,最好自己在图上什么的画一下。
创建虚拟头结点方法:
具体过程可以看图得知:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
ListNode* cur = dummyHead;
while(cur->next != nullptr && cur->next->next != nullptr) {
ListNode* tmp = cur->next; // 记录临时节点
ListNode* tmp1 = cur->next->next->next; // 记录临时节点
cur->next = cur->next->next; // 步骤一
cur->next->next = tmp; // 步骤二
cur->next->next->next = tmp1; // 步骤三
cur = cur->next->next; // cur移动两位,准备下一轮交换
}
ListNode* result = dummyHead->next;
delete dummyHead;
return result;
}
};
再问了gpt它说,递归法的时间复杂度是O(N),空间复杂度也是O(N)。
而虚拟头结点方法的时间复杂度是O(N),空间复杂度是O(1)。
19.删除链表的倒数第n个元素
这题如果想到了快慢指针的话,应该挺容易的。
不过,我在自己做的时候没有用虚拟头结点,然后它就一直显示超时,我也不知道是什么原因。
最后还是根据给的答案,加上了虚拟头结点才通过。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* _dummyHead = new ListNode(); //虚拟头结点
_dummyHead->next = head;
ListNode *slow = _dummyHead; //慢指针,初始化slow位置
ListNode *fast = _dummyHead;
while(n--)
{
slow = slow->next;
}
while(slow->next != nullptr)
{
fast = fast->next;
slow = slow->next;
}
ListNode *tmp = fast->next; //待删节点
fast->next = tmp->next;
delete tmp;
return _dummyHead->next;
}
};
160.相加链表(也是快慢指针)
我都没想到能先计算他们的长度,看看吧,主要是考虑到相加以后的链表长度都是一样的,所以就通过计算长度,然后利用快慢指针把这些长度差跳过去。另外里面还有个技巧就是,通过swap让,cur为最长链表的头,lenA成为它的长度
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.环形链表||
这道题知道用快慢指针解决的话,很容易可以想到怎么判断是否有环,但是最困难的是找到环的入口,然后就根据数学的知识:
在相遇时,慢指针slow走了x+y步,快指针fast走了x+n(y+z)+y步(n为快慢指针相遇前快指针在环内走了n圈,n>=1),以上为路程,而它们的速度分别为1和2,由于二者走了相同时间,
可建立等式(x+y)/1=[x+n(y+z)+y]/2,化简得x=n(y+z)-y,即x=(n-1)(y+z)+z。
若n=1,则x=z,说明相遇点到入口处的距离z=起始点到入口处的距离x。
当然,n也可以不取1,就表示index2从起始点到入口处的过程中,index1从相遇点开始走,走了n-1圈后,再走距离z,就可以在入口处与index2相遇(注意:index1和index2速度相同,都为1步)。—>这么写代码,不就可以找到入口处了嘛!
tips:!!慢指针在入环后,一定走了不到一圈就被快指针给追上了。
代码如下:
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;
}
};
总之,在使用快慢指针的时候,要好好地分析指针之间的数学关系。