当前位置: 首页 > news >正文

pq|二维前缀和

 

二维前缀和

lc304

 class NumMatrix {

    vector<vector<int>> sum;
public:
NumMatrix(vector<vector<int>> &matrix) {
int m = matrix.size(), n = matrix[0].size();
sum.resize(m + 1, vector<int>(n + 1));
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
sum[i + 1][j + 1] = sum[i + 1][j] + sum[i][j + 1] - sum[i][j] + matrix[i][j]; //+-+
}
}
}

    // 返回左上角在 (r1,c1) 右下角在 (r2,c2) 的子矩阵元素和
int sumRegion(int r1, int c1, int r2, int c2) {
return sum[r2 + 1][c2 + 1] - sum[r2 + 1][c1] - sum[r1][c2 + 1] + sum[r1][c1];//--+
}
};

 

lc1738

二维异或和

neat
只要满足交换律并且重复计算 那么二维矩阵不管是什么处理 都可以用二维前缀和加法的思想
这里XOR是一样的


还可利用 a ^ a ^ a = a,进行一维优化

 class Solution {
public:
int kthLargestValue(vector<vector<int>>& matrix, int k) {
int m = matrix.size(), n = matrix[0].size();
vector<int> a;
vector<vector<int>> s(m + 1, vector<int>(n + 1));
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
s[i + 1][j + 1] = s[i + 1][j] ^ s[i][j + 1] ^ s[i][j] ^ matrix[i][j];
}

//添加每行
a.insert(a.end(), s[i + 1].begin() + 1, s[i + 1].end());
}
ranges::nth_element(a, a.end() - k);
return a[a.size() - k];
}
};

 

第k大--快速选择算法

要理解最后两行如何获取第  k  大的元素,结合快速选择算法(由  ranges::nth_element  实现)

1. 快速选择算法的作用

ranges::nth_element  是 C++20 引入的算法,基于快速选择思想:

- 它会对容器(这里是  vector<int> a )进行部分排序,使得指定位置的元素最终处于“正确排序后”的位置。


- 具体来说, ranges::nth_element(a, a.end() - k)  会将  a  中第  (a.size() - k)  个位置的元素,调整为“若整个数组排序后,该位置应有的值”。此时:
- 该位置左边的所有元素都小于等于它,
- 该位置右边的所有元素都大于等于它



2. 结合“第  k  大”的逻辑

数组的“第  k  大”元素,等价于排序后数组中从后往前数的第  k  个元素。

- 假设数组长度为  len ,排序后最后一个元素是第 1 大,倒数第二个是第 2 大,……,倒数第  k  个就是第  k  大。
- 因此,“第  k  大的位置”对应数组的索引为  len - k (数组从 0 开始计数)。

3. 代码中两行的联动

- 第一步: ranges::nth_element(a, a.end() - k)  将  a  中索引为  a.size() - k  的元素,调整为“排序后该位置应有的值”(即第  k  大的元素)。
- 第二步: return a[a.size() - k];  直接返回该位置的元素,即为矩阵所有子矩阵异或和中的第  k  大值。

简单来说, nth_element  通过部分排序,直接定位到“第  k  大”的位置,无需对整个数组完全排序(时间复杂度为  O(N) ,远优于完全排序的  O(N \log N) ),效率更高

 

lc767

pq

pq每次选出现次数最多的字符来排

若当前字符能放就放

不能放就选次多的放,以此重构字符串,保证相邻字符不同,若无法做到则返回空。

class Solution {

public:
string reorganizeString(string s) {
int n = s.size();
unordered_map<char, int> hash;
// 统计每个字符的出现次数
for (auto c : s) {
hash[c]++;

if (hash[c] > (n + 1) / 2) {
return "";

}
}
// 优先队列(最大堆),按字符出现次数从大到小排列
priority_queue<pair<int, char>> pq;
for (auto& [c, cnt] : hash) {
pq.push({cnt, c});
}


string res;
while (!pq.empty()) {
auto [cnt1, c1] = pq.top();
pq.pop();
// 如果结果字符串为空,或者当前字符与结果字符串最后一个字符不同
if (res.empty() || c1 != res.back()) {
res += c1;
if (--cnt1 > 0) {
pq.push({cnt1, c1});

}
} else {
// 如果当前字符不能放,取次多的字符
if (pq.empty()) 
return "";

auto [cnt2, c2] = pq.top();
pq.pop();
res += c2;
if (--cnt2 > 0) 
pq.push({cnt2, c2});

pq.push({cnt1, c1});//放回
}
}
return res;

   }
};

 

lc640

求解一元一次方程

拆解等号左右两边的字符串,分别计算出x的系数和常数项

再移项计算x的值

同时处理“无穷解”和“无解”的情况

 class Solution {

public:

    pair<int, int> calcnt(string str){

        int a = 0, b = 0;

        if(str[0] != '+' && str[0] != '-') str = '+' + str; //前导补齐符号,方便统一处理

        

        int c = 1; //当前的符号是正(1)或负(0)

        int n = str.size();

        for(int i = 0; i < n; i ++){

            if(str[i] == '+'){

                c = 1;

                continue;

            }else if(str[i] == '-'){

                c = 0;

                continue;

            }else if(str[i] == 'x'){

                if(c == 1) a ++;

                else a --;

                continue;

            }else{

                int j = i;

                int t = 0;

                while(j < n && isdigit(str[j])){

                    t = t * 10 + (str[j] - '0');

                    j ++;

                }

                if(c == 0) t = -t;

                if(str[j] == 'x'){ //混合数字和x的情况, 例如"+2x"

                    a += t;

                    i = j;

                }else{

                    b += t;

                    i = j - 1;

                }

            }

        }

        return {a, b};

    }

 

    string solveEquation(string equation) {

        int x = equation.find('=');

        string ls = equation.substr(0, x), rs = equation.substr(x + 1);

        auto lres = calcnt(ls), rres = calcnt(rs);

        int a = lres.first - rres.first, b = rres.second - lres.second;

        if(!a){

            if(!b) return "Infinite solutions";

            else return "No solution";

        }

        return "x=" + to_string(b / a);

    }

};

这个地方的计算处理可以多看几遍🤔

 

 

lc632

pq维护各列表当前元素

不断更新区间左右端点

找到包含所有列表至少一个元素的最小区间

 class Solution {

public:

    vector<int> smallestRange(vector<vector<int>>& nums)

{

        priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<>> pq;

        int r = INT_MIN;

        for (int i = 0; i < nums.size(); i++)

  {

            pq.emplace(nums[i][0], i, 0); // 把每个列表的第一个元素入堆

            r = max(r, nums[i][0]);

        }

        int ans_l = get<0>(pq.top()); // 第一个合法区间的左端点

        int ans_r = r; // 第一个合法区间的右端点

 

        while (true) {

            auto [_, i, j] = pq.top();

            if (j + 1 == nums[i].size()) { // 堆顶列表没有下一个元素

                break;

            }

            pq.pop();

            int x = nums[i][j + 1]; // 堆顶列表的下一个元素

            pq.emplace(x, i, j + 1); // 入堆

            r = max(r, x); // 更新合法区间的右端点

            int l = get<0>(pq.top()); // 当前合法区间的左端点

            if (r - l < ans_r - ans_l) {

                ans_l = l;

                ans_r = r;

            }

        }

        return {ans_l, ans_r};

    }

};

 

lc1654

确定bound后bfs memo

 

 把数轴分成非安全区、安全区、出界区三段

假设存在最少跳跃次数的路径里有第一个出界的点  C  和第一个返回界内的点  H ,通过交换相关跳跃线段(把第  i + 1  次后跳、第  i + 6  次前跳交换),能让原来出界的点不再出界,还不增加跳跃次数、不违反连续后跳规则

这样就能把所有点限制在  [0, max(f + a + b, x)]  范围内,保证找最少跳跃次数时不用考虑出界情况

 class Solution {

public:

    int minimumJumps(vector<int>& forbidden, int a, int b, int x) {

        // 最远距离 bound = max(F + a + b, x + b)

        int F = *max_element(forbidden.begin(), forbidden.end()), bound = max(F + a + b, x + b);

 

        int ban[bound + 1];

        memset(ban, 0, sizeof(ban));

        for(int f : forbidden) {

            ban[f] = 1;

        }

        int dist[bound + 1][2]; // dist[i][0] - 上一次前跳, dist[i][1] - 上一次后跳

        memset(dist, 0x3f, sizeof(dist));

        dist[0][0] = 0;

        queue<pair<int,int>> q({{0, 0}});

 

        while(q.size()) {

            auto [i, pre] = q.front(); q.pop();

            if(i == x) 

                return dist[i][pre];

            

            if(pre == 0 && i-b >= 0 && !ban[i-b] && dist[i][pre] + 1 < dist[i-b][1]) {

                dist[i-b][1] = dist[i][pre] + 1;

                q.emplace(i-b, 1);

            }

            if(i+a <= bound && !ban[i+a] && dist[i][pre] + 1 < dist[i+a][0]) {

                dist[i+a][0] = dist[i][pre] + 1;

                q.emplace(i+a, 0);

            }

        }

        return -1;

    }

};

 

 

lc653

class Solution 

{

    unordered_set<int> hash;

    int k;

    bool f=false;

public:

    bool findTarget(TreeNode* root, int k) {

        this->k=k;

        dfs(root);

        return f;

    }

    

    void dfs(TreeNode* node)

    {

        if(!node) return;

        int t=k-node->val;

        if(hash.count(t))

        {

            f=true;

            return;

        }

        else

            hash.insert(node->val);

        

        dfs(node->left);

        dfs(node->right);

    }

};

优化

减少成员变量、提前终止遍历、规范代码风格三个维度改进,可读性和效率更优:

class Solution {

public:

    bool findTarget(TreeNode* root, int k) {

        unordered_set<int> valSet;

        // 用引用传递集合,避免拷贝;返回bool实现提前终止

        return dfs(root, k, valSet);

    }

 

private:

    // 私有辅助函数,封装遍历逻辑,返回值标识是否已找到目标

    bool dfs(TreeNode* node, int k, unordered_set<int>& valSet) {

        if (!node) return false;

        

        if (valSet.count(k - node->val)) return true;

        valSet.insert(node->val);

     

        return dfs(node->left, k, valSet) || dfs(node->right, k, valSet);

    }

};

 

核心优化点

1. 移除冗余成员变量:将  hash 、 k 、 f  改为函数内/参数传递,避免类成员状态管理,代码更简洁。

2. 实现提前终止:

- 辅助函数返回  bool ,找到目标值时直接返回  true ,不再继续遍历后续节点。

- 利用  ||  短路特性,左右子树只要有一个找到结果,就停止递归。

3. 规范访问控制:辅助函数  dfs  设为  private ,符合类的封装原则,对外隐藏实现细节。

 

http://www.dtcms.com/a/483334.html

相关文章:

  • 西安商城网站开发品牌营销型网站建设
  • vector,咕咕咕!
  • <数据集>药丸缺陷识别数据集<目标检测>
  • Java 在AWS上使用SDK凭证获取顺序
  • 百度手机网站自助建站青岛硅谷网站建设
  • 做响应式网站的物流微信商城网站案例展示
  • 【java】springboot分库分表 12 种分片算法(下)
  • Java多线程编程实战技巧深度解析:从并发基础到高吞吐架构的艺术
  • Nginx 自动化脚本安装方案
  • 巩义网站建设托管邹平建设项目网站公示
  • ETF网格交易覆盖率缺口与满仓踏空风险量化模型
  • 数据结构入门 (八):寻访节点的“路线图” —— 二叉树的深度与广度遍历
  • 复兴区建设局网站永久免费云储存空间
  • 线程池参数调整
  • Unity学习之Addressables可寻址系统
  • SQL优化手段有哪些
  • 建设银行江苏分行网站一键生成图片
  • php框架做网站的好处福田网站制作
  • 合并多个excel到一个excel中
  • 多因子模型识别供需共振:AI量化系统捕捉“白银突破52美元”的结构性动能
  • 网站建设公司哪里好企业微信小程序制作
  • 【前端学习】仿Deepseek官网AI聊天网站React
  • 2018年10月四川省自考《信息组织》试题
  • 统计期刊介绍——Journal of Statistical Software(JSS)
  • 漳州开发区人才网北京网站优化公司
  • Datawhale_数学建模导论_笔记
  • 【机器人学中的状态估计】2.5.1习题:假设u,v是两个相同维度的列向量,请你证明u^Tv=tr(vu^T)
  • 解决 Anki 启动器下载错误的完整指南
  • 烟台百度做网站多少钱怎么制作钓鱼网站链接
  • 做网站多少钱一般wordpress安装插件需要ftp