非科班学习算法day4 | LeetCode24: 两两交换链表中的节点,Leetcode19: 删除链表的倒数第N个节点,面试题02.07 链表相交 ,Leetcode142:环形链表||
目录
介绍
包含LC的四道题目,还有相应概念的补充。
相关图解和更多版本:
代码随想录 (programmercarl.com)https://programmercarl.com/#%E6%9C%AC%E7%AB%99%E8%83%8C%E6%99%AF
一、基础概念补充:
链表相关知识请见day3!
二、LeetCode题目
1.LeetCode24: 两两交换链表中的节点
题目链接:
题目解析
模拟过程,利用虚拟头节点,设置遍历指针;
和删除节点需要注意的事项一样,需要使用的后续节点提前保存。
c++代码如下:
/**
* 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(0);
//将虚拟头节点插到头部
dummyhead->next = head;
//设置遍历指针
ListNode * cur = dummyhead;
//
while(cur && cur->next &&cur->next->next)
{
ListNode* temp1 = cur->next;
ListNode* temp2 = cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = temp1;
cur->next->next->next = temp2;
cur = cur->next->next;
}
ListNode * result = dummyhead->next;
delete dummyhead;
return result;
}
};
注意点1:最后在返回的时候需要删除虚拟头节点,所以这里多了一步保存头节点信息,然后删除虚拟头节点,最后返回保存信息。
注意点2:关于遍历条件,我一开始写的是cur && cur->next,现在举个例子,如果希望能够交换两个值,那么这两个值必须存在,而我们cur指针一开始指向都是在需要交换的两个数前面。所以才会又现在的条件cur && cur->next->next。
注意点3:这道题没有必要采用两个指针来忽悠自己.(o-o)
2.Leetcode19:删除链表的倒数第N个节点
题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
题目解析
这一次采用最常规的方式,遍历两次,第一次获取链表长度;第二次遍历链表到指定位置进行删除操作。
C++代码如下:
/**
* 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:
int getCount(ListNode* head)
{
int count = 0;
while(head)
{
++count;
head = head->next;
}
return count;
}
ListNode* removeNthFromEnd(ListNode* head, int n)
{
//设置虚拟头节点
ListNode* dummdyhead = new ListNode(0);
//插入虚拟头节点
dummdyhead->next = head;
//设置检索指针
ListNode * cur = dummdyhead;
//获取链表长度
int count = getCount(head);
for(int i = 1; i < count - n + 1; ++i)
{
cur = cur->next;
}
cur->next = cur->next->next;
ListNode* result = dummdyhead->next;
delete dummdyhead;
return result;
}
};
注意点1:首先获取数组长度和插入虚拟头节点这两个操作并不陌生,这里补充一个便捷写法
ListNode* dummyhead = new ListNode(0, head);
注意点2:我自己在做的时候关于第二次遍历反复出错,这里在计算长度的时候一定要注意是从虚拟头节点(i=0)还是头节点(i = 1)来进行。
3.面试题02.07.链表相交
题目链接:面试题 02.07. 链表相交 - 力扣(LeetCode)
题目解析
首先这道题可以直到我们需要两个指针分别从headA和headB来解决遍历的,那么问题是,怎么确定headA和headB处于同一位置。
借助LeetCode的Krahets的思路:先假设两个链表存在公共的尾部,长度为c;链表A的长度为a,链表B的长度为b,公共的节点node。
当指针A遍历结束A链表,开始遍历B链表,到达node,此时一共需要步数:a+(b-c)
当指针B遍历结束B链表,开始遍历A链表,到达node,此时一共需要步数:b+(a-c)
这时不难发现两者相等。
C++代码如下:
/**
* 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)
{
if(headA == nullptr || headB == nullptr)
{
return nullptr;
}
//设置指针1
ListNode * cur1 = headA;
//设置指针2
ListNode * cur2 = headB;
while(cur1 != cur2)
{
if(cur1 == nullptr)
{
cur1 = headB;
}
else
{
cur1 = cur1->next;
}
if(cur2 ==nullptr)
{
cur2 = headA;
}
else
{
cur2 = cur2->next;
}
}
return cur1;
}
};
注意点1:对于返回值也有两种情况,首先,有公共节点,直接返回node,也就是现在指针的位置;没有公共节点的,两个遍历结束,也可以根据要求直接返回指针。
4.Leetcode142:环形链表||
题目链接:142. 环形链表 II - 力扣(LeetCode)
题目解析
有两个问题需要解决,第一个是如何确定有没有环?
第二个是如何确定环的入口?
针对第一个问题,我们可以采用快慢双指针,就像是小学数学中的追及问题,将快指针的步长设置为2,慢指针的步长设置为1,那么在移动指针的过程中,如果存在环,那么快慢指针一定在圆环上重合。
针对第二个问题,这里参考代码随想录 (programmercarl.com)中的思路,可以确定快慢指针重合之后的数量关系。
/**
* 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* fast = head;
//慢指针
ListNode* slow = head;
//追及问题
while(fast != nullptr)
{
slow = slow->next;
if(fast->next == nullptr)
{
return nullptr;
}
fast = fast->next->next;
if(fast == slow)
{
ListNode* ptr = head;
while(ptr != slow)
{
ptr = ptr->next;
slow = slow->next;
}
}
return ptr;
}
return nullptr;
}
};
注意点1:其实在解决了如何确定有没有环的问题之后,转化为代码的困难集中到了边界条件如何设置。首先来看追及问题的边界条件是指针遍历结束,这时链表不存在环,返回值自然也就是空,又因为快指针先遍历结束,所以条件为快指针不指向空。
注意点2:针对于快指针,因为它的步长是2,所以也要检查当前位置的后一个指针是不是空。不检查后两个的原因是,我们已经在外层设置了循环的条件是快指针的指向为空停止。
总结
打卡第四天,对我来说很有难度,耽搁到了今天,所以坚持!!!