LeetCode链表OJ题

本文介绍了LeetCode中的链表相关题目,如反转链表、查找倒数第k个节点、相交链表、合并有序数组、链表的中间结点、回文链表和环形链表的判断与操作,通过快慢指针等技巧给出了解决方案。
摘要由CSDN通过智能技术生成

目录

206. 反转链表 - 力扣(LeetCode)

​编辑

链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)

​编辑

链表分割_牛客题霸_牛客网 (nowcoder.com)

 《27. 移除元素 - 力扣(LeetCode)》

160. 相交链表 - 力扣(LeetCode)

88. 合并两个有序数组 - 力扣(LeetCode)

876. 链表的中间结点 - 力扣(LeetCode)

链表的回文结构__牛客网 (nowcoder.com)

141. 环形链表 - 力扣(LeetCode)

 142. 环形链表 II - 力扣(LeetCode)


第一种方法:

代码:


struct ListNode* reverseList(struct ListNode* head){
if(head==NULL)
{
    return NULL;
}
struct ListNode* tail=NULL;
struct ListNode* src=head;
struct ListNode* dst=src->next;

while(src)
{ 
    src->next=tail;
 tail=src; 
src=dst;
    if(dst)
dst=dst->next;
   


}
return tail;
}

思路:

 首先把tail放到NULL的位置,然后把src的next指向tail,然后把tail给给src,把src给给dst

然后再重复上一个步骤就可以实现第二个节点指向第一个节点 

然后重复即可。 

报错:

 修改:假如只有两个节点:

 那dst=dst->next要把dst跳到哪里呢?

所以我们要判断一下dst是否为空:

 报错:

 解析:这个就是head为空的问题,head如果为空,我们怎么连下一个节点呢,,没必要连了,所以判断一下,如果head为空,那么就什么都不返回。

第二种方法:头插法

思路是:创建一个cur,cur在head的位置上(cur=head),然后再创建一个next变量,这个变量在cur的下一个节点上。(next=cur->next)再创建一个空节点,然后让rhead这个变量等于NULL。(rhea=NULL;)

 然后我们把cur跟NULL连起来,(cur->next=rhead)再把rhead给cur。(cur=rhead)

 然后再把cur给给next。(next=cur);再让next跳到下一个节点上

 然后再重复上个步骤,再让cur指向rhead:

 依次类推,最终会实现逆转:

 代码:

struct ListNode* reverseList(struct ListNode* head){
if(head==NULL)
{
  return NULL;
}

struct ListNode* rhead=NULL;
struct ListNode* cur=head;


while(cur)
{

  struct ListNode* next=cur->next;

  cur->next=rhead;
rhead=cur;
cur=next;

}


return rhead;

}

要注意的两点:

第一点:传值

while(head)
{

  struct ListNode* next=cur->next;

  cur->next=rhead;
rhead=cur;
cur=next;

}

 像这里就会报错,因为我们传head进去,判断head是否会空,我们知道head是但是当第一次运算完之后这个节点就指向空了,

 第二次判断就直接退出循环了,所以我们要传cur。

第二点:返回值:

return cur

这里也会报错,因为我们最后要返回的是一个头结点,而cur最后会到最后那个节点 

 

 所以我们应该传rhead ,它才是头结点。

链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)

思路:快慢指针

slow,fast两个指针

让快指针先走k个节点,然后停下再让,slow指针开始走,然后fast指针再走,两个分别一人走一下,直到fast指针走完结束,此刻slow指针就是我们要找的倒数第k个节点

代码:

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
struct ListNode* fast= pListHead,* slow=pListHead;
  // write code here
while(k--)
{
  
fast=fast->next;
}
while(fast)
{ 
  slow=slow->next;
 fast=fast->next;
  
}


return slow;
}

报错:

根据下面的三个图我们可以看出问题所在: 

 

 从第三个案列开始,K都超过了链表的长度,然后预期输出里面都返回的空值,而我们的实际输出根本就输出不出来,因为我们没有设置如果k>链表长度则retuen NULL;这个条件。

 看第三个案列:

6{1,2,3,4,5}

 我们的链表总共就5个节点,fast走到第五个节点再往下走第六个节点就是NULL了

 所以我们只需要设置:

if(fast==NULL)
return NULL;

可运行的 代码:

链表分割_牛客题霸_牛客网 (nowcoder.com)

思路:尾插法

比如有一串数字:

 我们把小于9的放一个链表,大于9的放一个链表:

 因为是尾插,所以顺序不会变,然后我们把这两个链表链接起来。

 代码:

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
        struct ListNode* lesshead,*lesstail;
        struct ListNode* greathead,*greattail;
        lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
           greathead=greattail=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* cur=pHead;
if(pHead==NULL)
{
  return NULL;
}
           while(cur)
           {
            if(cur->val<x)
            {
              lesstail->next=cur;
              lesstail=lesstail->next;
            }
            else 
            {
           greattail->next=cur;
           greattail=greattail->next;
            }
            cur=cur->next;

            }
            lesstail->next=greathead->next;
            
            free(lesshead);
            free(greathead);
return pHead;
           

    }
};

 报错:

 我们自己输入一个测试用例然后运行,输出结果为空,就是输出不出来。

 

 然后我们再看报错提示:就是数组越界,堆栈溢出。

其实就是没有释放哨兵位:

我们应该把创建的

 struct ListNode* lesshead,*lesstail;
        struct ListNode* greathead,*greattail;

 释放掉。

虽然我们已经用free释放了,

  free(lesshead);
            free(greathead);

但是我们应该把哨兵位归为,把lesshead回到开头的位置。什么意思,就是把lesshead放到head前面,再释放它,对整个链表不影响。

head=lesshead->next;
  free(lesshead);
            free(greathead);

 去掉lesshead不影响链表 

 改完之后还有个报错:

 极端的错误:

要么全是小于x的

要么全是大于x的

要么有大有小的

我们自测输入,输入全是小于x的,正常运行

 输入全大于x的,正常运行

 

 输入有大有小的,

 报错了,根据实际输出可以看出来陷入了死循环。也就是

这个样子,我们的 greattail节点本来应该指向NULL,但是却指向了lesstail,就造成了死循环。

正常的应该是这个样子:

所以我们只需要把gretail的next指向NULL就可以了

greattail->next=NULL;

 

 通过代码:

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
        struct ListNode* lesshead,*lesstail;
        struct ListNode* greathead,*greattail;
        lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
           greathead=greattail=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* cur=pHead;
if(pHead==NULL)
{
  return NULL;
}
           while(cur)
           {
            if(cur->val<x)
            {
              lesstail->next=cur;
              lesstail=lesstail->next;
            }
            else 
            {
           greattail->next=cur;
           greattail=greattail->next;
            }
            cur=cur->next;

            }
            lesstail->next=greathead->next;
            pHead=lesshead->next;
            greattail->next=NULL;
            free(lesshead);
            free(greathead);
return pHead;
    }
};

 《27. 移除元素 - 力扣(LeetCode)

这道题怎么解:

用一个src和一个dec

​都从第一个数开始跑,但是src要先跑,并且dec不可以超过src。

接着判断src是否等于val的值,如果等于val,那么就只有src++,dec不动。如果src不等于val,那么src就赋值给dec,把dec的值覆盖掉,这样dec就只肯定不是val的值了,就可以保证前面一定不是val的值。然后再把src和dec都++,继续重复。

刚开始:

 

 

 只考虑前半段,因为前半段是有效的后半段是无效的,

 前面的{2,2}就是我们要的结果

 代码:

int removeElement(int* nums, int numsSize, int val){
int DEC=0;
int src=0;
while(src<numsSize)
{
   if(nums[src]!=val)
   {
      nums[DEC++]=nums[src++];
   }
   else
   {
      src++;
   }
}
return DEC;
}

160. 相交链表 - 力扣(LeetCode)

1.判断两个链表的尾节点是否相等,相等则相交。

但是时间复杂度太大,更简单一点的,我们向前再挪一点,判断c1是否相等不是可以少计算一点吗,时间复杂度也会小点。

那应该如何去判断呢?

 让src去遍历B链表的每个节点从B1直到c3节点 ,看是否与a1相等,如果不相等,src++,到a2头上,再去遍历B链表,直到src到c1头上时去遍历B链表,也遍历到了B链表的c1节点,发现相等,那就相交。

但是这个时间复杂度:src一次要遍历n个节点,O(N)。要遍历N遍,那就是O(N*N)

有个时间复杂度少一点的:谁长谁先走一步。

然后同时走,每走一个就对比一下节点是否为同一个节点,直到走到相同节点,那就相交。

 

 这个时间复杂度就是O(N).

这个代码报错了,为什么?

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {

    struct ListNode * longLlist=headA;
    struct ListNode * shortLlist=headB;
   

    
    if(shortLlist>longLlist)
    {
     
       shortLlist=shortLlist->next;
    } 
    while(longLlist->next&&shortLlist->next){
    if(shortLlist->next==longLlist->next)
    {
        return longLlist;
    }
    }
    return NULL;
    
}

第一,这里只定义了longLlist,shortLlist.但是没有实际意义,因为根据就没有去计算链表的长度。

我们应该让src一直走,每走一个len++,把A链表的长度计算出来,

B链表同理。

第2,不是谁长谁先走一步,而是谁长谁先长的步数,比如说下图:

 这让dst先走一步,再让src和dst同步走,两个也走不到一个节点上。只能让dst先走abs(dst-src)步,再让两者同步走。

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {

    struct ListNode * src=headA;
    struct ListNode * dst=headB;

    int lenA=1,lenB=1,len=1;
   while(src->next)
   {
       src=src->next;
       lenA++;
   }
 while(dst->next)
   {
      dst=dst->next;
       lenB++;
   }
   if(src!=dst)
   {
       return NULL;
   }
struct ListNode * longLlist= headA;
  struct ListNode * shortLlist=headB;
  int gap=0;
gap=abs(lenA-lenB);


    if(lenA<lenB)
    {
longLlist= headB;
  shortLlist=headA;
    } 


    while(gap--)
    {
            longLlist=longLlist->next;
    }
    
    while(longLlist!=shortLlist)
    {
       longLlist= longLlist->next;
       shortLlist=shortLlist->next;
    }
    
    
        return longLlist;
    

    
   
    
}

88. 合并两个有序数组 - 力扣(LeetCode)

思路:

这道题的意思是,A链表开辟m+n个空间,但是元素只有m个,B链表开辟n个空间,元素有n个

 

 然后按照升序排列把B链表合并过去,我们要的最终效果是这个样子:

 思路:

 1.设end1=m;

end2=n-1

i=m+n-1;

-1的原因是下标从0开始

 2.进行比较。

if     num1[end1]<nums2[end2]

这种情况的话就把nums2[end2]赋值给nums1[end2]

比如nums2[end2]=6,nums1[end1]=3,就把nums2[end2]复制给nums1[i];

 然后end2--,i--, end2和end1对比,end2大,执行nums1[i--]=nums2[end2--];

 end2和end1对比,end1大,执行  nums1[i--]=nums1[end1--];

然后执行end1--,i--,end2和end1对比,一样大,执行  nums1[i--]=nums2[end2--];
}

end2<0就结束了。

}

还有一种情况就是end1先走完。

 

 这种情况就是end1<0了,但是end2还大于0,但是还没完成合并,所以这里要单独写

while(end2>0)

就把nums2[end2]赋值给nums1[i]的位置

 结束

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){

int end1=m-1;
int end2=n-1;
int i=m+n-1;
while(end1>=0&&end2>=0)
{
    if(nums1[end1]>nums2[end2])
{
    nums1[i--]=nums1[end1--];
}
else
{
     nums1[i--]=nums2[end2--];
}

}
while(end2>=0)
{
    nums1[i--]=nums2[end2--];

}
}

876. 链表的中间结点 - 力扣(LeetCode)

思路:快慢指针

当链表为奇数链表时:

fast一次走两步,slow一次走一步,fast走完(fast=NULL),slow刚好走到中间 

当链表为偶数链表时,fast走超了一节点(fast->next->next=NULL),slow刚好走到中间:

struct ListNode* middleNode(struct ListNode* head){
struct ListNode* slow=head;
struct ListNode* fast=head;
while(fast&&fast->next)//fast!=NULL是奇数链表, fast->next不为空是偶数链表
{
    fast=fast->next->next;
    slow=slow->next;
}
return slow;
}

链表的回文结构__牛客网 (nowcoder.com)

思路:找到链表中间节点,876. 链表的中间结点 - 力扣(LeetCode)

从中间节点开始逆置206. 反转链表 - 力扣(LeetCode)

//反转函数
struct ListNode* middleNode(struct ListNode* head) {
  struct ListNode* slow = head;
  struct ListNode* fast = head;
  while (fast && fast->next) {
    fast = fast->next->next;
    slow = slow->next;
  }
  return slow;
}

//逆置函数
struct ListNode* reverseList(struct ListNode* head) {
  if (head == NULL) {
    return NULL;
  }
  struct ListNode* tail = NULL;
  struct ListNode* src = head;
  struct ListNode* dst = src->next;

  while (src) {
    src->next = tail;
    tail = src;
    src = dst;
    if (dst)
      dst = dst->next;
  }
  return tail;
}





//判断函数
class PalindromeList {
 public:
  bool chkPalindrome(ListNode* A) {
    // write code here

    ListNode* Pops=middleNode(A);
     ListNode* rmid=reverseList(Pops) ;
   while(rmid)
   {
    if(rmid->val!=A->val)//判断反转逆置完的第一个数的值是否相同
    {
      return false;
    }
    else//相同则判断下个节点的值是否相同
    {
      A=A->next;  
      rmid=rmid->next;
    }
   
   }
    return true;
  }
};

141. 环形链表 - 力扣(LeetCode)

首先,怎么判断是不是环形链表

1.我们可以通过快慢指针的方式,如果fast走到空就不是环形

2.如果fast->next-走到空就不是环形

上面这两种情况都不是环形,不是环形就返回false

 3.否则就是环形,是环形救直接返回true值吗,并不是,因为还要判断一下是否一个节点的next会回到前面的节点

快指针一次走两步,慢指针一次走一步,进入环里后,两个指针早晚会相遇。

这是因为假设fast和slow的距离为N(fast去追slow)

fast每次走两步,slow一次走1步,每同时走一次,fast和slow之间距离就缩小一步,也就是N-1.

再走一次就是N-2,最后会走到4,3,2,1,0,走到0的时候fast和slow就相遇了。

如果相遇,就返回true值

如果fast一次走3步,slow一次走1步,会相遇吗?

fast一次走3步,slow一次走一步,距离就缩小2步,N-2,第二次是N -4,最后会走成3,1,-1。

走到-1就错过了。

 如果错过了,fast和slow会继续走,fast会再次去追slow追几圈能追上不知道,能不能追上要看N-1减到最后是0还是-1,如果是0就追上了,如果是-1那就是没追上。

bool hasCycle(struct ListNode *head) {
    struct ListNode*  slow=head;
    struct ListNode* fast=head;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        
     
     if(slow==fast)
     {return true;
       
     }
     }
     return false;
}

 142. 环形链表 II - 力扣(LeetCode)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Rust 是一种现代的编程语言,特别适合处理内存安全和线程安全的代码。在 LeetCode 中,链表是经常出现的目练习类型,Rust 语言也是一种非常适合处理链表的语言。接下来,本文将从 Rust 语言的特点、链表的定义和操作,以及 Rust 在 LeetCode链表目的练习等几个方面进行介绍和讲解。 Rust 语言的特点: Rust 是一种现代化的高性能、系统级、功能强大的编程语言,旨在提高软件的可靠性和安全性。Rust 语言具有如下几个特点: 1. 内存安全性:Rust 语言支持内存安全性和原语级的并发,可以有效地预防内存泄漏,空悬指针以及数据竞争等问,保证程序的稳定性和可靠性。 2. 高性能:Rust 语言采用了“零成本抽象化”的设计思想,具有 C/C++ 等传统高性能语言的速度和效率。 3. 静态类型检查:Rust 语言支持静态类型检查,可以在编译时检查类型错误,避免一些运行时错误。 链表的定义和操作: 链表是一种数据结构,由一个个节点组成,每个节点保存着数据,并指向下一个节点。链表的定义和操作如下: 1. 定义:链表是由节点组成的数据结构,每个节点包含一个数据元素和一个指向下一个节点的指针。 2. 操作:链表的常用操作包括插入、删除、查找等,其中,插入操作主要包括在链表首尾插入节点和在指定位置插入节点等,删除操作主要包括删除链表首尾节点和删除指定位置节点等,查找操作主要包括根据数据元素查找节点和根据指针查找节点等。 Rust 在 LeetCode链表目的练习: 在 LeetCode 中,链表是常见的目类型,而 Rust 语言也是一个非常适合练习链表目的语言。在 Rust 中,我们可以定义结构体表示链表的节点,使用指针表示节点的指向关系,然后实现各种操作函数来处理链表操作。 例如,针对 LeetCode 中的链表目,我们可以用 Rust 语言来编写解法,例如,反转链表,合并两个有序链表,删除链表中的重复元素等等,这样可以更好地熟悉 Rust 语言的使用和链表的操作,提高算法和编程能力。 总之,在 Rust 中处理链表是非常方便和高效的,而 LeetCode 中的练习也是一个非常好的机会,让我们更好地掌握 Rust 语言和链表数据结构的知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孙鹏宇.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值