题目
给你一个整数数组 arr ,以及 a、b 、c 三个整数。请你统计其中好三元组的数量。
如果三元组 (arr[i], arr[j], arr[k]) 满足下列全部条件,则认为它是一个 好三元组 。
0 <= i < j < k < arr.length|arr[i] - arr[j]| <= a|arr[j] - arr[k]| <= b|arr[i] - arr[k]| <= c
其中 |x| 表示 x 的绝对值。
返回 好三元组的数量 。
示例 1:
输入:arr = [3,0,1,1,9,7], a = 7, b = 2, c = 3
输出:4
解释:一共有 4 个好三元组:[(3,0,1), (3,0,1), (3,1,1), (0,1,1)] 。
示例 2:
输入:arr = [1,1,2,2,3], a = 0, b = 0, c = 1
输出:0
解释:不存在满足所有条件的三元组。
bash
#include <stdlib.h>
int countGoodTriplets(int* arr, int arrSize, int a, int b, int c) {
int count = 0;
for(int i = 0;i < arrSize;i++){
for(int j = i + 1;j < arrSize;j++){
for(int k = j+1;k < arrSize;k++){
if(abs(arr[i] - arr[j]) <= a
&& abs(arr[j] - arr[k]) <= b
&& abs(arr[i] - arr[k]) <= c){
count++;
}
}
}
}
return count;
}
python
def countGoodTriplets(arr, a, b, c):
# 初始化计数器,统计合格三元组数目
count = 0
# 获取数组长度,后续循环边界使用
n = len(arr)
# i:第一个元素下标
for i in range(n):
# j:第二个下标,必须在i之后
for j in range(i + 1, n):
# 剪枝,i‑j差值超标,直接跳过当前j,省去k循环
if abs(arr[i] - arr[j]) > a:
continue
# k:第三个下标,必须在j之后,天然 i<j<k
for k in range(j + 1, n):
# 校验剩余两个绝对值条件
if abs(arr[j] - arr[k]) <= b and abs(arr[i] - arr[k]) <= c:
count += 1
# 返回结果
return count
# 测试示例代码
if __name__ == "__main__":
test_arr = [3,0,1,1,9,7]
print(countGoodTriplets(test_arr,7,2,3))
cpp
#include <vector>
#include <cmath> // abs绝对值函数头文件
using namespace std;
class Solution {
public:
int countGoodTriplets(vector<int>& arr, int a, int b, int c) {
// count:用来记录符合条件好三元组的总数
int count = 0;
// i代表三元组第一个元素的下标
for (int i = 0; i < arr.size(); i++)
{
// j必须大于i,第二个元素下标,保证 i < j
for (int j = i + 1; j < arr.size(); j++)
{
// 剪枝:如果i,j已经不满足条件,直接跳过该j,不用遍历k循环,节省性能
if (abs(arr[i] - arr[j]) > a)
{
continue;
}
// k必须大于j,第三个元素下标,保证 i<j<k
for (int k = j + 1; k < arr.size(); k++)
{
// 判断剩下两条绝对值约束
if (abs(arr[j] - arr[k]) <= b && abs(arr[i] - arr[k]) <= c)
{
count ++; // 满足全部条件,计数器 +1
}
}
}
}
// 循环结束,返回最终合格三元组数量
return count;
}
};
i 从左往右固定第一个元素
j 固定在 i 之后,先检验 i,j 的差值,如果超过 a,直接跳过该 j,不用遍历 k(剪枝优化)
k 固定在 j 之后,检验 j‑k、i‑k 的绝对值条件
全部条件成立,计数器 + 1
语法问题:为什么python不用arrsize
C/C++ 需要手动传入数组长度
int countGoodTriplets(int* arr, int arrSize, int a, int b, int c)
- C 的 int* arr 只是内存首地址指针
- 指针本身不知道数组一共有多少个元素
- 所以必须额外传参数 arrSize,告知数组长度,用来控制循环终止条件
- 如果你没有长度,程序不知道循环什么时候停下,会访问越界内存
C++ vector
vector<int>& arr
vector 是封装好的容器,可以调用 **arr.size()**获取内部存储元素数量,不需要单独传长度参数。
Python 列表本身自带属性
def countGoodTriplets(arr, a, b, c):
n = len(arr)
- Python 的 arr 是 list 对象(容器),不是 C 语言那种光秃秃的内存指针;
- 对象内部已经保存好了元素总数;
- 内置函数 len(列表) 随时可以读取列表长度;
- 函数不需要单独接收数组大小参数,随时**len(arr)**即可拿到。
cpp
vector<int>& arr
vector:C++ 标准库动态数组容器,相当于 Python 的 list、C 语言可变数组
<int>:容器里面存放的数据类型是 int‑整型
& = 引用
arr 是参数名字
整体含义:接收一个存放 int 的 vector 容器的引用
& 引用到底干什么
写法 1(不带引用:值传递)
cpp
vector<int> arr
函数调用的时候,把原数组完整复制一份副本给到函数内 arr
arr 只是拷贝出来的复制品 ;函数内部修改 arr,外面原数组不受影响
数组很大时拷贝非常消耗内存、速度慢
写法 2(带 &,引用传递,也就是别名)
cpp
vector<int>& arr
arr 只是外面原 vector 的别名、外号,它们指向同一块内存。
不会复制整个数组,只是绑定原对象
速度快,零拷贝
在函数内修改 arr,外面原本的数组也跟着改动