返回

征服leetcode,闯关23:无畏合并K个有序链表!

前端

前言

在leetcode的征战之旅中,第23关等待着我们去征服——合并K个有序链表。面对这道经典的题目,我们势必披荆斩棘,以无畏之心解锁其中的奥秘。这不仅是对算法功底的考验,更是对逻辑思维和代码实现能力的全面检验。

思路一:将k个链表暴力合并

初次面对这个题目,最直观的解法便是将k个链表暴力合并。具体步骤如下:

  1. 创建哨兵节点: 在合并之前,创建一个哨兵节点作为合并后链表的头节点。哨兵节点指向一个值为无穷大(INT_MAX)的虚假节点。
  2. 依次合并每个链表: 遍历k个链表,每次将当前链表的最小节点与哨兵节点指向的节点进行比较,将较小的节点连接到哨兵节点之后。
  3. 更新哨兵节点: 将哨兵节点指向新连接的节点。
  4. 重复步骤2和3: 直到所有链表合并完毕。
  5. 返回合并后的链表: 返回哨兵节点指向的节点,即合并后的链表头节点。

代码实现:

ListNode* mergeKLists(vector<ListNode*>& lists) {
    if (lists.empty()) {
        return nullptr;
    }
    ListNode* dummy = new ListNode(INT_MAX);
    ListNode* cur = dummy;
    while (true) {
        ListNode* minNode = nullptr;
        int minVal = INT_MAX;
        for (ListNode* head : lists) {
            if (head && head->val < minVal) {
                minVal = head->val;
                minNode = head;
            }
        }
        if (!minNode) {
            break;
        }
        cur->next = minNode;
        cur = cur->next;
        minNode = minNode->next;
    }
    return dummy->next;
}

时间复杂度: O(NK),其中N是链表总数,K是链表长度。每次比较都需要遍历所有链表,时间复杂度为O(NK)。

空间复杂度: O(1),只需要常数的空间存储哨兵节点和当前节点。

思路二:分治法

如果k个链表规模庞大,暴力合并的效率会大大降低。此时,我们可以采用分治法来解决问题。

  1. 将链表分组: 将k个链表分成若干组,每组包含1或2个链表。
  2. 合并每组链表: 对每组链表进行合并,得到新的组。
  3. 递归合并: 将新的组再分成若干组,并重复步骤1和2,直到只剩一个组。
  4. 返回合并后的链表: 返回最后一个组的链表头节点。

代码实现:

ListNode* mergeKLists(vector<ListNode*>& lists) {
    return merge(lists, 0, lists.size() - 1);
}

ListNode* merge(vector<ListNode*>& lists, int left, int right) {
    if (left == right) {
        return lists[left];
    }
    int mid = (left + right) / 2;
    ListNode* leftList = merge(lists, left, mid);
    ListNode* rightList = merge(lists, mid + 1, right);
    return mergeTwoLists(leftList, rightList);
}

ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
    if (!list1 || !list2) {
        return list1 ? list1 : list2;
    }
    ListNode* dummy = new ListNode(INT_MAX);
    ListNode* cur = dummy;
    while (list1 && list2) {
        if (list1->val < list2->val) {
            cur->next = list1;
            list1 = list1->next;
        } else {
            cur->next = list2;
            list2 = list2->next;
        }
        cur = cur->next;
    }
    cur->next = list1 ? list1 : list2;
    return dummy->next;
}

时间复杂度: O(NlogN),其中N是所有链表节点总数。分治法的分治过程将问题规模不断缩小,时间复杂度为O(logN),合并两个链表的时间复杂度为O(N),因此总时间复杂度为O(NlogN)。

空间复杂度: O(logN),递归过程中需要额外的栈空间存储调用信息。

SEO优化

**