*链表OJ

1.返回倒数第k个节点

https://leetcode.cn/problems/kth-node-from-end-of-list-lcci/

要求空间复杂度为O(1)

思路:利用快慢指针,让两个指针拉开k的差距。

如下图,当快指针指向最后一个位置时,慢指针指向的就是我们要找的倒数第k个节点。

cs 复制代码
 typedef struct ListNode ListNode;
int kthToLast(struct ListNode* head, int k) {
  ListNode* slow=head,* fast=head;
  while(k--)
  {
    fast=fast->next;
  }
  while(fast)
  {
    slow=slow->next;
    fast=fast->next;
  }
  return slow->val;
}

2.链表的回文结构

https://leetcode.cn/problems/palindrome-linked-list-lcci/

思路

1.找到中间节点

2.后半段逆置,分别与前面的数依次作比较即可。

如下图,

我们在前面学习过查找中间节点反转(逆置)链表,还不是很熟悉这两种经典算法的同学可以回顾一下前面的内容:

https://blog.csdn.net/gumidc/article/details/160154798?spm=1001.2014.3001.5501

cs 复制代码
typedef struct ListNode ListNode;
ListNode* middleNode(ListNode* head) {
    ListNode* fast, * slow;
    fast = slow = head;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}
ListNode* reverseList(ListNode* head) {
    ListNode* n1, * n2, * n3;
    //判空
    if (head == NULL)
        return head;
    n1 = NULL, n2 = head, n3 = n2->next;
    while (n2)
    {
        n2->next = n1;
        n1 = n2;
        n2 = n3;
        if (n3)
            n3 = n3->next;
    }
    return n1;
}
bool isPalindrome(ListNode* head) {
    ListNode* mid=middleNode(head);
    ListNode* rmid=reverseList(mid);
   while(rmid&&head)
  {
     if(head->val!=rmid->val)
        return false;

        rmid=rmid->next;
        head=head->next;
  }
  return true;
}

3.相交链表

https://leetcode.cn/problems/3u1WK4/

思路

1.**判断是否相交:只需要判断A与B的最后一个节点是否相同,注意要用地址**进行判断

**2.**返回相交的起始节点:记录两链表的长度,长的先走差距步,然后同时走,出现第一个相同的节点就是交点。


*假设法

**假设A长B短,错误的话修正一下即可。**如下:


cs 复制代码
typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    int lenA=1,lenB=1;
    ListNode* curA=headA,*curB=headB;
    while(curA)
    {
        curA=curA->next;
        lenA++;
    }
     while(curB)
    {
        curB=curB->next;
        lenB++;
    }
    if(curA!=curB)
    {
        return NULL;
    }
    else
    {
        int gap=abs(lenA-lenB);
        ListNode* longlist=headA,*shortlist=headB;
       if(lenA<lenB)
       {
        longlist=headB;
        shortlist=headA;
       }
       while(gap--)
       {
        longlist=longlist->next;
       }
       while(longlist!=shortlist)
       {
        longlist=longlist->next;
        shortlist=shortlist->next;
       }
       return longlist;
    }
}

4.环形链表

https://leetcode.cn/problems/linked-list-cycle/

思路:创建一对快慢指针,fast一定先进环,slow进环后,fast就开始追击slow,如果能追上,那么链表就带环。

cs 复制代码
typedef struct ListNode ListNode; 
bool hasCycle(struct ListNode *head) {
    ListNode* fast=head,*slow=head;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            return true;
        }
    }
    return false;
}

这里我们思考几个问题:

1.为什么一定会相遇,没有可能错过,永远追不上?

如下图:

2.slow一次走一步,fast走三步,四步,N步可以吗?

推理如下:

slow走一步,fast走三步时,有三种情况:

然后再对应讨论C-2或C-1分别为偶数或奇数的情况。

但是,真的会存在永远追不上的情况吗?

注意,根据上述推理分析过程,只有同时存在C是偶数且N是奇数时才会永远追不上

我们画图来进行分析:

所以我们最开始的推理是有问题的,最终正确的结论是:不管fast走几步都一定能追上slow

5.环形链表II

https://leetcode.cn/problems/linked-list-cycle-ii/

思路:用快慢指针来解决这个问题。

当slow与fast相遇时,我们定义相遇的节点为meet,再让head与meet以相同速度同步向前走,二者首次相遇的节点即为入环的第一个节点。

为什么?我们来推理一下:

最后我们将L的表达式写为(x+1)*C+C-N的形式,其中:

·(x-1)*C表示相遇点绕环x-1圈后,仍会回到相遇点meet

**·**C-N刚好是meet走到入环节点的距离

这说明:head与meet到入环点的距离等价,二者同步前进,首次相遇的位置必然是入环的第一个节点

cs 复制代码
 typedef struct ListNode ListNode; 
struct ListNode *detectCycle(struct ListNode *head) {
    ListNode* fast=head,*slow=head;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            ListNode* meet=slow;
            while(meet!=head)
            {
                meet=meet->next;
                head=head->next;
            }
           return meet;
        }
    }
    return NULL;
}

6.随机链表的复制

https://leetcode.cn/problems/copy-list-with-random-pointer/

这道题的难点在于处理random指针,直接复制节点会导致random指向原链表节点,无法实现真正的深拷贝

思路

**1.**把拷贝节点插入到原节点的后面,让二者建立关联关系

**2.**处理拷贝节点的random指针

**3.**将拷贝的链表从原链表上摘下来。

第一步:插入拷贝节点

如下图:

注意:要先让copy的next指向原节点的下一个节点,再修改原节点的next,否则会导致后续链表丢失。

第二步:设置random

· 如果random为,拷贝节点的random也设为空

· 如果random不为空,拷贝节点的random就等于cur->random->next,如下图:

第三步:把拷贝节点取下来尾插到新链表中,然后恢复原链表(不恢复也可以)

代码如下:

cs 复制代码
typedef struct Node Node;
struct Node* copyRandomList(struct Node* head) {
    Node* cur=head;
    while(cur)
    {
        Node* copy=(Node*)malloc(sizeof(Node));
        copy->val=cur->val;

        copy->next=cur->next;
        cur->next=copy;

        cur=copy->next;
    }
//控制random
      cur=head;
    while(cur)
{
   Node* copy=cur->next;
    if(cur->random==NULL)
   {
      copy->random=NULL;
   }
    else
   {
      copy->random=cur->random->next;
   }
      cur=copy->next;
 }
//尾插
cur=head;
Node* copyHead=NULL,*copyTail=NULL;
while(cur)
{
   Node* copy=cur->next;
   Node* next=copy->next;
    if(copyTail==NULL)
  {
    copyHead=copyTail=copy;
  }
    else
  {
    copyTail->next=copy;
    copyTail=copy;
  }
   cur->next=next;
   cur=next;
}
return copyHead;
}
相关推荐
如君愿2 小时前
考研复习 Day 21 | 数据结构与算法--排序(上)
数据结构·考研·排序算法·记录考研
wsoz2 小时前
Leetcode链表-day9
c++·算法·leetcode·链表
hnjzsyjyj2 小时前
全排列问题DFS实现执行示意图
数据结构·dfs
故事和你913 小时前
洛谷-算法2-2-常见优化技巧3
开发语言·数据结构·c++·算法·深度优先·动态规划·图论
菜鸟555553 小时前
2025江西省CCPC省赛暨全国邀请赛(南昌)
数据结构·c++·算法·acm·思维·ccpc·xcpc
6Hzlia4 小时前
【Hot 100 刷题计划】 LeetCode 21. 合并两个有序链表 | C++ 经典迭代与 Dummy 技巧
c++·leetcode·链表
꧁细听勿语情꧂4 小时前
用队列实现栈、用栈实现队列,树、二叉树、满二叉树、完全二叉树,堆、向下向上调整算法、出堆入堆、堆排序
c语言·开发语言·数据结构·算法
周末也要写八哥4 小时前
什么是快速选择及案例分析
数据结构
Felven4 小时前
B. Make Almost Equal With Mod
数据结构·算法