双指针解决 相交链表 删除倒数第n个节点 反转链表

一、LeetCode-160. 相交链表

题目

160.相交链表

代码

/**
 * 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) {
        struct ListNode *p, *q;
        int cnt = 0, m = 0, n = 0;
        p = headA;
        q = headB;
        //记录A和B的节点数
        while(p) {
            p = p->next;
            m++;
        }
        while(q) {
            q = q->next;
            n++;
        }
        p = headA;
        q = headB;
        //移动到从后往数数目相同的位置
        if(m > n) { 
            for(int i=0; i<m-n; i++) {
                p = p->next;
            }
        }
        else {
            for(int i=0; i<n-m; i++) { 
                q = q->next; 
            } 
        }
        while(p != q) {
            p = p->next;
            q = q->next;
            if(p == q) { return p; }
            if(p == NULL || q == NULL) { return NULL; }
        }
        return p;
    }
};
/**
 * 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) {
        struct ListNode *p, *q;
        p = headA;
        q = headB;
        while(p != q) {
            p = p == NULL ? headB : p->next;
            q = q == NULL ? headA : q->next;
        }
        return p;
    }
};

思路一

首先看初始的图:

相交链表-01

为了更方便找到相交位置,我们可以使用双指针,同时移动两个指针headA和headB,若两指针相等,则说明链表相交

但出现了两个问题

  1. 题目要求不能改变原链表
  2. headA和headB到达相交点的时间可能不同

第一个问题很好解决,只需要另外声明p和q代替headA和headB即可

第二个问题也很好解决,假设A不相交的节点有a个,B不相交的节点有b个,那么只要将a或b往前移动a-b或b-a即可

也就变成下图:

相交链表-02

这时再同时移动p和q,若相等则返回其中一个节点,若到达NULL,则返回NULL

思路二

第二个思路很巧妙,p和q会分别从headA和headB开始走,若走到链表今头,则换成另一个链表开始走

下面为过程演示:


当p第一次到链表结尾:

相交链表-03

此时p换到headB,并且此时q到链表结尾(不一定此时q刚好到链表结尾):

相交链表-04

此时q换到headA,p向前继续移动:

相交链表-05


会发现此时q和p位于同一竖直线上,又回到思路一的最后一步

分析

设A不相交的节点有a个,B不相交的节点有b个,相交的节点有c个

当p走完headA时走了 a + c步,当q走完headB时走了b + c

当p走到相交处节点时走了a + c + b步,当q走到相交处节点时走了b + c + a步,发现走的步数一致了

说明走到节点处一定可以位于同一竖直线上

复杂度

两个思路的时间复杂度都为O(m + n),空间复杂度O(1)

二、LeetCode-19. 删除链表的倒数第 N 个结点

题目

19.删除链表的倒数第 N 个结点

代码

/**
 * 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* removeNthFromEnd(ListNode* head, int n) {
        struct ListNode*p, *p0, *q;
        p0 = p = q = head;
        for(int i=0; i<n; i++) { q = q->next; }
        while(q) {
            q = q->next;
            p0 = p;
            p = p->next;
        }
        p == p0 ? head = head->next : p0->next = p->next;
        delete p;
        return head;
    }
};

思路

这道题本质就是找倒数第n个节点,方法一就是计算链表长度,扫描两边,方法二就是双指针(本题思路)

很显然,要找到倒数第n个节点,先找到第n个节点,例如样例1:

设p, q, p0三个指针,先初始化为head,q为快指针

找倒数第二个节点,则先让q向前移动两次,则可以得到下图

倒数第n个节点-01

接下来同时移动p、q和p0,注意:当p不为头节点时,保证p0为p前一个节点

得到下图

倒数第n个节点-02

此时只需要将p0指向p的下一个节点,别忘了delete掉p就解决了问题

这里需要注意一个特殊情况:删除的是头节点

这种情况下解决办法之一是判断p是否等于p0,如果等于,说明p和q指针并未移动,删除的是第n个节点,也就是头节点

这种方法不需要多增加行数,只要将结尾的p0->next = p->next;替换成

p == p0 ? head = head->next : p0->next = p->next;

三、LeetCode-206. 反转链表

题目

206.反转链表

代码

//递归
/**
 * 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* reverseList(ListNode* head) {
        if(head == NULL || head->next == NULL) {
            return head;
        }
        struct ListNode* newhead = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return newhead;
    }
};
//双指针
/**
 * 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* reverseList(ListNode* head) {
        if(head == NULL || head->next == NULL) {
            return head;
        }
        struct ListNode *p0, *p, *p1;
        p0 = NULL;
        p = head;
        p1 = head->next;
        while(p) {
            p->next = p0;
            p0 = p;
            p = p1;
            if(p1) { p1 = p1->next; }
        }
        return p0;
    }
};

思路

已经写四遍了,烂熟了,倒背如流,懒得写了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YRoads

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值