龙行哥做题leetcode K个升序链表的合并

解法一
解法一

 

解法二

这道题解法有很多,目前知道的最佳时间复杂度是N*logk, N表示所有链表节点总个数

解法一:建立一个size为k的最小堆,每次从最小堆中获取最小的元素,然后进行调整,每个元素会调整一次,最终复杂度是N*logk

解法二:将链表以2个为一组进行配对,为所有配对的两个链表进行合并,第一轮之后会得到k/2个有序链表,然后再进行第二轮第三轮,一直到链表个数合并为一个。

这种方法时间复杂度也是N*logk,因为每一轮合并时间复杂度都是N=L1+L2+......+Ln, 一共有logk轮

解法一算法步骤:

1.对k个链表进行处理,去掉空链表,得到一个有实际数据的链表,处理方法是将空链表全部移到数组尾部,用一个size变量表示实际有效大小;

然后进行特殊情况判断,即为空则返回,若只有一个链表,则返回该链表表头

2.基于上述链表的头节点建最小堆

2.1首先要熟悉堆数据结构,熟悉堆当前节点和其子节点的序号关系, 如果是从0开始表示,那么

left = (parent +1) * 2 - 1

right = left + 1

parent = int(son+1)/2)  - 1   左右子树通用  

建堆是从最后一个节点的母节点开始往前逐个调整

源码:

for (int i = (int)((size + 1)/2) - 1; i>=0; i--) {
          adjust_small_heap(lists, i, size-1);
 }

注意第二个参数是i,而不是0

2.2 堆调整函数,要点

2.2.1.一共三种情况,第一种当前节点分别有左右子树,只有左子树,没有子树

第二三种处理起来比较简单就不说了,第一种情况 需要先确认左右子树哪个数据比较小,记录下小的那个数的索引,再与当前节点比较,

这里记住是记录小的索引就行了,不要用布尔值去记录左右子树的大小,不然写起来会更复杂

3.获取堆顶元素作为新链表表头,之后进行各类条件判断处理,主要有两种情况

    3.1 头节点元素后移,该节点已经是链表的最后一个节点,需要将该链表从堆中移除,然后进行堆调整,堆删除操作具体步骤是,将堆第一个和最后一个元素进行替换,然后size-1

       3.1.1  进行删除操作后,堆大小(也就是有效链表数量)会变化,如果变为一,这个时候把这个链表补到新链表后面推出即可

       3.1.2  如果堆大小大于1,则继续往后

    3.2 该节点不是最后一个节点,则直接后移,然后进行堆调整

4.循环逐个获取下一个最小值,循环内的操作与三基本相同

解法一源码:

ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode *ret = NULL;
        int size = lists.size();
        int start = 0;
        while (size > start) {
            if (lists[start] == NULL) {
              swap(lists[start],lists[size - 1]);
              size--;
            } else {
                start++;
            }
        }
        if (size == 0) {
            return NULL;
        } else if (1 == size) {
            return lists[0];
        }
        for (int i = (int)((size + 1)/2) - 1; i>=0; i--) {
          adjust_small_heap(lists, i, size-1);
        }
        ret = lists[0];
        lists[0] = lists[0]->next;
        if (lists[0] == NULL) {
            swap(lists[0],lists[size - 1]);
            size--;
            if (1 == size) {
                ret->next = lists[0];
                return ret;
            }
        }
        adjust_small_heap(lists, 0, size-1);
        ListNode *cur_ret = ret;
        while (!lists.empty()) {
            cur_ret->next = lists[0];
            cur_ret = cur_ret->next;
            lists[0] = lists[0]->next;
            if (lists[0] == NULL) {
              swap(lists[0],lists[size-1]);
              size--;
              if (1 == size) {
                cur_ret->next = lists[0];
                return ret;
              }
            }
            adjust_small_heap(lists, 0, size-1);
        }
        return ret;
    }

void adjust_small_heap(vector<ListNode*> &heap, int start, int end) {
        int cur = start;
        while (cur <= end) {
            int left = (cur + 1) * 2 - 1;
            int right = left + 1;
            if (right <= end) {//有左右子树
                int swap_index = 0;
                if (heap[left]->val < heap[right]->val) {
                    swap_index = left;
                } else {
                    swap_index = right;
                }
                if (heap[cur]->val <= heap[swap_index]->val) {
                    return;
                } else {
                    swap(heap[cur],heap[swap_index]);
                    cur = swap_index;
                }
            } else if (left <= end) {//只有左子树
                if (heap[cur]->val > heap[left]->val) {
                    swap(heap[cur], heap[left]);
                }
                return;
            }
            else {//叶子节点
                return;
            }
        }
    }

解法2源码:

 ListNode* mergeKLists(vector<ListNode*>& lists) {
        int size = lists.size();
        if (size == 0) {
            return nullptr;
        }
        if (size == 1) {
            return lists[0];
        }
        queue<ListNode*> waiting(deque<ListNode*>(lists.begin(), lists.end())); //将vector转为队列
        //如果队列元素大于1,则取出两个进行合并,合并后的链表继续添加到链表尾部
        while (waiting.size() > 1) {
            ListNode *l1 = waiting.front();
            waiting.pop();
            ListNode *l2 = waiting.front();
            waiting.pop();
            waiting.push(merge2(l1, l2));
        } 
        return waiting.front();
    }
    ListNode* merge2(ListNode *l1, ListNode *l2) {
        ListNode *root = new ListNode(0), *pre = root;        
        while (l1 && l2) {
            if (l1->val <= l2->val) {
                pre->next = l1;
                l1 = l1->next;
            } else {
                pre->next = l2;              
                l2 = l2->next;
            }
            pre = pre->next;
        }
        pre->next = l1 ? l1 : l2;        
        return root->next;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值