代码随想录刷题60day
前言
今日主要介绍一些对链表节点进行操作与定位的运用范例,主要聚焦于链表节点交换,删除和定位循环链表(本文着重于问题的最优解,对于暴力解法本文不会提及)
两两交换链表中的节点
指针的选择及其应保存的值
通常,在使用迭代方式交换单链表两个节点位置时,我们至少需要使用三个指针来保存节点信息。
pre指针用来保存我们需要操作的一组节点(一个及以上)的前一组的头节点(以上图为例,当我们对值为1、2的节点进行操作时,因为1节点能通过next指针找2节点地址,而2节点不能找到1节点的地址,因此我们把1节点称为这一组节点的头节点)。
cur指针用来保存当前组的头节点。
temp指针用来保存当前组下一组的头节点。
使用pre指针是为了重新建立前一组节点与本组节点的关系,使用temp节点是因为当前组完成操作后已经无法通过cur指针将当前组更新为下一组,因此需要通过temp指针辅助迭代操作。
关于迭代
当我们在写while或for循环进行迭代操作时,必须将迭代开开始和结束的特殊情况通过额外的代码段彻底解决掉,否则即使迭代本体并没任何错误,也会出现一些会卡很久的错误(正确的废话)。
ListNode* swapPairs(ListNode* head)
{
if (!head || !head->next)return head;
ListNode* Head = new ListNode(0);
ListNode* pre, *cur, *temp;
Head->next = head->next;
pre = head;
while (pre && pre->next)
{
cur = pre->next;
temp = cur->next;//记录下一组需交换的第一个节点
if (temp && temp->next)//若下一组节点存在
pre->next = temp->next;//将交换后两组相邻节点连接
else
pre->next = temp;//直接相连
cur->next = pre;//交换节点
pre = temp;
}
return Head->next;
}
删除链表的倒数第n各个节点
快慢指针的引用,需要注意的是对删除头节点的特殊处理。
ListNode* removeNthFromEnd(ListNode* head, int n)
{
ListNode* pSlow=head, * pFast=head;
for (int i = n; i > 0; i--)
pFast = pFast->next;
//这里快指针多走一步是为方便慢指针移出节点操作
if(!pFast)
return pSlow->next;
//当快节点为空时,说明n为链表长度,删除的为头节点
while (pFast->next)
{
pFast = pFast->next;
pSlow = pSlow->next;
}
pSlow->next = pSlow->next->next;
//当删除的为头节点时
return head;
}
链表相交
本体实际上是小学追及相遇问题的一种变形。主要思路有两种:
第一种是“取长”:通过循环计算A,B两条路径的长途,计算两路径的长度差n,让长路径的指针提前移动距离n使得剩下路径相等,然后使路径A,B上的指针a,b同时同速移动直至两指针位置相等。
第二种是“长补短,短补长”:同时使a,b两指针移动,当a指针移动终点时,将其移动到B路径的起点;同理b移到起点A。由于a指针提前n个移动到路径B,当b指针移动到路径A时,两指针剩下距离相等,则会在路径交叉点相遇。
以下给出第二种方法的详细代码。
ListNode getIntersectionNode(ListNode* headA, ListNode* headB)
{
ListNode* a = headA, *b = headB;
while (a != a)
{
a = ((a == nullptr) ? headB : a->next);
b = ((b == nullptr) ? headA : b->next);
}
return a;//return b;
}
环形链表环的入口定位
本体实际上是借助快慢指针这一方法,通过循环链表这一数据结构解决一些数学问题。
首先,我们需要借助什么来找到问题的突破口?由于循环链表具有环这个特性,所以我们可以通过快慢指针有可能会相遇这个点来思考问题。那么为了问题的简单起见,我们设快指针移动速度为2,慢指针的移动速度为1,当两种进入环时,由于相对速度为1 ,所以始终会相遇。
那个这个相遇点能够为我们提供什么信息呢?通过分析第一次相遇点,我们可以得到如下信息:
1.快慢指针相遇时,快指针比慢指针多走了一圈距离(y+z)。
2.快指针的路程为慢指针路程的两倍。慢指针路程为x+y,快指针路程为x+y+y+z。
通过以上两点信息,我们可以得到以下结论:x距离等于z。于是,我们只要在快慢指针相遇时,将快指针移到起点,然后让两指针以相同速度1移动,则一定会在环的入口相遇。
详细示例代码如下:
ListNode* detectCycle(ListNode* head)
{
ListNode* pFast, * pSlow;
if (head && head->next)
{
pFast = head->next->next;
pSlow = head->next;
}
else
return nullptr;
while (pFast && pFast->next && pFast != pSlow)
{
pFast = pFast->next->next;
pSlow = pSlow->next;
}
if (!pFast || !pFast->next)
return nullptr;
pSlow = head;
while (pSlow != pFast)
{
pSlow = pSlow->next;
pFast = pFast->next;
}
return pSlow;
}