【每天学习亿点点系列】——单链表OJ题

1.反转单链表

题目

在这里插入图片描述

方法一:原地改变原链表的指向

在这里插入图片描述

这种方法很容易理解,实现也比较方便,只需要用三个指针,分别为前一个位置,当前位置,后一个位置来完成就可以实现

代码实现

struct ListNode* reverseList(struct ListNode* head)
{
    // 改变原链表的指向
    struct ListNode* cur=head;
    if(head==NULL)
    {
        return head;
    }
    struct ListNode* nnext=head->next;
    struct ListNode* pre=NULL;
    while(cur)
    {
        cur->next=pre;
        pre=cur;
        cur=nnext;
        if(nnext!=NULL)
        {
            nnext=nnext->next;
        }
    }
    head=pre;
    return head;
}

方法二:头插法

图解头插法

![在这里插入图片描述](https://img-blog.csdnimg.cn/c65af58ea6dc4cf199f01329ab07d5d2.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0lhbUdyZWVIYW5k,size_16,color_FFFFFF,t_70

代码实现

struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* newhead = NULL;
    struct ListNode* cur = head;
    while(cur)
    {
        struct ListNode* next = cur->next;
        //头插新节点,更新头
        cur->next = newhead;
        newhead = cur;
        cur = next;
    }
    
    return newhead;
}

方法三:递归

在这里插入图片描述

代码实现

struct ListNode* reverseList(struct ListNode* head) {
    if (head == NULL || head->next == NULL) {
        return head;
    }
    struct ListNode* newHead = reverseList(head->next);
    head->next->next = head;
    head->next = NULL;
    return newHead;
}

2. 链表中间节点

题目

在这里插入图片描述

方法一:记录总共多少个节点,然后找到它的一半的那个节点

实现代码

struct ListNode* middleNode(struct ListNode* head)
{
//方法一:记录总共多少个节点,然后找到它的一半的那个节点
     int n=0;
     struct ListNode* tail=head;
     if(tail!=NULL)
     {
         n=0;
     }
     else
     {
         return NULL;
     }
     while(tail)
     {
         n++;
         tail=tail->next;
     }
     int middlenum=(n/2)+1;
     struct ListNode* middle=head;
     while(--middlenum)
     {
         middle=middle->next;
     }
     return middle;
 }

方法二:快慢指针

方法一这种方法容易想出,也不算太复杂,那有没有更快的方法了? 下面我们迎来了一种全新的方法快慢指针,在这道题目里面,我们让快指针一次走两步,慢指针一次走一步,当快指针走到尾节点时,慢指针此时恰好在中间节点,这个道理也很容易想明白,因为快指针走的速度是慢指针的两倍,所以当快指针到尾时,慢指针才会在中间。快慢指针这种全新的方法在很多其他题目里面也非常的好用

代码实现

  struct ListNode* slow=head;
      struct ListNode* quick=head;
      while(quick!=NULL&&quick->next!=NULL)
      {
          slow=slow->next;
          quick=quick->next->next;
      }
      return slow

3.链表中倒数第k个结点

题目

在这里插入图片描述

方法一:记录总共多少,然后输出倒数k个(悬疑问题)

这种方法也是受前一题中间节点的那种计数的方法影响,看看这边是否也可以,但写了一下发现总是过不了,代码感觉也没有问题,希望懂得小伙伴可以帮忙看看

代码实现

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    // write code here
    int n=0;
    struct ListNode* tail=pListHead;
    struct ListNode* ans=pListHead;
    while(tail)
    {
        n++;
        tail=tail->next;
    }
    if(k>n&&k<=0)
    {
          return NULL;
    }
    else
    {
     int find=n-k+1;
    while(--find)
    {
        ans=ans->next;
    }
    return ans;
    }
}

方法二,快慢指针

我们可以让快的指针先走k步,以此来达到此目的,所以说快慢指针在有些题中用过是很大的

代码实现

    struct ListNode*slow=pListHead;
    struct ListNode* fast=pListHead;
    while(k--)
    {
        if(fast)
        {
            fast=fast->next;
        }
        else
        {
            return NULL;
        }
    }
    while(fast)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return slow;

4.合并两个有序链表

题目

在这里插入图片描述

解题方法

在看完题目之后,我对这题大概就知道解法了,可以创建一个新的链表,然后以此挑选小的来当来,这里面要注意如果移动,以及一些边界情况

代码实现

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
      struct ListNode*newhead=NULL;
      struct ListNode* p1=l1;
      struct ListNode* p2=l2;
      struct ListNode*tail=NULL;
      if(p1==NULL&&p2==NULL)
      {
          return NULL;
      }
      else if(p1!=NULL&&p2==NULL)
      {
          return p1;
      }
      else if(p2!=NULL&&p1==NULL)
      {
          return p2;
      }
      if(p1->val<p2->val)
      {
          newhead=p1;
          p1=p1->next;
      }
      else
      {
          newhead=p2;
          p2=p2->next;
      }
      tail=newhead;
      while(p1&&p2)
      {
          if(p1->val<p2->val)
          {
              tail->next=p1;
              tail=p1;
              p1=p1->next;
          }
          else
          {
              tail->next=p2;
              tail=p2;
              p2=p2->next;
          }
      }
      if(p1==NULL)
      {
          tail->next=p2;
      }
      if(p2==NULL)
      {
          tail->next=p1;
      }
      return newhead;
}

5.链表分割

题目

在这里插入图片描述

解决方法

在看完题目后,我第一反应是建立一个新的链表,然后将小于x的尾插进去,然后想着想着,发现单单建立一个是不行的,要两个链表才可以,一个存放小于x的,一个存放大于x等于x的,但我在这小的过程中犯了一个很重要的错误,就是链表节点之间的指向,如果你分下来分,换句话说,你没有在一个while循环里面把大于等于x和小于x的元素分开,这样会导致元素之间的指向关系混乱,下面大家也可以看看这段代码,以此为戒。

Bug代码

        struct ListNode* newhead=NULL;
        struct ListNode* tail=NULL;
        struct ListNode* cur=pHead;
        struct ListNode* bighead = NULL;
	    struct ListNode* bigtail = NULL;
        while(cur)
        {
            if(cur->val<x)
            {
                newhead=cur;
                tail=newhead;
                break;
            }
            else
            {
                cur=cur->next;
            }
        }
        if(cur==NULL)
        {
            return pHead;
        }
        else
        {
            cur=cur->next;
            while(cur)
            {
                if(cur->val<x)
                {
                    tail->next=cur;
                    tail=cur;
                    cur=cur->next;
                }
                else
                {
                    cur=cur->next;
                }
            }
        }
        tail->next=NULL;
        cur=pHead;
        while(cur)
        {
            if(cur->val>x||cur->val==x)
            {
                bighead=cur;
                bigtail=bighead;
                break;
            }
            else
            {
                cur=cur->next;
            }
        }
        if(cur==NULL)
        {
            return pHead;
        }
        else
        {
               cur=cur->next;
                  while(cur)
              {
                   if(cur->val>x||cur->val==x)
                  {
                     bigtail->next=cur;
                     bigtail=cur;
                     cur=cur->next;
                  }
                  else
                  {
                     cur=cur->next;
                  }
              }
        }
    bigtail->next = NULL;
	tail->next = bighead;
    return newhead;

正确解决

        if(pHead == NULL)
            return NULL;
        
        struct ListNode* lessHead, *lessTail,*greaterHead, *greaterTail;
        //创建链表表头
        lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode));
        greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* cur = pHead;
        while(cur)
        {
            //小于x的尾插到lessTail
            if(cur->val < x)
            {
                lessTail->next = cur;
                lessTail = lessTail->next;
            }
            //大于等于x的尾插到greaterTail
            else
            {
                greaterTail->next = cur;
                greaterTail = greaterTail->next;
            }
            cur = cur->next;
        }
        //链接两个链表
        lessTail->next = greaterHead->next;
        greaterTail->next = NULL;
        //获取表头
        pHead = lessHead->next;
        free(lessHead);
        free(greaterHead);
        
        return pHead;

其实当我实在是修改不了那段bug代码后,看正确代码的时候,发现还是应该在写代码前把思路理顺,想想怎么写才是最优化的,是什么问题导致我这个bug的,我可不可以避开这个问题了?如果我在写的时候或者写之前把这些想明白,可能也就不会写出那段bug代码。

  • 16
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论
### 回答1: 一本通 OJ 库的测试数据,通常是用来验证提交的代码在各种情况下的正确性。测试数据可以分为两种类型,手动和自动。 手动测试数据是由目的出人根据意和数据范围设计的一组数据,用来检测程序的正确性和运行效率。手动测试数据的优点是能够涵盖各种情况,但缺点是数量相对较少,不足以覆盖所有可能的情况。 自动测试数据是由程序自动生成的一组数据,可以生成大量的数据以检测程序的健壮性和效率。自动测试数据的优点是数量大且可以自动生成,但缺点是可能无法覆盖某些特殊情况,导致漏洞。 对于提交的代码,一本通 OJ 库会对其进行编译和运行,然后与测试数据进行比较,判断代码的正确性和效率。如果代码通过了测试数据,就会被判定为正确,否则会被判定为错误,并给出具体的错误信息,供用户进行调试和改进。 综上所述,一本通 OJ 库的测试数据是一个重要的组成部分,它可以帮助用户测试代码的正确性和运行效率,提高用户的编程技能,同时也可以帮助出人设计更好的目,并保证目的质量和难度。 ### 回答2: 一本通 oj库是一个在线的程序设计竞赛平台,提供了丰富的编程目和测试数据。测试数据是用于对程序进行测评的输入和输出数据集合。在目描述中,会对问进行详细的解释和要求,并提供多组测试数据作为样例,让程序员运行他们的代码,并得到程序的输出结果。 测试数据通常包括正向测试数据和反向测试数据。正向测试数据是指符合目条件的测试数据,覆盖了大多数情况,测试程序是否正确;而反向测试数据则是用于测试程序是否能够正确处理异常情况。 在使用一本通 oj库时,程序员不仅需要通过编写算法和程序的方式解决问,还需要通过分析测试数据来判断自己的代码是否正确。而一本通 oj库的丰富数据集合为程序员提供了充足的测试数据,帮助程序员准确地检测代码中存在的漏洞和错误。 总之,一本通 oj库提供了全面的测试数据来测试程序员的代码是否满足目描述和要求,是程序员进行程序设计竞赛、算法练习和编程学习的良好平台。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一个数学不怎么好的程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值