*链表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;
}
相关推荐
wuyk55514 小时前
3.链表:用指针串联的动态数据结构
c语言·开发语言·数据结构·链表
郝学胜-神的一滴14 小时前
干货版《算法导论》17:二叉树核心原理、遍历逻辑与高阶实操全解
数据结构·c++·python·算法·计算机·编程
疯狂打码的少年15 小时前
【数据结构】二叉树的性质(五大性质+计算)
数据结构·笔记·算法
wabs66616 小时前
关于图论【A*算法 | 卡码网127.骑士的攻击的思考】
数据结构·算法·图论·卡码网·广搜的改进版
凉茶钱16 小时前
【数据结构】堆的应用
c语言·数据结构
间歇性努力持续性发呆的野生快乐选手17 小时前
栈的性质(进栈,出栈,访问)
数据结构·c++
m0_5474866617 小时前
《数据结构与算法》全套PPT课件2026(中国海洋大学)
数据结构·算法
旖旎夜光18 小时前
LeetCode 904:水果成篮(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
我变成萤火虫20 小时前
河南萌新联赛2026第(三)场:郑州轻工业大学
数据结构·c++·算法·贪心算法·stl·深度优先·哈希算法
白狐_79820 小时前
408数据结构第7章:B树①——基础概念与结构
数据结构·b树