[LC优选算法#17] 链表 | 合并 K 个升序链表 | K个⼀组翻转链表

1. 合并 K 个升序链表

合并 K 个升序链表

解题思路:

  1. 暴力 O(N*K^2):一次合并两个有序链表,循环操作,直至所有链表合并为一个。虽然这种方法简单易想,但一旦K很大,时间复杂度就会特别高。

一个一个链表合并过于冗余,那能不能一次性合并所有链表呢?答案是可以的:每次选择K个链表中头节点值最小 的元素加入链表即可。因为链表都是有序的,因此我们考虑用的思想优化:

  1. O(N*K*logK):创建一个小根堆 ,放入所有的头节点 ,在合并链表时,每次选取堆顶元素尾插,另找一个头节点放入堆中。堆为空则说明所有的链表都已被合并了。
  1. 分治+递归 O(N*K*logK):在逐⼀⽐较时,答案链表会变得越来越**⻓** ,每个跟它合并的⼩链表的元素都需要⽐较很多次 才可以成功排序。因此,我们也可以用分治+递归 的方式优化链表长度,尽可能让长度相同的链表进行两两合并。
cpp 复制代码
//堆解法
class Solution {
public:
    typedef ListNode Node;

    struct cmp //需要提供比较逻辑
    {
        bool operator()(Node* n1, Node* n2)
        {
            return n1->val > n2->val;
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        priority_queue<Node*, vector<Node*>, cmp> heap;

		//放入头节点
        for(auto& l : lists)
        {
            if(l)
            {
                heap.push(l);
            } 
        }

        Node* newhead = new Node(0);
        Node* cur = newhead;

        while(!heap.empty())
        {
            Node* tmp = heap.top();
            heap.pop();
            cur->next = tmp;

            if(tmp->next)
            {
                heap.push(tmp->next);
            }

            cur = cur->next;
        }

        Node* ret = newhead->next;
        delete newhead;
        return ret;
    }
};
cpp 复制代码
//递归+分治解法
class Solution {
public:
    typedef ListNode Node;
    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        int n = lists.size();
        if(n == 0) return nullptr;
        if(n == 1) return lists[0];

        return Merge(lists, 0, n - 1);
    }

    Node* Merge(vector<ListNode*>& lists, int left, int right)
    {
        if(left == right) return lists[left];
        if(left > right) return nullptr;

        int mid = (left + right) / 2;

        Node* n1 = Merge(lists, left, mid);
        Node* n2 = Merge(lists, mid + 1, right);

        return Merge_Sort(n1, n2);
    }

    Node* Merge_Sort(Node* n1, Node* n2)
    {
        if(n1 == nullptr) return n2;
        if(n2 == nullptr) return n1;

        Node* cur1 = n1;
        Node* cur2 = n2;
        Node* newhead = new Node(0);
        Node* cur = newhead;


        while(cur1 && cur2)
        {
            if(cur1->val <= cur2->val)
            {
                cur->next = cur1;
                cur1 = cur1->next;
            }
            else
            {
                cur->next = cur2;
                cur2 = cur2->next;
            }

            cur = cur->next;
        }

        if(cur1)
        {
            cur->next = cur1;
        }
        if(cur2)
        {
            cur->next = cur2;
        }

        Node* ret = newhead->next;
        delete newhead;
        return ret;
    }
};

2. K个⼀组翻转链表

K个⼀组翻转链表

解题思路:

  1. 模拟 :链表具有单向性,我们不能直接在原链表中翻转,否则会丢失元素。因此采用头插法 的方式解决翻转。步骤如下:
    step1 :遍历链表,记节点个数为nn/k计算需要翻转多少组。
    step2 :新建链表,创建prev,cur,tmp,p,tail五个变量,对每组元素使用头插 的方式放入链表:将链表按K个为⼀组进⾏分组,组内进⾏反转 ,并且记录反转后的头尾结点,使其可以和前、后连接起来。
    step3:循环step2直至K组链表全部翻转,连接剩余元素并返回头节点。
cpp 复制代码
class Solution {
public:
    typedef ListNode Node;

    ListNode* reverseKGroup(ListNode* head, int k)
    {
        if(k == 1) return head;

        int num = 0;
        for(Node* cur=head;cur; cur=cur->next)
        {
            num++;
        }
        num /= k;

        Node* newhead = new Node(0);
        Node* prev = newhead;
        Node* cur1 = head;
        Node* next = head->next;
        Node* tail = cur1;

        if(num == 0) return nullptr;

        while(num--)
        {
            int cnt = 0;
            while(cnt < k)
            {
                Node* tmp = prev->next;
                prev->next = cur1;
                cur1->next = tmp;
                cur1 = next;
                if(cur1)
                {
                    next = next->next;
                }

                cnt++;
            }

            prev = tail;
            tail = cur1;
        }

        prev->next = cur1; //链接剩余节点

        Node* ret = newhead->next;
        delete newhead;
        return ret;
    }
};

// 本期内容就到这里啦,如果对你有帮助,请三连支持!我是青云,我们下期见^_~

相关推荐
_olone1 小时前
Luogu P2704 [NOI2001] 炮兵阵地
c++·算法·状压dp
kebidaixu2 小时前
两轮BMS 防打火策略详解
算法
wabs6663 小时前
关于动态规划【力扣1035.不相交的线和53.最大子数组和的思考】
算法·leetcode·动态规划
退休倒计时3 小时前
【每日一题】LeetCode 199. 二叉树的右视图 TypeScript
算法·leetcode·typescript
可编程芯片开发3 小时前
通过MATLAB实现PID控制器,积分分离控制器以及滑模控制器
算法
kebidaixu3 小时前
两轮BMS 短路保护策略详解
算法
zwenqiyu3 小时前
非线性字符串数据结构串讲
数据结构·c++·学习·算法
z小猫不吃鱼3 小时前
03 Optimal Brain Surgeon 详解:Hessian 剪枝为什么有效?
算法·机器学习·剪枝
硕风和炜4 小时前
【LeetCode: 1301. 最大得分的路径数目 + DP】
java·算法·leetcode·动态规划·dp·记忆化搜索