合并k个已排序的链表

简介: 合并k个已排序的链表

ojbk...开始以为这题没有内存要求,所以就用来一个很简单的方法合并。创建第三条链表,结果部分案例过不去。

这个代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergetK(ListNode *A,ListNode *B)
    {
        ListNode *C = nullptr;
        ListNode *ans = nullptr;
        while(A != nullptr && B != nullptr)
        {
            ListNode *s = new ListNode(0);
            if(A->val > B->val)
            {
                s->val = B->val;
                s->next = nullptr;
                B = B->next;
            }
            else
            {
                s->val = A->val;
                s->next = nullptr;
                A = A->next;
            }
            if(C == nullptr)
            {
                C = s;
                ans = C;
            }
            else
            {
                C->next = s;
                C = C->next;
            }
        }
        while(A != nullptr)
        {
            ListNode *s = new ListNode(0);
            s->val = A->val;
            s->next = nullptr;
            if(C == nullptr)
            {
                C = s;
                ans = C;
            }
            else
            {
                C->next = s;
                C = C->next;
            }
            A = A->next;
        }
        while(B != nullptr)
        {
            ListNode *s = new ListNode(0);
            s->val = B->val;
            s->next = nullptr;
            if(C == nullptr)
            {
                C = s;
                ans = C;
            }
            else
            {
                C->next = s;
                C = C->next;
            }
            B = B->next;
        }
        return ans;
    }
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        if(lists.empty())
        {
            return nullptr;
        }
        ListNode *ans;
        ans = lists[0];
        for(int i=1;i<lists.size();i++)
        {
           ans = mergetK(ans,lists[i]);
        }
        return ans;
    }
};

好吧,看来不能创建新的节点,只能用辅助指针了。。。。。(这题就是合并两个有序链表,水题)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergetK(ListNode *A,ListNode *B)
    {
        ListNode *pA = A;
        ListNode *pB = B;
        ListNode *ans = nullptr;
        ListNode *head = nullptr;
        while(A != nullptr && B != nullptr)
        {
           if(A->val <= B->val)
           {
               if(ans == nullptr)
               {
                   ans = A;
                   head = A;
               }
               else
               {
                   head->next = A;
                   head = head->next;
               }
               //pA = A;
               A = A->next; 
           }
           else
           {
               if(ans == nullptr)
               {
                   ans = B;
                   head = B;
               }
                else
               {
                   head->next = B;
                   head = head->next;
               }
               //pB = B;
               B = B->next;
           }
        }
        if(A != nullptr)
        {
            head->next = A;
        }
        if(B != nullptr)
        {
            head->next = B;
        }
        return ans;
    }
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        if(lists.empty())
        {
            return nullptr;
        }
        ListNode *ans;
        ans = lists[0];
        for(int i=1;i<lists.size();i++)
        {
           if(lists[i] == nullptr)
           {
               continue;
           }
           ans = mergetK(ans,lists[i]);
        }
        return ans;
    }
};

我去,超时.....

看来这题没有想的那么简单呀....

分析下代码的时间复杂度,,,,

遍历 litsts  O(n)

内部:等于遍历了一遍 链表  O(n+m)

这么算:O(n^2)

看题目要求的是时间复杂度   O(nlogk)

k是链表的个数:说明只能从 遍历 litsts开🔪了。。。。

根据经验,合并两个有序链表时间复杂度最优 O(n+m) 再一次说明  只能从 遍历 litsts优化了

等等,logK对数时间,第一反应应该是二分吧。我去,说不定可以解决

二分的前提:保证有序,nice,题目的条件刚好是有序的(因为数组下标是递增的呀)

优化后:

我操,终于过了。。。。。感动,落泪。一个小时呜呜....

两个链表的合并:(前面的代码有点小bug,有一种情况需要特殊处理)

当一个链表尾空时:(此时应该不需要合并)

[{-9},{},{-10,-8,-2,-1,2},{-10,-9,-7,-6,-5,-2,-2,1,4},{-7,-7,-1,0,4},{},{-4,0},{-9,-6,-5,-5,-2,2,3,3}]

更改:做个特判,两个有序链表的合并就不说了,只要你认真一点就一定能写出来。

合并两个有序链表代码:

struct ListNode 
{
     int val;
     ListNode *next;
     ListNode(int x) : val(x), next(NULL) {}
};
ListNode* merge(ListNode *A,ListNode *B)
{
    //特判 (A=nullptr && B=nullptr) 或者
    // (A=nullptr && B!=nullptr) 或者 (A!=nullptr && B=nullptr)
    if(A == nullptr || B == nullptr)
    {
        return A==nullptr ? B:A;
    }
    ListNode *head = nullptr;
    ListNode *ans  = nullptr;
    while(A != nullptr && B != nullptr)
    {
        if(A->val <= B->val)
        {
            if(ans == nullptr)
            {
                ans = A;
                head = A;
            }
            else
            {
                head->next = A;
                head = head->next;
            }
            A = A->next;
        }
        else
        {
            if(ans == nullptr)
            {
                ans = B;
                head = B;
            }
            else
            {
                head->next = B;
                head = head->next;
            }
            B = B->next;
        }
    }
    if(A != nullptr)
    {
        head->next = A;
    }
    if(B != nullptr)
    {
        head->next = B;
    }
    retrun ans;
}

二分的思路及代码:

对于二分有两种方法:递归(推荐使用)   非递归

通常我们使用递归就行,好分析,好写代码

我们先看一个例子来分析:

       先分成两半: A和B的处理过程一定要是一样的(或者说将一个整体划分为两个与这个整体相同的子问题)

       假设将A和B成最小的子问题了,此时我们应该干嘛????

       当然是进行链表合并咯。。。。

       好的我们来合并吧,又看,A和B还没划分到最小呀。(递归是一个自顶向下的过程)

       为什么我这里说要把A和B看作最小的子问题呢??这个是作者对递归的特别理解吧,如果画出解空间,递归是不是需要回溯,当我们回溯到这一层的时候是不是需要合并 A和B.

       ok....为了方便理解我们不妨就把每次状态都当作最小问题,对于最小问题的求解什么样的,我们就怎么处理就行了,而且本来就数划分为与子问题一样的问题吗,只是规模遍历而已。

       我们继续划分

       

 

  我们写代码吧(二分代码)

//返回值看我上面画的图
ListNode* binary_lists(vector<ListNode *> &lists,int l,int r)
{
    //先写边界条件(出口)
    if(l == r)
    {
        return lists[l];
    }
    if(l > r)
    {
        return nullptr;
    }   
    int mid = (l+r)/2;
    //合并 A和B    因为回溯到这个点的时候需要合并A和B
    return merge(binary_lists(lists,l,mid),binary_lists(lists,mid+1,r));
}

okk...

我们把所有的代码合在一起吧

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *ans1;
    ListNode* merget(ListNode *A,ListNode *B)
    {
        if(A == nullptr || B == nullptr)
        {
            return A == nullptr ? B:A;
        }
        ListNode *pA = A;
        ListNode *pB = B;
        ListNode *ans = nullptr;
        ListNode *head = nullptr;
        while(A != nullptr && B != nullptr)
        {
           if(A->val <= B->val)
           {
               if(ans == nullptr)
               {
                   ans = A;
                   head = A;
               }
               else
               {
                   head->next = A;
                   head = head->next;
               }
               //pA = A;
               A = A->next; 
           }
           else
           {
               if(ans == nullptr)
               {
                   ans = B;
                   head = B;
               }
                else
               {
                   head->next = B;
                   head = head->next;
               }
               //pB = B;
               B = B->next;
           }
        }
        if(A != nullptr)
        {
            head->next = A;
        }
        if(B != nullptr)
        {
            head->next = B;
        }
        return ans;
    }
    ListNode* merList(vector<ListNode *> &lists,int l,int r)
    {
        if(l == r)    //只有一条
        {
            return lists[l];
        }
        if( l > r)
        {
            return nullptr;
        }
        int mid = (l + r)/2;
        return merget(merList(lists,l,mid),merList(lists,mid+1,r));
    }
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        if(lists.empty())
        {
            return nullptr;
        }
//       ans = lists[0];
//         for(int i=1;i<lists.size();i++)
//         {
//            if(lists[i] == nullptr)
//            {
//                continue;
//            }
//            ans = mergetK(ans,lists[i]);
//         }        
        return merList(lists,0,lists.size()-1);
    }
};

 

  小结:创作不易,点个关注支持一下吧

  如果有喜欢C++的同学可以观看作者的其他专栏,全套C++都在更新(从C++基础到Linux系  统编程、网络编程 数据编程等等... 并且有一个完整C++项目)

相关文章
|
1月前
(剑指offer)18、删除链表的节点—22、链表中倒数第K个节点—25、合并两个排序的链表—52、两个链表的第一个公共节点(2021.12.07)
(剑指offer)18、删除链表的节点—22、链表中倒数第K个节点—25、合并两个排序的链表—52、两个链表的第一个公共节点(2021.12.07)
49 0
|
1月前
|
算法
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
31 0
|
3月前
|
存储 算法
LeetCode第83题删除排序链表中的重复元素
文章介绍了LeetCode第83题"删除排序链表中的重复元素"的解法,使用双指针技术在原链表上原地删除重复元素,提供了一种时间和空间效率都较高的解决方案。
LeetCode第83题删除排序链表中的重复元素
|
3月前
|
存储 算法 Java
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
45 0
|
5月前
23.合并K个升序链表
23.合并K个升序链表
|
5月前
|
存储 SQL 算法
LeetCode 83题:删除排序链表中的重复元素【面试】
LeetCode 83题:删除排序链表中的重复元素【面试】
|
5月前
|
存储 SQL 算法
LeetCode 题目 82:删除排序链表中的重复元素 II
LeetCode 题目 82:删除排序链表中的重复元素 II
|
5月前
|
存储 算法 数据挖掘
Leetcode二十三题:合并K个升序链表【22/1000 python】
Leetcode二十三题:合并K个升序链表【22/1000 python】
|
6月前
【移除链表元素】LeetCode第203题讲解
【移除链表元素】LeetCode第203题讲解
|
5月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表