【算法日志】链表运用:链表节点的交换,删除和定位以及循环链表(day4)

代码随想录刷题60day

【链表】day3


文章目录

前言

两两交换链表中的节点

指针的选择及其应保存的值

关于迭代

删除链表的倒数第n各个节点

链表相交

环形链表环的入口定位


前言

今日主要介绍一些对链表节点进行操作与定位的运用范例,主要聚焦于链表节点交换,删除和定位循环链表(本文着重于问题的最优解,对于暴力解法本文不会提及)


两两交换链表中的节点

【算法日志】链表运用:链表节点的交换,删除和定位以及循环链表(day4)_第1张图片

指针的选择及其应保存的值

通常,在使用迭代方式交换单链表两个节点位置时,我们至少需要使用三个指针来保存节点信息。

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各个节点

【算法日志】链表运用:链表节点的交换,删除和定位以及循环链表(day4)_第2张图片

 快慢指针的引用,需要注意的是对删除头节点的特殊处理。

    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;
     }

链表相交

【算法日志】链表运用:链表节点的交换,删除和定位以及循环链表(day4)_第3张图片

本体实际上是小学追及相遇问题的一种变形。主要思路有两种:

第一种是“取长”:通过循环计算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;
 }

环形链表环的入口定位

【算法日志】链表运用:链表节点的交换,删除和定位以及循环链表(day4)_第4张图片

 本体实际上是借助快慢指针这一方法,通过循环链表这一数据结构解决一些数学问题。

首先,我们需要借助什么来找到问题的突破口?由于循环链表具有环这个特性,所以我们可以通过快慢指针有可能会相遇这个点来思考问题。那么为了问题的简单起见,我们设快指针移动速度为2,慢指针的移动速度为1,当两种进入环时,由于相对速度为1 ,所以始终会相遇。

那个这个相遇点能够为我们提供什么信息呢?通过分析第一次相遇点,我们可以得到如下信息:

【算法日志】链表运用:链表节点的交换,删除和定位以及循环链表(day4)_第5张图片

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;
     }

你可能感兴趣的:(算法,数据结构,c++,leetcode,链表)