C++二分查找算法:有序矩阵中的第 k 个最小数组和

本文涉及的基础知识点

二分查找算法合集

本题的简化

C++二分查找算法:查找和最小的 K 对数字 十分接近m恒等于2

题目

给你一个 m * n 的矩阵 mat,以及一个整数 k ,矩阵中的每一行都以非递减的顺序排列。
你可以从每一行中选出 1 个元素形成一个数组。返回所有可能数组中的第 k 个 最小 数组和。
示例 1:
输入:mat = [[1,3,11],[2,4,6]], k = 5
输出:7
解释:从每一行中选出一个元素,前 k 个和最小的数组分别是:
[1,2], [1,4], [3,2], [3,4], [1,6]。其中第 5 个的和是 7 。
示例 2:
输入:mat = [[1,3,11],[2,4,6]], k = 9
输出:17
示例 3:
输入:mat = [[1,10,10],[1,4,5],[2,3,6]], k = 7
输出:9
解释:从每一行中选出一个元素,前 k 个和最小的数组分别是:
[1,1,2], [1,1,3], [1,4,2], [1,4,3], [1,1,6], [1,5,2], [1,5,3]。其中第 7 个的和是 9 。
示例 4:
输入:mat = [[1,1,10],[2,2,9]], k = 7
输出:12
参数范围
m == mat.length
n == mat.length[i]
1 <= m, n <= 40
1 <= k <= min(200, n ^ m)
1 <= mat[i][j] <= 5000
mat[i] 是一个非递减数组

分析

时间复杂度

O(mlog(500040)n+mkn)。GetLessKSum被调用m次,GetLessEqualSumNum共被调用mlog(500040)次。每次调用GetLessEqualSumNum,for循环共执行m次。
vRet.emplace_back极端情况下,可能被执行k
n次。

主要函数介绍

GetLessKSum两行升序数据的最小k个和
GetLessEqualSumNum两行升序数据和小于等于iSum的组合数量

注意:nums[i]为正数,所以如果pre的数量大于k,只需要保留前k小,其它的被淘汰了。

二分

寻找第一个符合条件的iSum,条件如下:
和小于等于iSum的组合数量大于等于k。

代码

核心代码

class Solution {
public:int kthSmallest(vector<vector<int>>& mat, int k) {m_c = mat.front().size();m_iK = k;vector<int> pre = mat[0];for (int r = 1; r < mat.size(); r++){pre = GetLessKSum(pre, mat[r]);}return pre.back();}vector<int> GetLessKSum(const vector<int>& pre, const vector<int>& cur){int left = 0, right = 5000 * 40;while (right - left > 1){const auto mid = left + (right - left) / 2;if (GetLessEqualSumNum(pre, cur, mid)>= m_iK){right = mid;}else{left = mid;}}vector<int> vRet;for (const auto& pr : pre){for (const auto& cu : cur){if (pr + cu <= right){vRet.emplace_back(pr + cu);}else{break;}}}sort(vRet.begin(), vRet.end());if (vRet.size() > m_iK){vRet.erase(vRet.begin() + m_iK, vRet.end());}return vRet;}int GetLessEqualSumNum(const vector<int>& pre, const vector<int>& cur,int iSum){int iNum = 0;for (const auto& pr : pre){iNum += std::upper_bound(cur.begin(), cur.end(), iSum - pr)- cur.begin();}return iNum;}int m_iK;int m_c;
};

测试用例

template
void Assert(const T& t1, const T& t2)
{
assert(t1 == t2);
}

template
void Assert(const vector& v1, const vector& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
Assert(v1[i], v2[i]);
}
}

int main()
{
vector<vector> mat;
int k;
int res;
{
Solution slu;
mat = { {1,3,11},{2,4,6} };
k = 5;
res = slu.kthSmallest(mat, k);
Assert(7, res);
}
{
Solution slu;
mat = { {1,3,11},{2,4,6} };
k = 9;
res = slu.kthSmallest(mat, k);
Assert(17, res);
}
{
Solution slu;
mat = { {1,10,10},{1,4,5},{2,3,6} };
k = 7;
res = slu.kthSmallest(mat, k);
Assert(9, res);
}
{
Solution slu;
mat = { {1,1,10},{2,2,9} };
k = 7;
res = slu.kthSmallest(mat, k);
Assert(12, res);
}

//CConsole::Out(res);

}

优化增加结果

vector<int> vRet;for (const auto& pr : pre){for (const auto& cu : cur){if (pr + cu < right){vRet.emplace_back(pr + cu);}else{break;}}}while (vRet.size() < m_iK){vRet.emplace_back(right);}

和小于right的数量<=k,如果不足够,则补right。时间复杂度由O(nk)降低到O(k+n)。

直接使用封装类

namespace NBinarySearch
{template<class INDEX_TYPE,class _Pr>INDEX_TYPE FindFrist(INDEX_TYPE left, INDEX_TYPE right, _Pr pr){while (right - left > 1){const auto mid = left + (right - left) / 2;if (pr(mid)){right = mid;}else{left = mid;}}return right;}
}class Solution {
public:int kthSmallest(vector<vector<int>>& mat, int k) {m_c = mat.front().size();m_iK = k;vector<int> pre = mat[0];for (int r = 1; r < mat.size(); r++){pre = GetLessKSum(pre, mat[r]);}return pre.back();}vector<int> GetLessKSum(const vector<int>& pre, const vector<int>& cur){auto GetLessEqualSumNum = [&pre, &cur, this](const int iSum)-> bool{int iNum = 0;for (const auto& pr : pre){iNum += std::upper_bound(cur.begin(), cur.end(), iSum - pr) - cur.begin();}return iNum >= m_iK;};const int right = NBinarySearch::FindFrist(0, 5000 * 40, GetLessEqualSumNum);		vector<int> vRet;for (const auto& pr : pre){for (const auto& cu : cur){if (pr + cu < right){vRet.emplace_back(pr + cu);}else{break;}}}while (vRet.size() < m_iK){vRet.emplace_back(right);}sort(vRet.begin(), vRet.end());if (vRet.size() > m_iK){vRet.erase(vRet.begin() + m_iK, vRet.end());}return vRet;}int GetLessEqualSumNum(const vector<int>& pre, const vector<int>& cur,int iSum){int iNum = 0;for (const auto& pr : pre){iNum += std::upper_bound(cur.begin(), cur.end(), iSum - pr)- cur.begin();}return iNum;}int m_iK;int m_c;
};

2023年3月暴力版

直接保留前k个。时间复杂度:O(mknlogk)
class Solution {
public:
int kthSmallest(vector<vector>& mat, int k) {
m_r = mat.size();
m_c = mat[0].size();
std::priority_queue pre;
pre.push(0);
for (int r = 0; r < mat.size(); r++)
{
std::priority_queue dp;
while (pre.size())
{
int t = pre.top();
pre.pop();
for (int c = 0; c < m_c; c++)
{
dp.push(mat[r][c] + t);
if (dp.size() > k)
{
dp.pop();
}
}
}
pre.swap(dp);
}
return pre.top();
}
int m_r, m_c;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
墨子曰:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境:

VS2022 C++17

本文涉及的基础知识点

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/196142.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多线程概述

文章目录 线程是什么线程有什么作用线程和进程的区别多线程相较于进程优势 在Java这个圈子中,多进程用的并不多,因为进程是一个重量级操作,进程是资源分配的基本单位,申请资源是一个比较消耗时间的操作. 线程是什么 线程是一个独立的执行流,可以被独立调度到CPU上执行 线程是…

Bean实例化的基本流程

Spring容器在进行初始化时&#xff0c;会将xml配置的<bean>的信息封装成一个BeanDefintion对象&#xff0c;所有的BeanDefintion存储到BeanDefintionMap的Map集合中去&#xff0c;Spring框架对该Map进行遍历&#xff0c;使用反射创建Bean实例对象&#xff0c;创建好的Bea…

Dockerfile自定义镜像以及案例分析

文章目录 一、Dockerfile自定义镜像1.1 镜像结构1.2 Dockerfile语法 二、构建Java项目三、基于java8构建java四、小结 一、Dockerfile自定义镜像 常见的镜像在DockerHub就能找到&#xff0c;但是我们自己写的项目就必须自己构建镜像了。 而要自定义镜像&#xff0c;就必须先了…

【开源】基于Vue.js的高校宿舍调配管理系统

项目编号&#xff1a; S 051 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S051&#xff0c;文末获取源码。} 项目编号&#xff1a;S051&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能需求2.1 学生端2.2 宿管2.3 老师端 三、系统…

高防CDN为什么可以防DDOS攻击

CDN的全称是ContentDeliveryNetwork&#xff0c;即内容分发网络&#xff0c;顾名思义&#xff0c;它是一个分布式节点网络(也称为边缘服务器)&#xff0c;CDN节点具有缓存内容的功能&#xff0c;使用户可以在不获取源服务器数据的情况下就近获取所需内容&#xff0c;提高客户访…

《微信小程序开发从入门到实战》学习二十

3.3 开发创建投票页面 3.3.8 使用icon图标文件 原来已经实现了投票选项的增加和修改功能&#xff0c;现在还差删除。现在为每一个选项增加删除按钮&#xff0c;可以以通过icon图标组件实现。 icon常用属性如下&#xff1a; type icon的类型&#xff0c;有success、s…

6 Redis的慢查询配置

1、redis的命令执行流程 redis的慢查询只针对步骤3 默认情况下&#xff0c;慢查询的阈值是10ms 在配置文件中进行配置 //这个参数的单位为微秒 //如果将这个值设置为负数&#xff0c;则会禁用慢日志功能 //如果将其设置为0&#xff0c;则会强制记录每个命令 slowlog-log-slow…

数据结构【DS】树与二叉树的应用

哈夫曼树 树的带权路径长度最小的二叉树WPL 路径长度【边数】 * 结点权值n个叶结点的哈夫曼树共有 2n-1 个结点 哈夫曼树的任意非叶结点的左右子树交换后仍是哈夫曼树对同一组权值&#xff0c;可能存在不同构的多棵哈夫曼树&#xff0c;但树的带权路径长度最小且唯一哈夫曼树…

斯坦福机器学习 Lecture2 (假设函数、参数、样本等等术语,还有批量梯度下降法、随机梯度下降法 SGD 以及它们的相关推导,还有正态方程)

假设函数定义 假设函数&#xff0c;猜一个 x->y 的类型&#xff0c;比如 y ax b&#xff0c;随后监督学习的任务就是找到误差最低的 a 和 b 参数 有时候我们可以定义 x0 1&#xff0c;来让假设函数的整个表达式一致统一 如上图是机器学习中的一些术语 额外的符号&#xf…

基于灰色神经网络的预测算法——订单需求预测

大家好&#xff0c;我是带我去滑雪&#xff01; 灰色系统理论的不确定性处理与神经网络的非线性建模相结合&#xff0c;有望更好地处理实际问题中的不确定性和复杂性。本期使用灰色神经网络实现预测冰箱订单需求。 一、问题背景与模型建立 &#xff08;1&#xff09;灰色理论…

测试Bard和ChatGPT关于双休的法规和推理

Bard是试验品&#xff0c;chatgpt是3.5版的。 首先带着问题&#xff0c;借助网络搜索&#xff0c;从政府官方网站等权威网站进行确认&#xff0c;已知正确答案的情况下&#xff0c;再来印证两个大语言模型的优劣。 想要了解的问题是&#xff0c;在中国&#xff0c;跟法定工作…

达标进度条

1.效果 1. 2.代码与使用 1.自定义组合控件 kotlin import android.annotation.SuppressLint import android.content.Context import android.graphics.drawable.Drawable import android.util.AttributeSet import android.view.Gravity import android.view.LayoutInflat…