代码随想读第4天|C++|leetcode|24.两两交换链表中的节点|删除链表的倒数第N个节点|面试题 02.07. 链表相交|142.环形链表 II

第二章 链表part02

昨日由于写的太晚没有上传,对于递归仍然不能独立写出,需要多加总结练习

LeetCode 24.两两交换链表中的节点

题目链接24. 两两交换链表中的节点

思路

  1. 直接三指针迭代交换法,时间复杂度最优:交换节点位置,很直观看出每一轮的操作就是(以1,2为例子,假设前面有dummy_Hand)

    p->next=q->next;
    t->next=q;
    q->next=p;
    //t后移两位,p后移一位,q后移三位,继续操作,直到q为nullptr
    //不为都后移两位的原因是,p,q位置是经过交换的,交换后q在p前,下一轮则q后移三位,p后移一位
    

在这里插入图片描述

  1. 递归法,栈的思路,一轮操作如下图:

    在这里插入图片描述

完整C++代码如下

//1
//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head==nullptr||head->next==nullptr)
            return head;
        ListNode *t = new ListNode();
        t->next = head;
        ListNode *p = head;
        ListNode *q = head->next;
        head = head->next;
        while(q){
            p->next = q->next;
            t->next = q;
            q->next = p;
            //p->next不过,p->next->next==nullptr判断直接跳过
            if (p->next==nullptr||p->next->next==nullptr)
                return head;
                p = p->next;
                q = q->next->next->next;
                t = t->next->next;
        }
        return head;
    }
};

//2
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head==nullptr||head->next==nullptr)
            return head;
        ListNode *newHead = head->next;
        head->next = swapPairs(newHead->next);
        newHead->next = head;
        return newHead;
        }
};

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

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

思路

本题其实并不难做,但怎么快速的一遍定位到倒数第N个节点,这需要天才思维之:快慢指针,通过一点数学原理,一次就能定位到目标位置

  1. 由于删除可能为任意一个节点(包括第一个节点),所以设置虚拟头结点dummy_Head
  2. 设置s,q快慢指针指向dummy_Head位置,先让快指针q先走n+1步
  3. 再让s,q同时走,结束条件是q指到nullptr
  4. 此时s所指位置下一个位置就是要删除的结点

解释:

设有t个节点,倒数第n个节点就是正数第t-n+1个节点,因为是要删除节点,所以要走到t-n个节点即从dummy_Head走t-n步就是目标。

q先走n+1步,到了第n+1个节点位置上,当走到nullptr需要再走t-n步,而前面说了s走t-n步恰好就是位于要删除节点的上一个位置(长度互补)。

完整C++代码如下

//时间复杂度: O(n)
//空间复杂度: O(1)
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
		ListNode* dummy_Head=new ListNode();
        dummy_Head->next=head;
        ListNode*q=dummy_Head,*s=dummy_Head;//连续定义指针注意*s
        n++;//走n+1步,先提前让n+1
        while(n--){
            q=q->next;
        }
        while(q){
            q=q->next;
            s=s->next;
        }
        ListNode* temp=s->next;
        s->next=temp->next;
        delete temp;
       return dummy_Head->next;
    }
};

注:ListNode*是一个指针类型说明符,ListNode *q=dummy_Head, *s=dummy_Head,其中s前面有 *, * 表示 qs 都是指针类型。

LeetCode 面试题 02.07. 链表相交

题目链接面试题 02.07. 链表相交

思路

  1. 哈希表,把一条路径上的所有节点地址存储了,之后另一条路径上逐个对照直到找到相交节点

  2. 双指针遍历法,运用数学思维,某种程度上来说本题解和上一道题的题解有异曲同工之处,仍然是(总长相等,内部两个部分互补的本质

    借用Krahets老哥的图来描述:

    如图,我们可以在headA和headB处分别建立指针A,B

    • 头节点 headAnode 前,共有a−c个节点;
    • 头节点 headBnode 前,共有 b−c 个节点;
    • 这里可以发现A从头走到尾(null)走了a,再将A从尾移到headB开始走到首个公共节点走了b-c,一共走了a+b-c
    • A从头走到尾(null)走了b,再将A从尾移到headB开始走到首个公共节点走了a-c,一共走了a+b-c
    • 原因在于长度互补,类似于我画的图这段橘色的线段长度是恒定的
      在这里插入图片描述

完整C++代码如下

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        unordered_set<ListNode *>set;
        while(headA){
            set.insert(headA);
            headA=headA->next;
        }
        while(headB){
            if(set.find(headB)!=set.end()){
                return headB;
            }
            headB=headB->next;
        }
        return nullptr;
    }
};

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(!headA||!headB)return NULL;
		ListNode *A=headA;
		ListNode *B=headB;
        while(A!=B){
     		if(!A){
                 A=headB;//也算作一次机会,等于是说明A已经经过headA节点
            }else{
                 A=A->next;
            }
            if(!B){
                B=headA;
            }else{
                B=B->next;
            }  
        }
      return B;
    }
};

LeetCode 142.环形链表 II

题目链接142. 环形链表 II

思路:数学思维

  1. 数学思维双指针法,建议特别收录至数学思维题库

    • 设置快指针fast ,慢指针slow在头节点,快指针每次走两步,慢指针每次走一步,如果是无环单链表,slow永远追不上quick,但当有环的时候,我们发现,只要slow也进了圈,quick就相当于以速度1(相对)追slow,最后某一时刻一定会追上。
    • 此处需要用到数学思考:

    假设从头结点到环形入口节点 的位移数为x。 环形入口节点到 fast指针与slow指针相遇节点 位移数为y。 从相遇节点 再到环形入口节点位移数为 z。

    相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z)

    又已经知道: fast指针走过的节点数 = slow指针走过的节点数 * 2

    (x + y) * 2 = x + y + n (y + z)->x + y = n (y + z)

    又由于我们要求x,可以将式子转化为:x = (n - 1) (y + z) + z

    至此,真相大白,y+z是整个环形圈,并不重要,我们可以知道的是当两个指针分别从头节点和相遇节点开始位移,终有时刻会相遇,而相遇时的位置即为入环的第一个节点。

    在这里插入图片描述

  2. 哈希,和前面的题目哈希思路基本一致,较为简单,不再写

完整C++代码如下

//1
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
            if (slow == fast) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2; // 返回环的入口
            }
        }
        return NULL;
    }
};


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值