LeetCode 23

合并K个排序链表,没错。我的思路是分别从 K 个链表的中找出最小的一个值,然后依次插入链表,最后遍历完所有链表就好了,也没想中的那么难呀,循环遍历的思路如下: (先说明一下,它是不合格的

主要思路是:

1.首先从所有排序链表里找一个最小val节点作为头结点

2.依次循环在各个链表里面找到最小节点摘下来尾插,循环结束的条件是当链表的数目为0或者为1的时候结束(可以通过lists[i]==NULL来判断lists[i]代表的链表是否结束了),还编写了一个函数用来找最小元素下标的,每次返回的lists[i]的节点的val的值是"最小的"

3.但是这种做法有很多问题,当每个链表的长度都为1,但是总的数目很多的时候,效率太低,会有很多不必要的访问的(摘掉节点后的 lists 里面会有很多存放的是 NULL 指针),所以下面这种做法是无法达到时间限制的。

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {     
        if(lists.size() == 0)
        {
            return NULL;
        }
        int pos = findmin(lists);
        if(pos == -1)
        {
            return NULL;
        }
       
        ListNode *newHead=lists[pos];
        lists[pos] = lists[pos]->next;
        newHead->next = NULL;
        ListNode* cur = newHead;
        while(numOfList(lists)>1)
        {
            int pos = findmin(lists);
            if(pos == -1)
            {
                break;
            }
            cur->next = lists[pos];
            lists[pos] = lists[pos]->next;
            cur = cur->next;
            cur->next=NULL;
            EraseList(lists);
        }
        
        if( numOfList(lists) == 1)
        {
            int lastpos = findmin(lists);
            cur->next = lists[lastpos];
        }
        return newHead;
    }
    
    int numOfList(vector<ListNode*>&lists)
    {
        int num = 0;
        int len = lists.size();
        while(--len)
        {
            if(lists[len])
            {
                ++num;
            }
        }
        return num;
    }
    
    int findmin(vector<ListNode*>& lists)
    {
        int minval = 0;
        int minpos = -1;
        int j=0;
        for( ;j<lists.size();++j)
        {
            if(lists[j])
            {
                minval=lists[j]->val;
                minpos=j;
                break;
            }
        }
        for(int i=j+1;i<lists.size();++i)
        {
            if(lists[i] == NULL)
            {
                continue;
            }
            if(lists[i]->val < minval)
            {
                minval=lists[i]->val;
                minpos = i;
            }
        }
        return minpos;
    }

    void EraseList(vector<ListNode*>& lists)
    {
        int pos = 0;
        while(lists[pos] == NULL)
        {
            ++pos;
        }
        lists.erase(lists.begin(),lists.begin()+pos);
    }
    
};
-------------------写了好几个函数才实现的,居然效率不达标,那么这时候必须换种想法了-------------- 分治

对,我们可以通过分治的想法来实现,我们把大任务分为子任务,最后再汇总起来不就行了。所以我通过归并的思路去解题的,思路如下:

1.先把 k 个链表无限二等分并且递归进去

2.当区间的长度为1时,直接返回上一级,说明已经完成了这一趟的合并

3.当区间的长度为2时,合并这两个链表,在把结果返回上一级

4........最后,在把第一次分开的两个分支合并-------------------这就转化为了 k/2 次合并两个链表的思想(合并次数大于K/2)

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {
        /*
            合并k个排序链表
            极端情况是n个只有一个节点的链表
            所以不能循环的去做,我想到了归并排序的方法
        */
        
        ListNode *list1;
        ListNode *list2;
        //归并排序
        int len = lists.size();
        if(len == 0)
        {
            return NULL;
        }
        if(len == 1)
        {
            return lists[0];
        }
        
        int mid=len/2;
        domerge(list1,lists,0,mid);
        domerge(list2,lists,mid+1,len-1);
        ListNode *newHead = merge(list1,list2);
        return newHead;
    }   
    
    void domerge(ListNode*& list,vector<ListNode*>&lists,int begin,int end)
    {
        if(begin > end)
        {
            list=NULL;
        }
        if(begin == end) 
        {//返回
            list=lists[begin];
        }
        
        if(end-begin == 1)
        {//合并
            list=merge(lists[begin],lists[end]);  
        }
        
        if(end-begin >1)
        {//继续二分
            int mid = begin+(end-begin)/2;
            ListNode *l1;
            ListNode *l2;
            domerge(l1,lists,begin,mid);
            domerge(l2,lists,mid+1,end);
            list=merge(l1,l2);
        }
    }
    
    ListNode *merge(ListNode *l1,ListNode *l2)
    {
        if(l1 == NULL && l2==NULL)
        {
            return NULL;
        }
        if(l1 == NULL)
        {
            return l2;
        }
        if(l2 == NULL)
        {
            return l1;
        }
        ListNode *ret = NULL;
        if(l1->val <= l2->val)
        {
            ret = l1;
            l1 = l1->next;
        }
        else
        {
            ret = l2;
            l2 = l2->next;
        }
        ret->next = NULL;
        ListNode* cur = ret;
        while(l1 && l2)
        {
            if(l1->val <= l2->val)
            {
                cur->next = l1;
                l1 = l1->next;
                cur=cur->next;
            }
            else
            {
                cur->next = l2;
                l2 = l2->next;
                cur = cur->next;
            }
            cur->next = NULL;
        }
        if(l1)
        {
            cur->next = l1;
        }
        if(l2)
        {
            cur->next = l2;
        }
        return ret;
    }
};
结果是同过所有测试用例,包括第一种解法遇到的问题





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值