目录
Day 4:
4.1 Leetcode24 两两交换链表中的节点
注意:
- 虚拟头结点的建立,保证首元结点(或头结点)和其他结点操作一致
- 画图理逻辑,看看怎么交换,注意交换的前后顺序
代码:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyNode=new ListNode(-1);
dummyNode->next=head;
ListNode* pre=dummyNode;
ListNode* cur=head;
while(cur&&cur->next)
//必须保证cur和cur-next都不为空,才能进行交换,交换的就是这两个点。有一个为空,代表后面没节点或者只剩一个结点,退出循环
{
//交换节点
ListNode* temp=cur->next->next;//cur->next不空时,cur->next->next才成立
pre->next=cur->next;
cur->next->next=cur;//cur后面一个结点的next指向,反向
cur->next=temp;//cur指向cur后的第2个元素(即后面的后面)
//移动pre和cur,使新位置和其实位置一致,pre指向操作点的前一个,cur指向操作点,这是原则
pre=cur;
cur=cur->next;
}
return dummyNode->next;
}
4.2 Leetcode19 删除链表的倒数第n个结点
我的思路:看成总数第len-n+1个,需求链表总长度
代码随想录题解:fast从虚拟头结点先移动n步,然后fast和slow同时移动,当fast移动到末尾的NULL位置时,slow指向倒数第n个节点
//思路:倒数第n个,相当于正数第len-n+1个,用for循环到这即可。这里纯循环不做其他比较,用for更快不易出错
//牛客剑指22是返回倒数第n个元素的值,不是删除,注意区别,那个更简单
//先建个getLength函数,获取链表长度(也可合在一起)
int getLength(ListNode* head)
{
ListNode* cur=head;
int length=0;
while(cur)
{
length++;
cur=cur->next;
}
return length;
}
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyNode=new ListNode(-1);
dummyNode->next=head;
ListNode* pre=dummyNode;
ListNode* cur=head;
int len=getLength(head);
//题目给出了n<=len这个条件,所以不用判断n>len情况;如果是第一个元素,跳过for循环
for(int i=1;i<=len-n;i++)//循环1次到第二个数,循环len-n次到第len-n+1个数,符合条件
{
pre=cur;
cur=cur->next;
}
pre->next=cur->next;
delete cur;
return dummyNode->next;
}
4.3 Leetcode面试题02.07 链表相交
注意:本题是求两个链表交点节点的指针。
但要特别注意,交点不是数值相等,而是指针相等,即结点ListNode* 相等
我的思路:做过,第一反应还是哈希表,此时空间复杂度为O(n)。又用了双指针连接法,提交时三目运算符前面的条件写错了,要注意。
方法3是代码随想录解法,没写。
//方法1:以前做过,第一反应是哈希表:先遍历一个链表统计次数,再遍历另一个链表看出没出现过,出现即返回
//注意结点相同,不是结点的值相等
//空间复杂度O(n),时间复杂度O(n)
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_map<ListNode*,int> mp;//int表示前面节点出现的次数
ListNode* cur=headA;
while(cur)
{
mp[cur]++;
cur=cur->next;
}
cur=headB;
while(cur)
{
if(mp[cur]!=0)//说明出现过,或者mp.find[cur]!=mp.end(),find和.end()配合
{
return cur;
}
cur=cur->next;//此时不必再往哈希表里放了
}
return NULL;
}
//方法2:两个链表连接在一起,相当于各遍历 一个长度为(两个链表长度和+一个NULL位)的链表
//空间复杂度O(1),时间复杂度O(n)
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
//为了提高运算速度,可以把下面这两行加上,不加也能AC
if(headA==NULL||headB==NULL)
{
return NULL;
}
ListNode* p=headA;
ListNode* q=headB;
while(p!=q)
{
p=p!=NULL?p->next:headB;//这一这里的条件一开始上下写错了,写成p->next=NULL了,这一导致找不到共同点时无限循环了
q=q!=NULL?q->next:headA; //改成这个之后,找不到时,p和q同时到达NULL。第一次p和q不同时到达NULL
}
//遍历结束时,要么遍历完两个链表长度后,要么pq不为空,且p=q退出,找到交点;要么p和q同时相等,为NULL
return p;//最后p=q
}
//方法3:快慢指针,统计长度,先让长度长的for循环走到相等处,再一起走,走到有一个为空或者相交处返回
4.4 Leetcode142 环形链表II
注意:以下是代码随想录题解,快慢指针总结的还是挺有用的。
- 可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
2、fast每次走两步,slow每次走一步,fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的。
3、因为fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,不能跳过,所以fast一定可以和slow重合。
4、公式推导后:从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
5、fast指针走过的节点数 = slow指针走过的节点数 * 2:
我的思路还是哈希表:空间复杂度O(n)
//方法1:第一思路还是哈希表,遍历全装进哈希表,看有没有重复装的。空间复杂度为O(n)
ListNode *detectCycle(ListNode *head) {
unordered_map<ListNode*,int> mp;
ListNode* cur=head;
while(cur) //如果有环,一直循环;如果没环,到NULL退出循环
{
if(mp[cur]!=0)//已出现过
{
return cur; //返回环第一个结点
}
else//此处加不加else都行
{
mp[cur]++;
cur=cur->next;
}
}
return NULL;
}
//方法2:代码随想录题解思路
ListNode *detectCycle(ListNode *head) {
ListNode* fast=head;
ListNode* slow=head;
while(fast&&fast->next)//当fast和fast->next不为空时循环,如果任一为空则没环
{
fast=fast->next->next;//移动两步,fast->next不空才有fast->next->next
slow=slow->next;
if(slow==fast)//找到相遇点
{
ListNode*index1=slow;//俩指针,一个指向头结点,一个指向相遇点
ListNode*index2=head; //最好新定义两个,防止和while循环里的弄混,易出问题
while(index1!=index2)//再次相遇即为环入口
{
index1=index1->next;
index2=index2->next;
}
return index1;
}
}
return NULL;
}