LeetCode-23.合并 k 个排序链表 [C语言]

合并 个排序链表,返回合并后的排序链表。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

1.最简单的思路,每次从k个链表中选择最小,加入目标链表中。简单分析一下,每次的比较选择,需要k-1次,则是O(k)的时间复杂度,假设一共有n个节点,则总的时间复杂度为O(nk)。代码如下:

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

struct ListNode* mergeKLists(struct ListNode** lists, int listsSize)
{
    //进行k路比较
    struct ListNode** p=lists;
    int i=0,min=-1,j=0;      //min此次比较中最小的结点链表序号
    struct ListNode* head,*tail,*pmin;
    head=(struct ListNode*)malloc(sizeof(struct ListNode));
    head->next=NULL;     //构造表头结点易于对第一个节点的操作
    tail=head;           //尾结点,采用尾插法


    int num=0;  //用于指示已经有多少个空列表
    pmin=NULL;
    min=-1;
    while(num<listsSize)  //num=listsSize则说明k个链表都寻找完了
    {
        for(j=0,num=0; j<listsSize && lists[j]==NULL ; j++)
            num++;       //用于找到k个链表目前第一个不为空的值
        if(j<listsSize)
        {
            pmin=lists[j];
            min=j;
        }
        //printf("%d %d \n",lists[min]->val,min);

        for(i=j+1; i<listsSize; i++)          //进行比较找出最小的值和最小值的指针
        {

            if(lists[i]!=NULL && lists[i]->val < pmin->val)
            {
                min=i;
                pmin=lists[i];

            }
            else if(lists[i]==NULL)
            {
                num++;
                //printf("%d \n",i);
            }
            else
                ;
        }

        if(num<listsSize )           //将找到的结点插入链表,并对取出的结点的链表更新
        {
            lists[min]=lists[min]->next;

            pmin->next=tail->next;
            tail->next=pmin;
            tail=pmin;
        }
        else if(min==-1)
        {
            return head->next;
        }

    }
    return head->next;

}

测试结果,很显然,很耗时,看到下面时间(抱住自己,怎么滴,也得再搞一种更优更优的算法)如图:

2.我们再分析一下,如果采用两两合并,并归的思路,对于k链表,我们可以通过递归来二分下去,最后通过合并两个有序链表完成排序,时间复杂度\mathbf{}O\left (n\cdot log_{2}k \right )\textup{},代码如下:

 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* MergeTwo(struct ListNode* list1,struct ListNode* list2)
{   
    //合并两个链表
    if(list1==NULL) return list2;
    if(list2==NULL) return list1;
    
    if(list1->val<list2->val)
    {
        list1->next=MergeTwo(list1->next,list2);
        return list1;
    }
    else
    {
        list2->next=MergeTwo(list1,list2->next);
        return list2;        
    }        
}

struct ListNode* mergeKLists(struct ListNode** lists, int listsSize)
    
{
    //k路并归思路
    struct ListNode* list1,*list2,*l;
    if( listsSize==0 )
        return NULL;
    else if
        (listsSize==1)    //退出递归的两种特殊情况
        return *lists;
    else
    {
        list1=mergeKLists(lists,listsSize/2);
        list2=mergeKLists(&(lists[listsSize/2]),listsSize-listsSize/2); //注意取地址
        l=MergeTwo(list1,list2);
        return l;
    }    
}

显然优于第一个算法,再一次证明选择好的算法思想的重要性!!!

不说了继续搬砖了!

                                                                                                                                                                                   2019.8.19-晚

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值