【水滴计划 | 每日两题】:删除链表的倒数第 N 个结点、链表相交

82 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第19天,点击查看活动详情

1、写在前面

大家好,我是翼同学,这里是【水滴计划 | 刷题日志】

每日两题,拒绝摆烂。

2、内容

2.1、题目一:删除链表的倒数第 N 个结点

链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

(1) 描述

image.png

(2) 举例

image.png

image.png

image.png

image.png

(3) 解题

这道题可以利用双指针来求解。

定义快慢指针fastslow,先让fast移动n步,接着让fastslow同时移动,直到fast移动到了链表的末尾,此时删掉slow指向的结点,就相当于删掉倒数第n个结点。

演示如下:

力扣.gif

结果为:

image.png

代码如下:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* pHead = new ListNode(0);  // 虚拟头结点
        pHead->next = head;                 // 将虚拟头结点指向头指针

        ListNode* fast = pHead;     // 快指针
        ListNode* slow = pHead;     // 慢指针
        
        // 让 fast 移动 n 步
        while(n-- && fast != NULL) {
            fast = fast->next;
        }

        // fast再移动一步,因为需要让 slow 指向删除节点的上一个节点
        fast = fast->next;

        // 同时移动快慢指针
        while (fast != NULL) {
            fast = fast->next;
            slow = slow->next;
        }
        
        // 删除掉 slow-next
        ListNode *tmp = slow->next;
        slow->next = tmp->next;
        delete tmp;
        
        // 返回运算结果
        return pHead->next;
    }
};

image.png


2.2、题目二:链表相交

链接:面试题 02.07. 链表相交 - 力扣(LeetCode)

(1) 描述

image.png

(2) 举例

image.png

image.png

image.png

image.png

(3) 解题

第一种解题思路是:

  1. 定义两个工作指针curAcurB,分别指向两个链表的头指针
  2. 接着进入循环(当curA==curB时退出循环)
  3. 在循环中,指针curA先遍历链表A ,再遍历链表BcurB也一样,先遍历链表B,再遍历链表A
  4. 当指针curAcurB相等时就可以退出循环,此时直接返回curA即可(如果两个链表没有公共部分,则最后curAcurB会同时指向NULL

动态示意图如下:

1.gif

代码如下:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        // 定义两个工作指针
        ListNode *curA = headA;
        ListNode *curB = headB;
        // 循环遍历,当curA==curB时退出循环
        while (curA != curB)
        {
            // 指针 curA 先遍历链表 A ,再遍历链表 B
            if(curA != NULL) {
                curA = curA->next;
            } else {
                curA = headB;
            }
            // 指针 curB 先遍历链表 B ,再遍历链表 A
            if(curB != NULL) {
                curB = curB->next;
            } else {
                curB = headA;
            }   
        }
        // 返回结果即可
        return curA;
    }
};

image.png


第二种思路:

首先有两个链表:

image.png

定义两个工作指针,并分别求出两个链表的长度。此时将两链表的长度之差记录下来。然后将短链表的尾部和长链表对齐。如下所示:

image.png

最后我们就可以开始比较两个工作指针是否相同。如果相同,则返回curA即可。否则将两个工作指针同时向后移动一位。

image.png

代码如下:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {

        ListNode* curA = headA;     // 工作指针 A
        ListNode* curB = headB;     // 工作指针 B

        int lenA = 0;   // 链表 A 的长度
        int lenB = 0;   // 链表 B 的长度

        // 求链表A的长度
        while (curA != NULL) { 
            lenA++;
            curA = curA->next;
        }
        // 求链表B的长度
        while (curB != NULL) { 
            lenB++;
            curB = curB->next;
        }

        // 重新定义工作指针的指向
        curA = headA;
        curB = headB;


        // 让 curA 为最长链表的头,lenA为其长度
        if (lenB > lenA) {
            swap (lenA, lenB);
            swap (curA, curB);
        }

        // 求长度差
        int len = lenA - lenB;

        // 让 curA 和 curB 在同一起点上(末尾位置对齐)
        while (len--) {
            curA = curA->next;
        }

        // 遍历 curA 和 curB,遇到相同则直接返回
        while (curA != NULL) {
            if (curA == curB) {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

image.png

3、写在最后

好了,今天就刷到这里,明天再来。