每日算法刷题Day63:8.19:leetcode 堆6道题,用时1h50min

三、重排元素

1.套路
2.题目描述
3.学习经验

1.重排元素要任意两个相邻的数不相等,可以利用贪心加奇偶位置构造思想,先取数量最多的数,然后把它排到答案的偶数位置,其余数填补剩下的偶数位置,若偶数位置填完,则填奇数位置。

1. 984. 不含AAA或BBB的字符串(中等)

984. 不含 AAA 或 BBB 的字符串 - 力扣(LeetCode)

思想

1.给定两个整数 ab ,返回 任意 字符串 s ,要求满足:

  • s 的长度为 a + b,且正好包含 a'a' 字母与 b'b' 字母;
  • 子串 'aaa' 没有出现在 s 中;
  • 子串 'bbb' 没有出现在 s 中。
    2.贪心思想,优先选择数量多的放进去,可以把代码再优化一下,变成以下逻辑:
  • (1)a>b,则放入"aab"
  • (2)a=b,则放入"ab"
  • (3)a<b,则放入"bba"
  • (4)最后单独放剩下元素,题目保证一定有答案
代码
复制代码
class Solution {
public:
    string strWithout3a3b(int a, int b) {
        string res = "";
        int maxa = 0;
        int maxb = 0;
        bool tag = true;
        if (a < b)
            tag = false;
        while (a || b) {
            if (a >= b) {
                maxa = 2;
                maxb = 1;
            } else {
                maxa = 1;
                maxb = 2;
            }
            int lena = min(maxa, a);
            int lenb = min(maxb, b);
            if (tag) {
                if (lena == 2) {
                    res += "aa";
                } else if (lena == 1) {
                    res += "a";
                }
                if (lenb == 2) {
                    res += "bb";
                } else if (lenb == 1) {
                    res += "b";
                }
            } else {
                if (lenb == 2) {
                    res += "bb";
                } else if (lenb == 1) {
                    res += "b";
                }
                if (lena == 2) {
                    res += "aa";
                } else if (lena == 1) {
                    res += "a";
                }
            }

            a -= lena;
            b -= lenb;
        }
        return res;
    }
};

优化代码:

复制代码
class Solution {
public:
    string strWithout3a3b(int a, int b) {
        string res = "";
        while (a && b) {
            if (a > b) {
                res += "aab";
                a -= 2;
                --b;
            } else if (a == b) {
                res += "ab";
                --a;
                --b;
            } else {
                res += "bba";
                b -= 2;
                --a;
            }
        }
        while (a--) {
            res += "a";
        }
        while (b--) {
            res += "b";
        }
        return res;
    }
};
2. 767. 重构字符串(中等,学习构造思想)

767. 重构字符串 - 力扣(LeetCode)

思想

1.给定一个字符串 s ,检查是否能重新排布其中的字母,使得两相邻的字符不同。

返回 s 的任意可能的重新排列。若不可行,返回空字符串 ""

2.首先肯定能想到求出现次数最多的字母的次数maxn,然后其他字符就插入,要大于等吗maxn-1,否则不可行。

我的想法是优先选择次数最多的,但是为了不想邻,记录下标,如果等于之前下标,则选次次数最多的,想到最大堆。

但是这题只要两两不相邻,故可考虑奇偶,即次数最多的排偶数,然后其余的接着偶数排,偶数排完排奇数

代码
复制代码
class Solution {
public:
    struct Node {
        int cnt;
        char c;
        int lastIdx;
        Node(int _cnt, char _c, int _lastIdx)
            : cnt(_cnt), c(_c), lastIdx(_lastIdx) {}
    };
    struct cmp {
        bool operator()(const Node& a, const Node& b) { return a.cnt < b.cnt; }
    };
    string reorganizeString(string s) {
        int n = s.size();
        map<char, int> mp;
        int maxn = 0;
        string res = "";
        for (auto& c : s) {
            ++mp[c];
            maxn = max(maxn, mp[c]);
        }
        if (maxn - 1 > n - maxn)
            return "";
        priority_queue<Node, vector<Node>, cmp> pq;
        for (auto t : mp) {
            pq.push(Node(t.second, t.first, -5));
        }
        int id = 0;
        while (!pq.empty()) {
            auto tmp = pq.top();
            pq.pop();
            if (tmp.lastIdx != id - 1) {
                res.push_back(tmp.c);
                --tmp.cnt;
                if (tmp.cnt > 0)
                    pq.push(Node(tmp.cnt, tmp.c, id));
            } else {
                auto tmp2 = pq.top();
                pq.pop();
                res.push_back(tmp2.c);
                --tmp2.cnt;
                if (tmp2.cnt > 0)
                    pq.push(Node(tmp2.cnt, tmp2.c, id));
                pq.push(Node(tmp));
            }
            ++id;
        }
        return res;
    }
};

构造优化:

复制代码
typedef pair<char, int> PCI;
bool cmp(const PCI& a, const PCI& b) { // 写在类外
    return a.second > b.second;
}
class Solution {
public:
    string reorganizeString(string s) {
        int n = s.size();
        map<char, int> mp;
        for (auto& c : s)
            ++mp[c];
        vector<PCI> vec(mp.begin(), mp.end());
        sort(vec.begin(), vec.end(), cmp);
        int maxn = vec[0].second;
        if (maxn - 1 > n - maxn)
            return "";
        string res(n, '0');
        int id = 0;
        for (auto tmp : vec) {
            char c = tmp.first;
            int cnt = tmp.second;
            while (cnt--) {
                res[id] = c;
                id += 2;
                if (id >= n)
                    id = 1;
            }
        }
        return res;
    }
};
3. 1054. 距离相等的条形码(中等)

1054. 距离相等的条形码 - 力扣(LeetCode)

思想

1.在一个仓库里,有一排条形码,其中第 i 个条形码为 barcodes[i]

请你重新排列这些条形码,使其中任意两个相邻的条形码不能相等。 你可以返回任何满足该要求的答案,此题保证存在答案。

2.跟[[九.堆(优先队列)#2. 767. 重构字符串(中等,学习构造思想)]]一模一样

代码
复制代码
typedef pair<int, int> PII;
bool cmp(const PII& a, const PII& b) { return a.second > b.second; }
class Solution {
public:
    vector<int> rearrangeBarcodes(vector<int>& barcodes) {
        int n = barcodes.size();
        map<int, int> mp;
        for (auto& x : barcodes)
            ++mp[x];
        vector<PII> vec(mp.begin(), mp.end());
        sort(vec.begin(), vec.end(), cmp);
        vector<int> res(n, 0);
        int id = 0;
        for (auto& tmp : vec) {
            int val = tmp.first;
            int cnt = tmp.second;
            while (cnt--) {
                res[id] = val;
                id += 2;
                if (id >= n)
                    id = 1;
            }
        }
        return res;
    }
};

四、第K小/大

1.套路

1.部分题目也可以用二分解决。

2.类似于top-k,但是能用二分的肯定二分快

3.堆中先存初始元素,然后进行弹出再插入的动态更新,直到弹出第K个,即为答案。

但是插入会产生重复,可以利用哈希去重[[九.堆(优先队列)#1. 264. 丑数II(中等,去重想哈希)]]或者预先插入一部分,规定更新顺序[[九.堆(优先队列)#3. 373. 查找和最小的K对数字(中等,学习)]]

2.题目描述
3.学习经验
1. 264. 丑数II(中等,去重想哈希)

264. 丑数 II - 力扣(LeetCode)

思想

1.给你一个整数 n ,请你找出并返回第 n丑数
丑数 就是质因子只包含 235 的正整数。

2.等价于取第n个最小的丑数,所以小顶堆,然后栈顶元素乘2,3,5再放入堆,但是会产生重复,利用哈希去重(这里集合就行)

代码
复制代码
class Solution {
public:
    typedef long long ll;
    int nthUglyNumber(int n) {
        int id = 0;
        priority_queue<ll, vector<ll>, greater<ll>> pq;
        set<ll> st;
        pq.push(1);
        ll res = 0;
        while (id < n) {
            res = pq.top();
            pq.pop();
            ++id;
            if (!st.count(res * 2)) {
                st.insert(res * 2);
                pq.push(res * 2);
            }
            if (!st.count(res * 3)) {
                st.insert(res * 3);
                pq.push(res * 3);
            }
            if (!st.count(res * 5)) {
                st.insert(res * 5);
                pq.push(res * 5);
            }
        }
        return res;
    }
};
2. 378. 有序矩阵中第K小的元素(中等)

378. 有序矩阵中第 K 小的元素 - 力扣(LeetCode)

思想

1.给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。

请注意,它是 排序后 的第 k 小元素,而不是第 k不同 的元素。

你必须找到一个内存复杂度优于 O(n2) 的解决方案。

2.用top-k小最大堆时间复杂度是O(n2logk),比二分慢很多

代码
复制代码
class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        int n = matrix.size();
        priority_queue<int> pq;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                pq.push(matrix[i][j]);
                if (pq.size() > k)
                    pq.pop();
            }
        }
        return pq.top();
    }
};
3. 373. 查找和最小的K对数字(中等,学习)

373. 查找和最小的 K 对数字 - 力扣(LeetCode)

思想

1.给定两个以 非递减顺序排列 的整数数组 nums1nums2 , 以及一个整数 k

定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2

请找到和最小的 k 个数对 (u1,v1), (u2,v2) ... (uk,vk)

2.因为此题两个数组都是有序的,所以当前最小的数对(i,j),那么下一个次小的一定是(i+1,j)或者(i,j+1),很符合堆拿出来一个,然后利用它再放进去,然后动态弹出和插入,但是会产生重复问题 ,即当前的(i,j)会由(i-1,j)(i,j-1)两次插入,所以我们人为规定只插入(i,j+1),那么i不能动了,就要预先把所有的(i,0)都插入最小堆。

代码
复制代码
class Solution {
public:
    struct Node {
        int sum;
        int id1;
        int id2;
        Node(int _sum, int _id1, int _id2) : sum(_sum), id1(_id1), id2(_id2) {}
    };
    struct cmp {
        bool operator()(const Node& a, const Node& b) { return a.sum > b.sum; }
    };
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2,
                                       int k) {
        int n1 = nums1.size(), n2 = nums2.size();
        priority_queue<Node, vector<Node>, cmp> pq;
        for (int i = 0; i < n1; ++i) {
            pq.push(Node(nums1[i] + nums2[0], i, 0));
        }
        int id = 0;
        vector<vector<int>> res(k);
        while (id < k) {
            auto tmp = pq.top();
            pq.pop();
            res[id] = vector<int>{nums1[tmp.id1], nums2[tmp.id2]};
            if (tmp.id2 + 1 < n2) {
                pq.push(Node(nums1[tmp.id1] + nums2[tmp.id2 + 1], tmp.id1,
                             tmp.id2 + 1));
            }
            ++id;
        }
        return res;
    }
};
相关推荐
gihigo19981 小时前
matlab多目标优化差分进化算法
数据结构·算法
weixin_582470172 小时前
GS-IR:3D 高斯喷溅用于逆向渲染
人工智能·算法
Lin9成2 小时前
机器学习集成算法与K-means聚类
算法
JNU freshman2 小时前
算法 之 拓 扑 排 序
数据结构·算法
NAGNIP3 小时前
DeepSeek-R1 架构解析:如何通过强化学习激发大模型的推理能力?
算法
小指纹3 小时前
河南萌新联赛2025第(六)场:郑州大学
java·开发语言·数据结构·c++·算法
岁忧3 小时前
(nice!!!)(LeetCode 每日一题) 1277. 统计全为 1 的正方形子矩阵 (动态规划)
java·c++·算法·leetcode·矩阵·go·动态规划
Davis_12194 小时前
代码随想录算法训练营27天 | 56. 合并区间、738.单调递增的数字、968.监控二叉树(提高)
数据结构·c++·算法·leetcode·贪心算法
闻缺陷则喜何志丹4 小时前
【倍增 桶排序】后缀数组
c++·算法·倍增·桶排序·后缀数组·lcp·后缀树