力扣刷题笔记 23. 合并K个排序链表 C#

题目如下:

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

今天的签到题是  378. 有序矩阵中第K小的元素,依旧是暴力解题后看图解,在第二种解法使用归并,提示参考  23. 合并K个排序链表,随先解此题。

本体题解给出了 3 种解法

  1. 顺序合并
  2. 分治合并
  3. 优先队列合并

顺序合并较为简单,不作赘述,会在分治合并中提及,由于 C# 没有优先队列,故我也还没有理解优先队列合并。

以下列出自己解题时使用的方法和理解分治合并后的方法。

解法一

一开始使用的解法比较暴力,遍历 k 个链表头节点,每次对比出最小的头节点 head,记录下这个节点 head,接入上一次记录的头节点的 next,同时把所在链表的头节点设为 head.next。遍历以上过程,直至 k 个链表中的所有节点都被对比过。

此解法是自己写的,提交延迟达734ms,不宜使用,只作记录反思。

解法时间复杂度为总节点数 * 链表数 O(n * k),空间复杂度为创建了一个新数组存储新的头节点 O(k)。

以下为代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public int val;
 *     public ListNode next;
 *     public ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode MergeKLists(ListNode[] lists) {
        ListNode[] nodeArr = new ListNode[lists.Length];
        for (int i = 0;i< lists.Length;i++)
        {
            if (lists[i] != null)
            {
                nodeArr[i] = lists[i];
            }
        }
        ListNode head = null;
        ListNode _head = null;
        bool isEmpty = false;
        while (!isEmpty)
        {
            isEmpty = true;
            int k = int.MaxValue;
            int index = 0;
            for (int i = 0;i < nodeArr.Length;i++)
            {
                if (nodeArr[i] != null)
                {
                    isEmpty = false;
                    if(nodeArr[i].val < k)
                    {
                        index = i;
                        k = nodeArr[i].val;
                    }
                }
            }
            if (!isEmpty)
            {
                if (head == null)
                {
                    _head = nodeArr[index];
                    head = nodeArr[index];
                    nodeArr[index] = nodeArr[index].next;
                }
                else
                {
                    head.next = nodeArr[index];
                    head = head.next;
                    nodeArr[index] = nodeArr[index].next;
                }
            }
        }
        return _head;
    }
}

解法二、分治合并

相较于自己的解法一,顺序合并对合并过程做了一次规划,每次只用一条结果链表去合并新的链表。解法时间复杂度为总节点数  O(n),空间复杂度为 O(1)。

分支合并则是将 k 条链表分成 k/2 组,进行两两合并,反复该过程,第二次分为 k/4 组,第三次 k/8 组,以此类推,直至只剩一条链表即结果。

复杂度分析较为复杂,直接引用力扣解释,同时引用力扣图解:

自己编写的代码中有一处已知问题:合并结果直接赋值到传入的链表数组中,改变了数组原本的内容。另外,检查之后并未发现有开辟新的内存空间,但是提交后内存消耗只击败了50%的用户,不知道是不是语言优势,如有大佬赐教不胜感激

以下为代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public int val;
 *     public ListNode next;
 *     public ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode MergeKLists(ListNode[] lists) {
        if (lists.Length == 0)
        {
            return null;
        }
        int k = 1;
        int nodeNum = 0;
        while (nodeNum != 1)
        {
            nodeNum = 0;
            for (int i = 0;i < lists.Length;i += k * 2)
            {
                if (i+k >= lists.Length)
                {
                    lists[i] = MergeTwoLists(lists[i],null);
                }
                else
                {
                    lists[i] = MergeTwoLists(lists[i],lists[i + k]);
                }
                nodeNum ++;
            }
            k = k * 2;
        }
        return lists[0];
    }

    private ListNode MergeTwoLists(ListNode nodeA,ListNode nodeB)
    {
        if (nodeA == null || nodeB == null)
        {
            return nodeA == null? nodeB:nodeA;
        }
        ListNode head;
        if (nodeA.val <= nodeB.val)
        {
            head = nodeA;
            nodeA = nodeA.next;
        }
        else
        {
            head = nodeB;
            nodeB = nodeB.next;
        }
        ListNode tail = head;
        while (nodeA != null && nodeB != null)
        {
            if (nodeA.val<= nodeB.val)
            {
                tail.next = nodeA;
                nodeA = nodeA.next;
            }
            else
            {
                tail.next = nodeB;
                nodeB = nodeB.next;
            }
            tail = tail.next;
        }
        tail.next = nodeA != null? nodeA:nodeB;
        return head;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值