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

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

相关推荐
炸膛坦客8 小时前
单片机/C/C++八股:(二十六)IIC 专题(I²C)---- 上集
c语言·c++·单片机
旖-旎8 小时前
LeetCode 518:零钱兑换||(完全背包)—— 题解
c++·算法·leetcode·动态规划·背包问题
To_OC9 小时前
LC 42 接雨水:暴力超时卡半天?前后缀数组一用就通了
javascript·算法·leetcode
Henry Zhu1239 小时前
C++中的特殊成员函数与智能指针
c++
触底反弹10 小时前
深入理解大模型采样:Temperature、Top-K、Top-P 的原理与实战
人工智能·算法·面试
_wyt00111 小时前
多重背包问题详解
c++·背包dp
沉默王二11 小时前
腾讯一面,我霸气反问:“你说你们在做Agent项目,说说 SubAgent、Plan 模式、Skill 调用这些你们都是怎么做的?”面试官一直在擦汗。。
面试·agent·ai编程
Ljwuhe12 小时前
C++——多态
开发语言·c++
越甲八千12 小时前
STL stack为何没有迭代器
c++
从零开始的代码生活_14 小时前
C++ 继承详解:访问控制、对象模型、菱形继承与设计取舍
开发语言·c++·后端·学习·算法