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 <= listsi.length <= 500

-10^4 <= listsij <= 10 ^4

listsi升序 排列

listsi.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)原题链接

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

相关推荐
綝~7 小时前
爬虫数据采集工程师岗位面试题
爬虫·面试·请求
如竟没有火炬8 小时前
最大矩阵——单调栈
数据结构·python·线性代数·算法·leetcode·矩阵
8Qi89 小时前
LeetCode 1143 & 718:最长公共子序列 / 最长重复子数组
算法·leetcode·职场和发展·动态规划
Qt程序员10 小时前
Linux RCU 原理与应用
linux·c++·内核·linux内核·rcu
想吃火锅100510 小时前
【leetcode】1.两数之和js版
javascript·算法·leetcode
qeen8710 小时前
【C++】类与对象之类的默认成员函数(二)
android·c语言·开发语言·c++·笔记·学习
王老师青少年编程11 小时前
信奥赛C++提高组csp-s之搜索进阶(记忆化搜索案例实践3)
c++·记忆化搜索·方格取数·csp·信奥赛·csp-s·提高组
乐观的山里娃11 小时前
【反八股 01】HashMap 的设计参数是怎么来的
面试
嵌入式ZYXC12 小时前
第3篇:《面试题:I2C为什么要加上拉电阻?阻值怎么选?》
stm32·单片机·嵌入式硬件·面试·职场和发展
Titan202412 小时前
Linux动静态库
linux·服务器·c++