【Leetcode】23.合并K个排序链表(最小堆)

题目链接

点击打开题目

题目描述

【Leetcode】23.合并K个排序链表(最小堆)_第1张图片

题解

有多种方法,这个题我使用最小堆的方法。

首先了解一下最小堆的概念:最小堆 构建、插入、删除的过程图解

解题步骤

  1. 首先以各链表的头指针为结点构建一棵完全二叉树。
  2. 按最小堆的形式整理二叉树。
  3. 取二叉树的根结点,连在结果链表后。
  4. 用根结点所在链表的下一个结点作为根结点,并重新更新为最小堆。如果没有下一个结点了,则填入一个正无穷结点。
  5. 重复第3步,直到根结点为正无穷结点。

代码

/*
 * @lc app=leetcode.cn id=23 lang=cpp
 *
 * [23] 合并K个排序链表
 * 
 * 思路:最小堆,先构造一棵结点数为链表数的最小堆(完全二叉树)。
 * 依次取二叉树根结点插入结果链表,最小堆的根结点换成该结点的下一个元素,如果是NULL则换成正无穷。
 * 
 */

// @lc code=start
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

class Solution {
public:
    struct Node  // 二叉树结点
    {
        ListNode *node;
        Node *left=nullptr, *right=nullptr;
    };
    const int MAX_NUM = 9999999;
    // 构建完全二叉树
    void build_tree(Node *&tree_root, const vector<ListNode*> &lists)
    {
        int pos = 0;
        tree_root = new Node;
        tree_root->node = lists[pos++];
        queue<Node*> Q;
        Q.push(tree_root);
        while(pos < lists.size())
        {
            Node *node = Q.front();
            Q.pop();
            node->left = new Node;
            node->left->node = lists[pos++];
            Q.push(node->left);
            if (pos >= lists.size())
                break;

            node->right = new Node;
            node->right->node = lists[pos++];
            Q.push(node->right);
        }
    }
    // 销毁二叉树
    void destroy_tree(Node *node)
    {
        if (node == nullptr)
            return;
        destroy_tree(node->left);
        destroy_tree(node->right);
        delete node;
    }
    // 堆中元素下降
    void refresh_node(Node *now)
    {
        if (now->left != nullptr && now->right != nullptr)
        {
            Node *temp = now;
            // 如果左右孩子都有,与其中小的比较
            // 右边小
            if (temp->left->node->val > temp->right->node->val)
            {
                // 大于右边的话,则交换
                if (temp->node->val > temp->right->node->val)
                {
                    swap(temp->node, temp->right->node);
                    temp = temp->right;
                }
                // 比小的还小,不用交换
                else
                    return;
            }
            // 左边小
            else
            {
                if (temp->node->val > temp->left->node->val)
                {
                    swap(temp->node, temp->left->node);
                    temp = temp->left;
                }
                else
                    return;
            }
            // 递归向下继续交换
            refresh_node(temp);
        }
        // 右边为空,左边非空,则和左边比较是否要交换
        else if (now->left != nullptr)
        {
            if (now->node->val > now->left->node->val)
                swap(now->node, now->left->node);
        }
    }
    // 由完全二叉树构建最小堆
    void get_min_heap(Node *node)
    {
        if (node == nullptr || node->left == nullptr)
            return;

        get_min_heap(node->left);
        get_min_heap(node->right);
        refresh_node(node);
    }

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        // 删除空链表
        for (int i=0; i<lists.size(); i++)
        {
            if (lists[i] == nullptr)
            {
                lists.erase(lists.begin()+i);
                i--;
            }
        }
        if (lists.empty())
            return nullptr;

        ListNode max_node(MAX_NUM);  // 正无穷结点,用作填充
        ListNode *list_root = new ListNode(0);
        ListNode *list_now = list_root;
        Node *tree_root;
        // build tree
        build_tree(tree_root, lists);
        // 更新二叉树,变成最小堆
        get_min_heap(tree_root);
        // 依次取堆顶元素,并用所在链表下一个元素填充(如果没有用正无穷填充)
        while(tree_root->node->val != MAX_NUM)
        {
            // 取堆顶
            list_now->next = tree_root->node;

            // 换堆顶元素
            if (tree_root->node->next == nullptr)
                tree_root->node = &max_node;
            else
                tree_root->node = tree_root->node->next;
            refresh_node(tree_root);

            // 断开链接
            list_now = list_now->next;
            list_now->next = nullptr;
        }

        // delete and return
        list_now = list_root->next;
        delete list_root;
        destroy_tree(tree_root);
        return list_now;
    }
};

你可能感兴趣的:(Leetcode刷题,====数据结构====,数据结构,算法,最小堆,二叉树,链表)