链表进阶(第四天)——力扣24两两交换链表中的节点,19删除链表的倒数第N个节点,面试题 02.07链表相交,142环形链表II

力扣题目解析

一.力扣24 两两交换链表中的节点

注意:

要设一个虚拟的头节点,原因代码里可以看出来

思路如下

/**
 * 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* swapPairs(ListNode* head) {
        ListNode *q;
        ListNode *k; 
        ListNode *Head = new ListNode(0);//方便交换之后找到起始节点
        Head->next = head;//方便交换之后找到起始节点
        ListNode *p = Head;
        while(p->next && p->next->next)
        {
            q = p->next->next;
            k = q->next;
            q->next = p->next;
            p->next = q;
            q->next->next = k;
            p = q;
        }
        return Head->next;
    }
};

二.力扣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) 
    {
        ListNode* Head = new ListNode(0);
        Head->next = head;
        ListNode* fast = Head;
        ListNode* slow = Head;
        while(n--)
        {
            fast = fast->next;
        }
        while(fast->next)
        {
            fast = fast->next;
            slow = slow->next;
        }//此时slow指向的结点就是需删除删除结点的上一个结点
        ListNode* q = slow->next;
        slow->next = q->next;
        delete q;
        q = nullptr;
        head = Head->next;
        return head;
    }
};

三.面试题 02.07. 链表相交

思路:求出两个链表的长度,求长度差值,使指向较长链表的指针后移到与较短链表头结点对齐。

这个不需要设虚拟头结点,但我还是建立了,哈哈成为习惯了。

/**
 * 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) {
        ListNode *Heada = new ListNode(0);
        ListNode *Headb = new ListNode(0);
        Heada->next = headA;
        Headb->next = headB;
        ListNode *a = Heada;
        ListNode *b = Headb;
        int sizea = 0;
        int sizeb = 0;
        while(a->next)
        {
            a = a->next;
            sizea++;
        }
        while(b->next)
        {
            b = b->next;
            sizeb++;
        }
        a = headA;
        b = headB;
        if(sizea < sizeb)
        {
            int dif = sizeb - sizea;
            while(dif--)
                b = b->next;
        }
        if(sizea > sizeb)
        {
            int dif = sizea-sizeb;
            while(dif--)
                a = a->next;
        }
        while(a != NULL && b != NULL)
        {
            if(a == b)
                return a;//直接返回交点
            a = a->next;
            b = b->next;
        }
        return NULL;
    }
};

四.142.环形链表II 

思路:

1.首先是判断是否有环存在:可以定义两个指针fast和slow,fast指针每次走2个结点,slow指针每次走一个结点。就像是小学做的追赶数学题,如果存在环fast总会追上slow,也就是fast和slow总会指向同一个节点。

2.判断起点

此图片是借用代码随想录里的内容

把y移动右边得到x = (n-1)(y+z) + z ,也就是说x等于若干圈环的结点数 加上 相遇结点到环结束结点的结点数(着重理解这句话)

所以当n = 1时,可以定义两个指针p指向头结点,q指向相遇节点,循环每次两指针移动一个结点,指到两指针相遇,那么该结点就是起始结点。

而当n > 1时,同样是定义两个指针,p指向头结点,q指向fast和slow相遇的节点,看起来我们这个时候再让p移动(n-1)个环的长度,然后在循环,找到q和p的相遇节点就可以了。但实际上是不需要让p向前移动(n-1)个环的长度,因为q还会继续循环下去,如图所示,方便理解。

到这里我们就可以照着思路完成代码了。

如果大家对于为什么在slow指针走第一圈环的过程中就一定会和fast相遇有疑问的话,我画了一个图作为解答。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *Head = new ListNode(0);
        Head->next = head;
        ListNode *fast = head;
        ListNode *slow = head;
        ListNode * q;
        ListNode * p = head;
        int f = 0,s = 0;
        int boo;
        while(fast != NULL && fast->next != NULL)
        {
            fast = fast->next->next;
            f += 2;
            slow = slow->next;
            s++;
            if(fast==slow)
            {
                q = fast;
                while(q != p)
                {
                    q = q->next;
                    p = p->next;
                }
                return q;
            }
        }
        return NULL;
    }
};

链表知识点总结

1.删除某个结点时,要找到的是该结点的上一个结点。

2.在头节点前建立一个虚拟结点,多数情况下都会方便很多,比如力扣第19题如果删除的节点是头节点的话就,设置一个虚拟头节点就会减少很多麻烦。

3.关于删除结点,delete只是释放了内存,还需要指针指向NULL,否则指针将会成为野指针随机指向一处内存地址。

4,如果头结点有可能改变,记得加上head = Head->next (Head是虚拟头节点)

5.很多时候结点要计数才能找到的时候,可以定义两个指针找相遇节点。

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值