LeetCode 面试经典 150_分治_合并 K 个升序链表(108_23_C++_困难)

LeetCode 面试经典 150_分治_合并 K 个升序链表(108_23_C++_困难)

题目描述:

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

输入输出样例:

示例 1:
输入 :lists = [[1,4,5],[1,3,4],[2,6]]
输出 :[1,1,2,3,4,4,5,6]
解释 :链表数组如下:

1-\>4-\>5, 1-\>3-\>4, 2-\>6

将它们合并到一个有序链表中得到。

1->1->2->3->4->4->5->6

示例 2:
输入 :lists = []
输出:[]

示例 3:
输入 :lists = [[]]
输出:[]

提示:

k == lists.length

0 <= k <= 10^4

0 <= lists[i].length <= 500

-10^4 <= lists[i][j] <= 10 ^4

lists[i] 按 升序 排列

lists[i].length 的总和不超过 10^4

题解:

解题思路:

思路一(分治合并(非递归)):

1、通过每趟两两归并,最终合并成一个单链表。

list1~list5代表 五个单链表

lists={list1,list2,list3,list4,list5}

① lists={ [list1,list3,list5] ,list4,list5} 合并list1,list2=>list1,合并list3,list4=>list3。

② lists={ [list1,list5] ,list5,list4,list5} 合并list1,list3=>list1。

③ lists={ [list1] ,list5,list5,list4,list5} 合并list1,list5=>list1,合并到只有一个链表时结束。

此思想非常类似于哈夫曼编码,尽量使短的链表进行合并,节省操作的时间。(每次将合并后的数据存储在数组头部,每次对合并好的元素再进行二次合并,再次存储在头部

其他实现思路和代码呈现形式:LeetCode 热题 100_合并 K 个升序链表(34_23_困难_C++)(分治合并(非递归);数组(利用题目所给数据范围))(ListNode head 与 new ListNode())

2、复杂度分析:

① 时间复杂度: O(kn×logk),假设每个链表的最长长度是 n,有k 个链表。第一轮合并 k/2 组链表,每一组的时间代价是 O(2n);第二轮合并 k/4 组链表,每一组的时间代价是 O(4n)...所以总的时间代价是O(kn×logk),故渐进时间复杂度为 O(kn×logk)。

② 空间复杂度:O(1)。

代码实现

代码实现(思路一(合并后排序)):
cpp 复制代码
class Solution {
private:
    // 合并两个有序链表的辅助函数
    ListNode* merge(ListNode* head1, ListNode* head2) {
        // 如果其中一个链表为空,直接返回另一个链表
        if(head1 == nullptr || head2 == nullptr) 
            return head1 ? head1 : head2;

        // 创建一个虚拟头结点,方便处理合并链表
        ListNode* dummyHead = new ListNode();
        ListNode* mergeTail = dummyHead; // mergeTail 用于跟踪合并链表的最后一个节点

        // 开始合并两个链表,直到有一个链表遍历完
        while(head1 != nullptr && head2 != nullptr) {
            // 比较两个链表当前节点的值
            if(head1->val < head2->val) {
                // 将较小的节点添加到合并链表中
                mergeTail->next = head1; 
                head1 = head1->next; // 移动 head1 指针到下一个节点
            } else {
                // 同样操作将 head2 的节点添加
                mergeTail->next = head2; 
                head2 = head2->next; // 移动 head2 指针到下一个节点
            }
            mergeTail = mergeTail->next; // 更新合并链表的尾部
        }

        // 处理剩余节点(只会有一个链表还有剩余)
        if(head1 != nullptr) {
            mergeTail->next = head1; // 将剩余的 head1 链表连到合并链表后
        } else {
            mergeTail->next = head2; // 将剩余的 head2 链表连到合并链表后
        }

        // 返回合并后的链表,跳过虚拟头结点
        ListNode* result = dummyHead->next; 
        delete dummyHead; // 释放虚拟头结点
        return result; // 返回合并后的链表头
    }

public:
    // 合并 K 个有序链表的主函数
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int len = lists.size(); // 获取链表数组的大小
        // 如果没有链表,则直接返回 nullptr
        if(len == 0){
            return nullptr;
        }

        // 循环合并链表,直到只剩下一个链表
        while(len > 1) {
            int k = 0; // k 用于跟踪合并后的链表数量
            // 每次将相邻的两个链表合并
            for(int i = 0; i < len; i += 2) {
                if (i + 1 < len) {
                    // 如果还有相邻的两个链表,则合并
                    lists[k] = merge(lists[i], lists[i + 1]);
                } else {
                    // 如果奇数个链表,最后一个链表不合并,直接保留
                    lists[k] = lists[i];
                }
                k++; // 对合并后的链表数量进行增加
            }
            len = k; // 更新当前链表数量(合并后的数量)
        }

        // 返回最终合并后的链表
        return lists[0];
    }
};
以思路一为例进行调试
cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;

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) {}
};

//尾插法创建单链表 
ListNode *createList(vector<int> arr){
	ListNode *head=nullptr,*tail=nullptr;
	for(const auto &val:arr){
		if(head==nullptr){
			head=tail=new ListNode(val);
		}else{
			tail->next=new ListNode(val);
			tail=tail->next; 
		}
	}
	return head;
}

class Solution {
private:
    // 合并两个有序链表的辅助函数
    ListNode* merge(ListNode* head1, ListNode* head2) {
        // 如果其中一个链表为空,直接返回另一个链表
        if(head1 == nullptr || head2 == nullptr) 
            return head1 ? head1 : head2;

        // 创建一个虚拟头结点,方便处理合并链表
        ListNode* dummyHead = new ListNode();
        ListNode* mergeTail = dummyHead; // mergeTail 用于跟踪合并链表的最后一个节点

        // 开始合并两个链表,直到有一个链表遍历完
        while(head1 != nullptr && head2 != nullptr) {
            // 比较两个链表当前节点的值
            if(head1->val < head2->val) {
                // 将较小的节点添加到合并链表中
                mergeTail->next = head1; 
                head1 = head1->next; // 移动 head1 指针到下一个节点
            } else {
                // 同样操作将 head2 的节点添加
                mergeTail->next = head2; 
                head2 = head2->next; // 移动 head2 指针到下一个节点
            }
            mergeTail = mergeTail->next; // 更新合并链表的尾部
        }

        // 处理剩余节点(只会有一个链表还有剩余)
        if(head1 != nullptr) {
            mergeTail->next = head1; // 将剩余的 head1 链表连到合并链表后
        } else {
            mergeTail->next = head2; // 将剩余的 head2 链表连到合并链表后
        }

        // 返回合并后的链表,跳过虚拟头结点
        ListNode* result = dummyHead->next; 
        delete dummyHead; // 释放虚拟头结点
        return result; // 返回合并后的链表头
    }

public:
    // 合并 K 个有序链表的主函数
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int len = lists.size(); // 获取链表数组的大小
        // 如果没有链表,则直接返回 nullptr
        if(len == 0){
            return nullptr;
        }

        // 循环合并链表,直到只剩下一个链表
        while(len > 1) {
            int k = 0; // k 用于跟踪合并后的链表数量
            // 每次将相邻的两个链表合并
            for(int i = 0; i < len; i += 2) {
                if (i + 1 < len) {
                    // 如果还有相邻的两个链表,则合并
                    lists[k] = merge(lists[i], lists[i + 1]);
                } else {
                    // 如果奇数个链表,最后一个链表不合并,直接保留
                    lists[k] = lists[i];
                }
                k++; // 对合并后的链表数量进行增加
            }
            len = k; // 更新当前链表数量(合并后的数量)
        }

        // 返回最终合并后的链表
        return lists[0];
    }
};

int main(){
	vector<vector<int>> a={{1,4,5},{1,3,4},{2,6}};
	vector<ListNode *> lists;
	//将a容器中的数据转换成单链表 
	for(auto const &list:a){
			ListNode *head=createList(list);
			lists.emplace_back(head);
	}
	Solution s;
	//进行各个单链表合并 
	ListNode *ans=s.mergeKLists(lists);
	//输出合并的单链表 
	while(ans!=nullptr){
		cout<<ans->val<<"->";
		ans=ans->next;
	}
	cout<<"NULL";
	
	return 0;
}

LeetCode 面试经典 150_分治_合并 K 个升序链表(108_23)原题链接

欢迎大家和我沟通交流(✿◠‿◠)

相关推荐
天赐学c语言2 小时前
12.29 - 字符串相加 && vector和map的区别
数据结构·c++·算法·leecode
yuuki2332332 小时前
【C++】 list 实现:双向循环链表解析
c++·链表·list
GISer_Jing2 小时前
2025年FE_Jinger的年度总结、经验分享与展望
前端·经验分享·面试·前端框架·aigc
南山安2 小时前
React 学习:useContext——优雅解决跨层级组件通信
javascript·react.js·面试
随意起个昵称2 小时前
【做题总结】顺子(双指针)
c++·算法
鹏程十八少2 小时前
Android 一套代码适配车机/手机横竖屏?看我如何用搞定小米、比亚迪、蔚来、理想、多品牌架构设计
android·前端·面试
郝学胜-神的一滴2 小时前
Linux多线程编程:深入解析pthread_detach函数
linux·服务器·开发语言·c++·程序人生
阿闽ooo2 小时前
深入浅出享元模式:从图形编辑器看对象复用的艺术
c++·设计模式·编辑器·享元模式
海盗猫鸥2 小时前
「C++」多态
开发语言·c++