912. 排序数组

#include <vector>

using namespace std;

class Solution {
public:
    // 自上而下调整大顶堆,O(logn)
    void adjustHeap(vector<int> &nums, int len, int curIndex) {
        int leftChild = 2 * curIndex + 1;
        int temp = nums[curIndex];
        while (leftChild <= len - 1) {
            // 如果有右孩子,且更大,就换成右孩子
            if (leftChild < len - 1
                && nums[leftChild + 1] > nums[leftChild])
                leftChild++;
            if (nums[leftChild] <= temp) break;
            nums[curIndex] = nums[leftChild];
            curIndex = leftChild;
            leftChild = 2 * curIndex + 1;
        }
        nums[curIndex] = temp;
    }

    // 自底向上建堆,O(n)
    void bottomUp(vector<int> &nums) {
        int n = nums.size();
        // 从最后一个非叶子节点开始往上
        for (int i = n / 2 - 1; i >= 0; i--)
            adjustHeap(nums, n, i);
    }

    // 插到末尾,再自底向上调整
    void heapInsert(vector<int> &nums, int len, int val) {
        nums[len++] = val;
        int curIndex = len - 1;
        int parent = (curIndex - 1) / 2;
        while (nums[curIndex] > nums[parent]) {
            swap(nums[curIndex], nums[parent]);
            curIndex = parent;
            parent = (curIndex - 1) / 2;
        }
    }

    // 自顶向下建堆,O(nlogn)
    void topDown(vector<int> &nums) {
        int n = nums.size();
        for (int i = 0; i < n; ++i)
            heapInsert(nums, i, nums[i]);
    }

    void heapSort(vector<int> &nums) {
        int n = nums.size();
        // 堆的大小
        int len = nums.size();
        // 只需要 n - 1 次
        for (int i = 1; i < n; ++i) {
            swap(nums[0], nums[--len]);
            adjustHeap(nums, len, 0);
        }
    }

    vector<int> sortArray(vector<int> &nums) {
        bottomUp(nums);
        // topDown(nums);
        heapSort(nums);
        return nums;
    }
};

23. 合并 K 个升序链表

  • 每次从链表数组中找到最小的头节点,加入最终结果
class Solution {
public:
    // 每次从链表数组中找到最小的头节点,加入最终结果
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        // 没有元素直接返回
        if (lists.empty()) return nullptr;
        ListNode *dummyHead = new ListNode(0, nullptr);
        ListNode *pre = dummyHead;
        int len = lists.size();

        while (true) {
            // 记录当前所有链表中头节点值最小的链表在 lists 中的下标
            int minHeadIndex = -1;
            // 遍历每一条链表的头部,找到最小的头节点
            for (int i = 0, finished = 0; i < len; ++i) {
                if ((lists[i] != nullptr) &&
                    (minHeadIndex == -1 || (lists[i]->val < lists[minHeadIndex]->val)))
                    minHeadIndex = i;
            }
            // 已经找不到元素了(每个链表都是空的),则返回
            if (minHeadIndex == -1) return dummyHead->next;

            // 否则,移出最小元素,并且合并到最终结果中
            ListNode *node = lists[minHeadIndex];
            lists[minHeadIndex] = lists[minHeadIndex]->next;
            node->next = nullptr;
            pre->next = node;
            pre = pre->next;

            // 判断是否只剩下一条链表
            int finished = 0;
            for (ListNode *curHead: lists)
                if (curHead == nullptr) finished++;
            if (finished >= len - 1) break;
        }

        // 把最后剩下的一条链表直接接上
        for (ListNode *curHead: lists) {
            if (curHead != nullptr) {
                pre->next = curHead;
                return dummyHead->next;
            }
        }

        return dummyHead->next;
    }
};
  • 遍历链表数组,每次把一整条链表和最终链表合并
class Solution {
public:
    // 合并两个链表
    ListNode *mergeTwoLists(ListNode *a, ListNode *b) {
        if ((!a) || (!b)) return a ? a : b;
        ListNode *dummyHead = new ListNode(0, nullptr);
        ListNode *pre = dummyHead;
        ListNode *p = a, *q = b;
        while (p && q) {
            if (p->val < q->val) {
                pre->next = p;
                p = p->next;
            } else {
                pre->next = q;
                q = q->next;
            }
            pre = pre->next;
        }
        pre->next = (p ? p : q);
        return dummyHead->next;
    }

    // 遍历链表数组,每次把一整条链表和最终链表合并
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        ListNode *res = nullptr;
        for (int i = 0; i < lists.size(); ++i)
            res = mergeTwoLists(res, lists[i]);
        return res;
    }
};
  • 分治
class Solution {
public:
    // 合并两个链表
    ListNode *mergeTwoLists(ListNode *a, ListNode *b) {
        if ((!a) || (!b)) return a ? a : b;
        ListNode *dummyHead = new ListNode(0, nullptr);
        ListNode *pre = dummyHead;
        ListNode *p = a, *q = b;
        while (p && q) {
            if (p->val < q->val) {
                pre->next = p;
                p = p->next;
            } else {
                pre->next = q;
                q = q->next;
            }
            pre = pre->next;
        }
        pre->next = (p ? p : q);
        return dummyHead->next;
    }

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

    // 分治合并
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        return merge(lists, 0, lists.size() - 1);
    }
};
  • 小顶堆
class Solution {
public:
    struct cmp {
        bool operator()(ListNode *a, ListNode *b) {
            return (*a).val > (*b).val;
        }
    };

    // 使用小顶堆,只有初始化堆的时候要遍历一次数组,之后每次把堆顶节点并入最终链表,并且把堆顶节点的后继加入堆中
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        // 没有元素直接返回
        if (lists.empty()) return nullptr;
        ListNode *dummyHead = new ListNode(0, nullptr);
        ListNode *pre = dummyHead;

        // 小顶堆
        priority_queue<ListNode *, vector<ListNode *>, cmp> heap;
        // 初始化小顶堆,把所有的链表头节点加入其中
        for (ListNode *curHead: lists) {
            if (curHead == nullptr) continue;
            heap.push(curHead);
            curHead = curHead->next;
        }

        while (!heap.empty()) {
            // 取出最小节点,并加入到最终的链表中
            ListNode *node = heap.top();
            heap.pop();
            pre->next = node;
            pre = pre->next;

            // 如果后面的节点非空的话,就加入到堆中
            if (node->next != nullptr)
                heap.push(node->next);
        }
        return dummyHead->next;
    }
};

线段重合

#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>

using namespace std;

int main() {
    int n;
    cin >> n;
    vector<vector<int>> lines(n, vector<int>(2, 0));
    for (int i = 0; i < n; ++i)
        cin >> lines[i][0] >> lines[i][1];

    // 根据线段的左端点排序
    sort(lines.begin(), lines.end(),
         [](vector<int> &v1, vector<int> &v2) { return v1[0] < v2[0]; });
    // 小顶堆,根据线段的右端点排序
    priority_queue<int, vector<int>, greater<int>> heap;

    int res = 0;
    // 任何一个重合的区域,一定有一个左端点最大的,下面的循环就是考虑这个最大的左端点是谁
    // [                   ]
    //      [        ]
    //         [        ]
    //      最大的左端点
    for (int i = 0; i < n; ++i) {
        // 堆中保存着的线段的右端点比当前线段的左端点还小
        // 说明没有重合的地方,也不会与后续的线段重合,全部弹出
        while (!heap.empty() && heap.top() <= lines[i][0])
            heap.pop();
        heap.emplace(lines[i][1]);
        res = max(res, (int) heap.size());
    }
    cout << res;
}

2208. 将数组和减半的最少操作次数

#include <vector>
#include <queue>
#include <numeric>

using namespace std;

class Solution {
public:
    int halveArray(vector<int> &nums) {
        // 大根堆,并用 nums 初始化
        priority_queue<double, vector<double>, less<double>> heap(begin(nums), end(nums));
        // 求和
        double sum = accumulate(begin(nums), end(nums), (double) 0);
        // 目标值
        sum /= 2;
        int res = 0;
        // 每次找最大值减半,再放回去
        for (double minus = 0, half; minus < sum; res++, minus += half) {
            half = heap.top() / 2;
            heap.pop();
            heap.emplace(half);
        }
        return res;
    }
};
posted @ 2024-09-27 12:09  n1ce2cv  阅读(4)  评论(0编辑  收藏  举报