leetcode 链表的合并和分割两道题

Posted Allen9012

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode 链表的合并和分割两道题相关的知识,希望对你有一定的参考价值。

1.合并两个有序链表

1.1 题目描述

本题目来源于leetcode 21.合并两个有序链表

提示:

1.2 接口函数

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){

}

1.3 大致框架

1.3.1 思路分析

  1. 新建立一个链表,即建立一个头结点

这里我们要有2个指针,分别都指空

  • 为什么要一个head,因为要返回头指针
  • 为什么要一个tail,因为要方便尾插

  1. 然后数组上下两两比较,小的就依次放在新链表

比较是用一个L1和一个L2指针分别指向原来两个链表的头节点

  1. l1指向下一个指针然后新链表的head不动,tail指针指向这一个节点,即叫做尾指针的意思
  2. 不断循环,最后当其中一个链表的L1或L2指针指向NULL时,就停止循环
  3. 最后将剩下来的一个链表连接在新链表后面就可以了

1.3.2 具体步骤

先要防止传进来的而指针是空的

 //注意第一次凡是有空指针也会崩
//如果为空直接返回另外一个就可以了
        if(l1==NULL)
        {
            return l2;
        }
        if(l2==NULL)
        {
            return l2;
        }

这里,头插最好不要放在while循环里面,在循环之前解决掉就可以了

 	   if(l1->val<l2->val)
        {
            head=tail=l1;
            l1=l1->next;
        }
        else
        {
            head=tail=l2;
            l2=l2->next;
        }

while循环不断走直到最后走到由一个指针NULL

 while(l1&&l2)
        {
             //取小的尾插到新链表
            if(l1->val<l2->val)
            {
                tail->next=l1;
                l1=l1->next;
            }
            else
            {
                tail->next=l2;
                l2=l2->next;
            }
            //tail走到新的位置上
             tail=tail->next;
        }

当由一个为空时把另外一个连到新链表上然后,返回头指针

	   //l2为空
        if(l1)
        {
            tail->next=l1;
        }
        //l1为空
        if(l2)
        {
            tail->next=l2;
        }
       return head;

1.4 具体实现

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
         //注意第一次凡是有空指针也会崩
        if(l1==NULL)
        {
            return l2;
        }
        if(l2==NULL)
        {
            return l1;
        }
        struct ListNode* head=NULL;
        struct ListNode* tail=NULL;
        //第一次不要放在循环里面
        if(l1->val<l2->val)
        {
            head=tail=l1;
            l1=l1->next;
        }
        else
        {
            head=tail=l2;
            l2=l2->next;
        }
        while(l1&&l2)
        {
             //取小的尾插到新链表
            if(l1->val<l2->val)
            {
                tail->next=l1;
                l1=l1->next;
            }
            else
            {
                tail->next=l2;
                l2=l2->next;
            }
            //tail走到新的位置上
             tail=tail->next;
        }
        //l2为空
        if(l1)
        {
            tail->next=l1;
        }
        //l1为空
        if(l2)
        {
            tail->next=l2;
        }
        return head;
}

1.5 方法选择

另一个带头节点法:

  1. 设置一个哨兵位头节点

  2. 一样的判断步骤,进行尾插

    这里不能忘记==tail->next=NULL;==避免两个都是空的

  3. 剩下的步骤同样道理,主要是少一点开始的判断步骤

  4. 注意最后要处理释放之前的节点

 //创建一个哨兵位头节点
        struct ListNode* head=NULL;
        struct ListNode* tail=NULL;
        head =tail=(struct ListNode*)malloc(sizeof(struct ListNode));
        tail->next=NULL;//防止两个都是空的,方便尾插
        while(l1&&l2)
        {
             //取小的尾插到新链表
            if(l1->val<l2->val)
            {
                tail->next=l1;
                l1=l1->next;
            }
            else
            {
                tail->next=l2;
                l2=l2->next;
            }
    
             tail=tail->next;
        }
        //l2为空
        if(l1)
        {
            tail->next=l1;
        }
        //l1为空
        if(l2)
        {
            tail->next=l2;
        }
        struct ListNode* node=head;
        head=head->next;
        free(node);
        return head;

2. 分割链表

2.1 题目描述

这是遇到的第一道难度为中等的题
本题目来源于leetcode 分割链表

提示:

2.2 接口函数

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* partition(struct ListNode* head, int x){

}

2.3 大致框架

假如给出这样的例子,虽说原题没有要求说顺序不变,但是这里用牛客网的要求做试试看

那么这时我们的答案应该是,因为要求了相对顺序是不能变的,所以最后输出就是这样

2.3.1 思路分析

把小于x的尾插到一个链表,再把大于x的尾插到一个链表,最后连接两个链表

  1. 创建两个哨兵位的头节点,分别创建对应的头尾指针

  1. 分别判断假如该节点的值小于给出的x,就尾插到小数链表,反之大的链表

  1. 当然尾指针要自动跟上,变成尾,走完后会发现大致顺序没有变化

  1. 最后来链接在一起就ok了,终止条件是原链表走完

2.3.2 具体步骤

按照第一次的思路按部就班地做

出现了问题,提示

struct ListNode* partition(struct ListNode* head, int x){
struct ListNode* lessHead,*lessTail,*greaterHead,*greaterTail;
    lessHead=lessTail=(struct ListNode*)malloc(sizeof(struct ListNode));
    greaterHead=greaterTail=(struct ListNode*)malloc(sizeof(struct ListNode));
    lessTail->next=NULL;
    greaterTail->next=NULL;
      struct ListNode*cur=head;
      while(cur)
      {
          if(cur->val<x)//只处理<的
          {
            lessTail->next=cur;
            lessTail=lessTail->next;
          }
          else
          {
              greaterTail->next=cur;
               greaterTail=greaterTail->next;
          }
                cur=cur->next;
     }
        //连接两个链表
            lessTail->next=greaterHead->next;
            head=lessHead->next;
            free(lessHead);
            free(greaterHead);
            return head;
}

这样说明出现问题,就要改进一下

一般时间超限原因是出现死循环

内存超限有时候是因为其他原因导致,这道题不是因为这里超过内存复杂度问题,应该想一下极端情况

这个情况是很难想到的,需要多多体会

我们将 greater 的 next指针置空,这是因为当前节点复用的是原链表的节点,而其 next指针可能指向一个小于 x 的节点,这样就会形成,我们需要切断这个引用。

正是因为死循环所以才会报错

解决方法就是把最后一个节点置空

 //关键
greaterTail->next=NULL;

2.4 具体实现

struct ListNode* partition(struct ListNode* head, int x){
   struct ListNode* lessHead,*lessTail,*greaterHead,*greaterTail;
    lessHead=lessTail=(struct ListNode*)malloc(sizeof(struct ListNode));
    greaterHead=greaterTail=(struct ListNode*)malloc(sizeof(struct ListNode));
    lessTail->next=NULL;
    greaterTail->next=NULL;
      struct ListNode*cur=head;
      while(cur)
      {
          if(cur->val<x)//只处理<的
          {
            lessTail->next=cur;
            lessTail=lessTail->next;
          }
          else
          {
            greaterTail->next=cur;
            greaterTail=greaterTail->next;
          }
          cur=cur->next;
     }
        //连接两个链表
            lessTail->next=greaterHead->next;
            //关键
            greaterTail->next=NULL;
            head=lessHead->next;
            free(lessHead);
            free(greaterHead);
            return head;
}

小结:

这道题其实还有多种解法,有兴趣可以看一下多题解

这篇题目就到此为止,大家觉得有收获就请一键三连,谢谢

以上是关于leetcode 链表的合并和分割两道题的主要内容,如果未能解决你的问题,请参考以下文章

数组2:合并有序数组的两道题

LeetCode Algorithm 21. 合并两个有序链表

LeetCode Algorithm 21. 合并两个有序链表

链表3:判断环形链表的两道高频题

数组3:与进制有关的两道题

leetCode第21题——合并两个有序链表