*链表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 小时前
b树详细介绍与示例
数据结构·b树
开发小能手1239 小时前
答案daanC语言
数据结构·算法
启观川13 小时前
数据结构与算法-第 3 章 常用算法-贪心算法
数据结构·笔记·python·算法·贪心算法
程序员清风14 小时前
堆与优先队列:从 Top K 到任务调度
java·数据结构·算法
suhan14 小时前
【数据结构】四、串:串string、模式匹配BF\KMP、广义表
数据结构·后端·算法
啵啵啵鱼14 小时前
第二章 线性表
java·数据结构·算法
局部有解15 小时前
Hot100 - 三数之和
数据结构·c++·算法
辉艺棉麻小张15 小时前
用 JSON 建模面料规格:成分、克重、支数与公定回潮率字段设计
数据结构
longlongzihan15 小时前
C++ 常用数据结构速查:vector、stack、queue、list、树、图、哈希表等声明与核心操作
数据结构·c++·list
爱读书的小胖19 小时前
LeetCode 27. 移除元素(C)解题分析
数据结构·算法