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

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

  • 1.反转单链表
    • 题目
    • 方法一:原地改变原链表的指向
      • 代码实现
    • 方法二:头插法
      • 图解头插法
      • 代码实现
    • 方法三:递归
      • 代码实现
  • 2. 链表中间节点
    • 题目
    • 方法一:记录总共多少个节点,然后找到它的一半的那个节点
      • 实现代码
    • 方法二:快慢指针
      • 代码实现
  • 3.链表中倒数第k个结点
    • 题目
    • 方法一:记录总共多少,然后输出倒数k个(悬疑问题)
      • 代码实现
    • 方法二,快慢指针
      • 代码实现
  • 4.合并两个有序链表
    • 题目
    • 解题方法
      • 代码实现
  • 5.链表分割
    • 题目
    • 解决方法
      • Bug代码
      • 正确解决

1.反转单链表

题目

【每天学习亿点点系列】——单链表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;
}

方法二:头插法

图解头插法

【每天学习亿点点系列】——单链表OJ题_第2张图片

代码实现

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;
}

方法三:递归

【每天学习亿点点系列】——单链表OJ题_第3张图片

代码实现

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. 链表中间节点

题目

【每天学习亿点点系列】——单链表OJ题_第4张图片

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

实现代码

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个结点

题目

【每天学习亿点点系列】——单链表OJ题_第5张图片

方法一:记录总共多少,然后输出倒数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.合并两个有序链表

题目

【每天学习亿点点系列】——单链表OJ题_第6张图片

解题方法

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

代码实现

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.链表分割

题目

【每天学习亿点点系列】——单链表OJ题_第7张图片

解决方法

在看完题目后,我第一反应是建立一个新的链表,然后将小于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代码。

你可能感兴趣的:(每天学习亿点点系列,leetcode,链表,数据结构,leetcode,指针,bug)