合并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,今天看的这几道题都不是很难,后面两道用到了归并的思想。

相关推荐
星轨初途1 小时前
数据结构排序算法详解(2)——选择排序(附动图)
c语言·数据结构·经验分享·笔记·b树·算法·排序算法
Chance_to_win3 小时前
数据结构之排序
数据结构
小年糕是糕手3 小时前
【C++】类和对象(二) -- 构造函数、析构函数
java·c语言·开发语言·数据结构·c++·算法·leetcode
kupeThinkPoem4 小时前
跳表有哪些算法?
数据结构·算法
前端小L4 小时前
图论专题(二十一):并查集的“工程应用”——拔线重连,修复「连通网络」
数据结构·算法·深度优先·图论·宽度优先
前端小L4 小时前
图论专题(二十三):并查集的“数据清洗”——解决复杂的「账户合并」
数据结构·算法·安全·深度优先·图论
啊董dong5 小时前
课后作业-2025年11月23号作业
数据结构·c++·算法·深度优先·noi
dlz08365 小时前
从架构到数据结构,到同步逻辑,到 show run 流程优化
数据结构
jllws16 小时前
数据结构_字符和汉字的编码与查找
数据结构
学困昇6 小时前
C++11中的包装器
开发语言·数据结构·c++·c++11