合并2(K)个链表、排序链表

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        //迭代写法
        ListNode* dummy = new ListNode(0);
        ListNode* head = dummy;
        if(list1 == nullptr) return list2;
        if(list2 == nullptr) return list1;
        while(list1 != nullptr && list2 != nullptr){
            if(list1->val > list2->val){
                dummy->next = list2;
                list2 = list2->next;
            }else{
                dummy->next = list1;
                list1 = list1->next;
            }
            dummy = dummy->next;
        }
        if(list1 == nullptr){
            dummy->next = list2;
        }else{
            dummy->next = list1;
        }
        return head->next;
    }
};

今天复习的是合并链表系列,合并2个链表简单题不再赘述。

有一个非常唐的做法就是依次两两合并K个链表。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int len = lists.size();
        if(len == 0) return nullptr;
        if(len == 1) return lists[0];
        ListNode* curr = new ListNode(0);
        curr = lists[0];
        for(int i = 1; i<len;i++){
            curr = mergeTwoLists(curr,lists[i]);
        }
        return curr;
    }

    ListNode* mergeTwoLists(ListNode* l1,ListNode* l2) {
        if(l1 == nullptr) return l2;
        if(l2 == nullptr) return l1;
        if(l1->val <= l2->val){
            l1->next = mergeTwoLists(l1->next,l2);
            return l1;
        }
        else{
            l2->next = mergeTwoLists(l1,l2->next);
            return l2;
        }
    }
};

然而我们可以用分治来优化上面这种解法:

cpp 复制代码
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        return MergeKLists(lists, 0, lists.size()-1);
    }

    ListNode* MergeKLists(vector<ListNode*>& lists, int l, int r)
    {
        if (l > r) return nullptr;
        if (l == r) return lists[l];
        int mid = l + (r - l) / 2;
        auto left = MergeKLists(lists, l, mid);
        auto right = MergeKLists(lists, mid+1, r);
        return MergeTwoLists(left, right);
    }

    ListNode* MergeTwoLists(ListNode* l1, ListNode* l2)
    {
        ListNode* dummy = new ListNode();
        ListNode* cur = dummy;
        while (l1 != nullptr && l2 != nullptr) {
            if (l1->val < l2->val) {
                cur->next = l1;
                l1 = l1->next;
            } else {
                cur->next = l2;
                l2 = l2->next;
            }
            cur = cur->next;
        }

        cur->next = l1 != nullptr ? l1 : l2;
        return dummy->next;
    }
};

这个是把O(n)优化成了O(log2n),一个一个合并是顺序遍历,分治来做的话让他自己内部合并,而不用像遍历一样去维护一个ans。还有一个方法是用最小堆来做,也是容易想到的,所以这道题目应该不算是困难题。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(head == nullptr || head->next == nullptr){
            return head;
        }
        ListNode* mid = findMiddle(head);
        ListNode* rightHead = mid->next;
        mid->next = nullptr;

        ListNode* l1 = sortList(head);
        ListNode* l2 = sortList(rightHead);
        return mergeTwoLists2(l1,l2);
    }

    ListNode* findMiddle(ListNode* head) {
        ListNode* slow = head;
        ListNode* fast = head->next;
        while(fast != nullptr && fast->next != nullptr){
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }

    //合并有序链表1
    ListNode* mergeTwoLists(ListNode* l1,ListNode* l2){
        ListNode* dummy = new ListNode(0);
        ListNode* curr = dummy;
        while(l1 != nullptr && l2 != nullptr){
            if(l1->val > l2->val){
                curr->next = l2;
                l2 = l2->next;
            }else if(l1->val <= l2->val){
                curr->next = l1;
                l1 = l1->next;
            }
            curr = curr->next;        
        }
        if(l1 == nullptr){
            curr->next = l2;
        }else{
            curr->next = l1;
        }
        return dummy->next;
    }

    //合并有序链表2
    ListNode* mergeTwoLists2(ListNode* l1,ListNode* l2){
        if(l1 == nullptr){
            return l2;
        }if(l2 == nullptr){
            return l1;
        }
        if(l1->val > l2->val) {
            l2->next = mergeTwoLists2(l1,l2->next);
            return l2;
        }else{
            l1->next = mergeTwoLists2(l1->next,l2);
            return l1;
        }
    }
};

这实际上也是分治(归并排序)。归并给我的感觉就像是你很难处理一个棘手的问题,然后你把这个棘手的问题一步步拆解下放,直到你能解决的地步,比如这里对于链表排序就是拆解到只剩下两个节点的时候,你肯定能通过判断大小关系直接排序了,然后一步步回升,直到解决这个问题。

这里有个工具函数,找链表中点我们可以记忆一下。这是一个快慢指针,这个判断条件你可以画图证明一下,如果节点总数是偶数,最后fast->next == nullptr。如果节点总数是奇数,最后fast == nullptr。都是比较好证明的。

OK,今天看的这几道题都不是很难,后面两道用到了归并的思想。

相关推荐
灋✘逞_兇19 分钟前
快速幂+公共父节点
数据结构·c++·算法·leetcode
十五年专注C++开发1 小时前
面试题:请描述一下你在项目中是如何进行性能优化的?针对哪些方面进行了优化,采取了哪些具体的措施?
开发语言·数据结构·c++·qt·设计模式·性能优化
wuqingshun3141592 小时前
经典算法 判断一个图是不是树
数据结构·c++·算法·蓝桥杯·深度优先
别来无恙2022 小时前
数据结构(4)
数据结构·c++
南玖yy2 小时前
排序算法复杂度及稳定性全解析(八种排序)
数据结构·算法·排序算法
小_t_同学2 小时前
C语言自定义类型详解一:结构体(内存对齐)
数据结构
飞川撸码2 小时前
【LeetCode 热题100】二叉树构造题精讲:前序 + 中序建树 & 有序数组构造 BST(力扣105 / 108)(Go语言版)
数据结构·leetcode·golang·二叉树
似水এ᭄往昔3 小时前
【初阶数据结构】——算法复杂度
数据结构
_x_w4 小时前
【16】数据结构之基于树的排序算法篇章
开发语言·数据结构·python·算法·链表·排序算法
一路向北he4 小时前
杰理10k3950温度测量
java·数据结构·算法