0
点赞
收藏
分享

微信扫一扫

10个图像处理的Python库

东林梁 2023-06-30 阅读 90
算法

文章目录

一、合并 K 个升序链表

题目链接

题目描述

示例 1:

示例 2:

示例 3:

思路分析
先思考如果两个升序链表该如何排序成一个链表?
可以用双指针把两个链表归并排序。
多个链表的话可以循环两两合并,最终合并成一个链表。但是这样效率太低。
可以使用优先级队列合并
维护当前每个链表没有被合并的元素的最前面一个放入小堆中,自定义比较排序,每次取出堆顶的链表,把该链表的头节点链接到排序链表中,然后把下一个节点再push进优先级队列中,如果是空节点就跳过。

priority_queue自定义比较函数

1️⃣ 使用仿函数

struct cmp
{
    bool operator()(ListNode* h1, ListNode* h2)
    {
        return h1->val > h2->val;
    }
};

priority_queue<ListNode*, vector<ListNode*>, cmp> q;

2️⃣ 使用函数指针

static bool cmp(ListNode* h1, ListNode* h2)
{
    return h1->val > h2->val;
}

priority_queue<ListNode*, vector<ListNode*>, decltype(&cmp)> q(&cmp);

这里要注意如果是在类里面定义优先级队列要定义在函数体内。
3️⃣ 使用lambda表达式

auto cmp = [](const ListNode* h1, const ListNode* h2){
    return h1->val > h2->val;
};
priority_queue<ListNode*, vector<ListNode*>, decltype(cmp)> q(cmp);

代码如下:

class Solution {
public:
    struct cmp
    {
        bool operator()(ListNode* h1, ListNode* h2)
        {
            return h1->val > h2->val;
        }
    };

    priority_queue<ListNode*, vector<ListNode*>, cmp> q;

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int n = lists.size();
        for(int i = 0; i < n; i++)
        {
            if(lists[i])
            q.push(lists[i]);
        }
        ListNode* head = new ListNode;
        ListNode* tail = head;
        while(q.size())
        {
            tail->next = q.top();
            ListNode* cur = nullptr;
            if(q.top())
            {
                cur = q.top()->next;
                tail = tail->next;
            }
            q.pop();
            if(cur) q.push(cur);
        }
        return head->next;
    }
};

二、有序矩阵中第 K 小的元素

题目链接

题目描述

示例 1:

示例 2:

思路分析
这道题其实根上面一道题一样,只不过把链表变成了数组
为了让数组模拟链表的形式来实现多路归并,我们可以定义一个结构体

struct op
{
    int _val;
    int _row;
    int _col;
    op(int val, int row, int col)
    : _val(val)
    , _row(row)
    , _col(col)
    {}
};

优先级队列中维护的是结构体,结构体包含下标和数值,这样就可以做到模拟链表形式。
代码如下:

class Solution {
public:
    struct op
    {
        int _val;
        int _row;
        int _col;
        op(int val, int row, int col)
        : _val(val)
        , _row(row)
        , _col(col)
        {}
    };

    struct cmp
    {
        bool operator()(const op& p1, const op& p2)
        {
            return p1._val > p2._val;
        }
    };

    priority_queue<op, vector<op>, cmp> q;
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        int n = matrix.size();
        for(int i = 0; i < n; i++)
        {
            q.push({matrix[i][0], i, 0});
        }
        for(int i = 0; i < k - 1; i++)
        {
            auto op = q.top();
            q.pop();
            if(op._col < n - 1)
            {
                q.push({matrix[op._row][op._col + 1], op._row, op._col + 1});
            }
        }
        return q.top()._val;
    }
};

拓展思路:二分法

观察矩阵找到规律:
矩阵任一点的数值一定比左边和上边的围成的矩阵的值要大。
在这里插入图片描述

根据这个性质,假如我们找到了一个值,让矩阵的左上角的值 <= 该值,然后统计左上角有多少个值,如果小于k,说明这个值取小了,如果大于等于k,说明答案不大于该值。

例如如图(取8):
在这里插入图片描述
那么怎么统计数量呢?

代码如下:

class Solution {
public:
    bool search(vector<vector<int>>& vv, int n, int target, int k)
    {
        int num = 0;
        int i = n - 1, j = 0;
        while(i >= 0 && j < n)
        {
            if(vv[i][j] <= target)
            {
                num += i + 1;
                j++;
            }
            else
            {
                i--;
            }
        }
        return num >= k;
    }


    int kthSmallest(vector<vector<int>>& matrix, int k) {
        int n = matrix.size();
        int l = matrix[0][0], r = matrix[n - 1][n - 1];
        while(l < r)
        {
            int mid = l + (r - l) / 2;
            if(search(matrix, n, mid, k))
            {
                r = mid;
            }
            else
            {
                l = mid + 1;
            }
        }
        return l;
    }
};

这里要注意二分的是值域。

三、查找和最小的 K 对数字

题目链接

题目描述

示例 1:

示例 2:

示例 3:

思路分析
优先级队列维护两个下标,i表示nums1中的下标,j表示nums2中的下标。
正常思路是假设枚举到(ai, bi),那么接下来就把(ai+1, bi), (ai, bi+1)push进优先级队列,但这样会出现重复的情况,所以可以先把nums1的每个元素和nums2的首元素组成的二元组push进去,这样每次只用让nums2的下标变化就行。
小优化:始终确保 nums1 为两数组中长度较少的那个,然后通过标识位来记录是否发生过交换,确保答案的点顺序的正确性。

举个例子:
首次取出的二元组为 (0,0),即点对 (nums1[0],nums2[0]),取完后将序列的下一位点对 (nums1[0],nums2[1])以二元组 (0,1)(0, 1)(0,1) 形式放入优先队列。

代码如下:

class Solution {
public:
    typedef pair<int, int> PII;
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        vector<vector<int>> vv;
        int n = nums1.size(), m = nums2.size();
        bool flag = true;
        if(n > m)
        {
            swap(n, m);
            swap(nums1, nums2);
            flag = false;
        }
        auto cmp = [&](const auto& e1, const auto& e2){
            return nums1[e1.first] + nums2[e1.second] > nums1[e2.first] + nums2[e2.second];
        };
        priority_queue<PII, vector<PII>, decltype(&cmp)> q(cmp);
        for(int i = 0; i < min(n, k); i++)
        {
            q.push({i, 0});
        }
        while(vv.size() < k && q.size())
        {
            auto [a, b] = q.top();
            q.pop();
            flag ? vv.push_back({nums1[a], nums2[b]}) : vv.push_back({nums2[b], nums1[a]});
            if(b < m - 1) q.push({a, b + 1});
        }
        return vv;
    }
};


举报

相关推荐

0 条评论