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

这篇具有很好参考价值的文章主要介绍了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++二分查找算法:有序矩阵中的第 k 个最小数组和,数据结构与算法,# 算法题,算法,c++,矩阵,二分查找,有序矩阵,第K小,数组和

本文涉及的基础知识点

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频文章来源地址https://www.toymoban.com/news/detail-752827.html

到了这里,关于C++二分查找算法:有序矩阵中的第 k 个最小数组和的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法训练-数组 五】【二分查找】:旋转排序数组的最小数字、旋转排序数组的指定数字

    废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【数组的二分查找】,使用【数组】这个基本的数据结构来实现,这个高频题的站点是: CodeTop ,筛选条件为: 目标公司+最近一年+出现频率排序 ,由高到低的去 牛客TOP101 去找,只有两

    2024年02月09日
    浏览(50)
  • 算法训练第二天|977.有序数组的平方、209.长度最小的有序数组、59.螺旋矩阵2

    题目链接:力扣 思路:同样使用双指针的方法,这样就可以只遍历一次原数组。 可以考虑需要按照一个顺序来遍历,那就是从大到小或者从小到大,我选择的是从大到小。 不难看出,原数组将每个数平方后,呈现从两边到中间逐渐减小的规律。 所以使用一个指针指向原数组

    2023年04月22日
    浏览(54)
  • C++二分算法(二分查找&二分答案)细节详解

     二分算法可以分为 二分查找 和 二分答案 。 以在一个 升序数组 中查找一个数为例。它每次考察数组当前部分的 中间元素 ,如果中间元素刚好是要找的,就结束搜索过程;如果中间元素小于所查找的值,那么左侧的只会更小,不会有所查找的元素,只需到右侧查找;如果

    2024年02月08日
    浏览(52)
  • 算法-有序数组的平方,长度最小的子数组,螺旋矩阵II

    伪装成一个老手! 题目 给你一个按 非递减顺序 排序的整数数组 nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。 示例 : 输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100] 排序后,数组变为 [0,1,9,16,100] 来源:力扣977 思路 遍

    2024年02月11日
    浏览(41)
  • 二分查找算法讲解及其C++代码实现

    二分查找算法是一种常用的查找算法,也被称为折半查找。它可以在有序的数组或列表中快速查找需要的元素。 算法描述: 首先确定数组的中间位置mid=(left+right)/2; 然后将要查找的值key与中间位置的值进行比较; 如果key等于中间位置的值,则查找成功,返回mid; 如果key小

    2024年02月01日
    浏览(43)
  • C++二分查找算法:132 模式枚举3

    本篇是视频课程的讲义,可以看直接查看视频。也可以下载源码,包括空源码。 二分查找算法合集 包括题目及代码 C++二分查找算法:132 模式解法一枚举3 C++二分查找算法:132 模式解法二枚举2 代码最简洁 C++二分查找算法:132 模式解法三枚举1 性能最佳 C++单调向量算法:1

    2024年02月04日
    浏览(40)
  • 【C++代码】有序数组的平方,长度最小的子数组,螺旋矩阵 II--代码随想录

    题目:有序数组的平方 给你一个按 非递减顺序 排序的整数数组 nums ,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。 题解 数组其实是有序的, 只不过负数平方之后可能成为最大数了。那么数组平方的最大值就在数组的两端, 不是最左边就是最右边,不

    2024年02月11日
    浏览(44)
  • 【算法】在二维不单调的矩阵上二分查找——力扣1901. 寻找峰值 II

    1901. 寻找峰值 II 给定一个从0开始编号的m x n矩阵 mat ,其中任意两个相邻格子的值都不相同。峰值是指那些严格大于其相邻格子(上、下、左、右)的元素。需要找出任意一个峰值 mat[i][j] 并返回其位置 [i, j] 。 示例 1: 示例 2: 步骤一:列转行 首先,将矩阵的列转换为行,表示为

    2024年02月03日
    浏览(49)
  • 初阶算法(3):二分法的讲解与实现(C语言),以及二分不止光在有序数组中的应用

     第一章 初阶算法(1):通过简单的排序算法来认识时间复杂度  第二章 初阶算法(2):进行详细地介绍插入排序的细节和时间复杂度  第三章 初阶算法(3):二分法的讲解与实现,以及二分不止光在有序数组中的应用 目录 系列文章目录 前言 一、二分法的讲解与实现

    2024年02月14日
    浏览(42)
  • C++二分查找算法的应用:最长递增子序列

    C++二分算法应用:最长递增子序列 二分查找算法合集 单调映射 点击下载源码 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子

    2024年02月06日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包