LeetCode 611. 有效三角形的个数

原题链接:611. 有效三角形的个数 - 力扣(LeetCode)
题目说,给定一个包含非负整数的数组 num,返回其中可以组成三角形三条边的三元组个数。
示例: nums = [4, 2, 3, 4];

有效组合如下:

2,3,4 (使用第一个4)

2,3,4 (使用第二个4)

2,4,4

3,4,4
做题思路:

可能想到的第一种方案就是暴力枚举,枚举出所有的情况,比如下面的代码:

cpp 复制代码
int sum = 0;
for (int i = 0; i < n; ++i)
{
	for (int j = i + 1; j < n; ++j)
	{
		for (int k = j + 1; k < n; ++k)
		{
			if (IsValid(v[i], v[j], v[k]))	++sum;
		}
	}
}

上面的暴力枚举的时间复杂度是 O(N^3)。 我们思考一下,能不能优化一下?
优化一:判断 a,b,c 三个数字能否构成一个合法三角形

首先,如果数据不是有序的,那么判断需要通过三次,即:

  • a + b > c;
  • a + c > b;
  • b + c > a;

但如果此时数据是有序的,假如, a <= b <= c,那么此时判断能否构成一个合法三角形,只需要判断一次,即:

a + b > c

而 a + c > b 和 b + c > a 不需要判断,因为此时c是最大的数据,最大的数据加上一个非负数,一定大于另外的一个数据,比如,a + c 是一定大于 b 的。

这是优化一。
优化二,我们想通过双指针 + 单调性的方案优化上面的问题,通过一个示例来理解一下:
假设数据为2,3,4,6,6,7,9,让我们来判断一下可以构成三角形的个数:
首先,我们固定一个值,然后判断,剩下的数字和这个固定值能构成的三角形个数是多少,比如,我们固定最大值为9,然后判断剩下的数据和这个9能构成的三角形个数是多少个,如图所示:

因为此时 a = 2, b = 7, c = 9,它们是有序的数据,故只需要判断一次:a + b > c,不成立,既然最小的 a 不能构成一个三角形,那么就让a向右移动,如下:

此时 a = 3,b = 7,c = 9,同理,只需要判断一次,a + b > c , 成立,因为数据是有序的,而此时在 3 到 7 这个区间中,3 是最小的数据,那么4,6,6,毋庸置疑,也可以和 b = 7,c = 9 构成一个三角形, 故4,6,6, 不需要再判断,因此,当 c = 9,b = 7 时,能构成的三角形个数就等于b所在的下标 - a所在的下标,此时b = 7这种情况就讨论完了,因此,b 向左移动:

此时 a = 3, b = 6,c = 9, a + b > c,不成立,因此,a 向右移动:

a = 4, b = 6, c = 9,a + b > c,成立,因为在a到b这个区间中,a此时是最小的,而数据又是单调递增 (有序的),因此,在 a 和 b 中间的值就不需要再判断了,此时一定可以和 b = 6,c = 9 构成三角形,因此,此时可以构成三角形的个数:b的下标 - a 的下标,b向左移动。

此时,和上述情况一样,可以构成三角的个数:b的下标 - a的下标 ,b向左移动:

a 和 b 相遇,c = 9 这种情况能构成的三角形的个数就讨论完了。此时,我们在固定一个值,即让c向左移动,让 c == 7,然后重复上述过程,如下所示:

此时,我们就可以将一组数据可以构成三角型的个数全部讨论清楚,可以发现,上面这个思路的时间复杂度:O(N^2),因为c从最后一个数据开始,需要从后向前遍历数组(O(N)),而每一个c都需要让a和b一起遍历 (a向右移动,b向左移动) 数组,故也是O(N),因此上面算法的时间复杂度为 O(N^2),这就是通过单调性 + 双指针解决上述问题。

代码如下:

cpp 复制代码
#include <iostream>
#include <vector>

class Solution {
public:
	inline bool IsValid(int a, int b, int c)
	{
		return a + b > c;
	}

	int triangleNumber(std::vector<int>& nums) {
        // 这里的 left 相当于a
        // 这里的 right 相当于b
        // 这里的 objpos 相当于c
        // 它们都是数组下标
        std::sort(nums.begin(), nums.end());
		int objpos = nums.size() - 1;
		int sum = 0;
		while (objpos > 1)
		{
			int left = 0;
			int right = objpos - 1;
			while (left < right)
			{
				if (IsValid(nums[left], nums[right], nums[objpos]))
				{
					sum += (right - left);
					right--;
				}
				else
				{
					left++;
				}
			}
			objpos--; 
		}
		return sum;
	}
};
相关推荐
CoovallyAIHub2 小时前
CVPR 2026 | MixerCSeg:仅2.05 GFLOPs刷新四大裂缝分割基准!解耦Mamba隐式注意力,CNN+Transformer+Mamba三
深度学习·算法·计算机视觉
CoovallyAIHub2 小时前
YOLO26-Pose 深度解读:端到端架构重新设计,姿态估计凭什么跨代领先?
深度学习·算法·计算机视觉
CoovallyAIHub3 小时前
化工厂气体泄漏怎么用AI检测?30张图3D重建气体泄漏场景——美国国家实验室NeRF新研究
深度学习·算法·计算机视觉
颜酱14 小时前
图的数据结构:从「多叉树」到存储与遍历
javascript·后端·算法
zone773920 小时前
006:RAG 入门-面试官问你,RAG 为什么要切块?
后端·算法·面试
CoovallyAIHub1 天前
OpenClaw 近 2000 个 Skills,为什么没有一个好用的视觉检测工具?
深度学习·算法·计算机视觉
CoovallyAIHub1 天前
CVPR 2026 | 用一句话告诉 AI 分割什么——MedCLIPSeg 让医学图像分割不再需要海量标注
深度学习·算法·计算机视觉
CoovallyAIHub1 天前
Claude Code 突然变成了 66 个专家?这个 5.8k Star 的开源项目,让我重新理解了什么叫"会用 AI"
深度学习·算法·计算机视觉
兆子龙1 天前
前端哨兵模式(Sentinel Pattern):优雅实现无限滚动加载
前端·javascript·算法
CoovallyAIHub1 天前
9个视觉语言模型工厂实测:Qwen 87.9%碾压全场,你的显卡能跑哪个?
算法