Skip to content

Commit 6c1b81a

Browse files
author
lucifer
committed
feat: $611
1 parent 77214ac commit 6c1b81a

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ leetcode 题解,记录自己的 leetcode 解题之路。
261261
- [0547.friend-circles](./problems/547.friend-circles-en.md)
262262
- [0560.subarray-sum-equals-k](./problems/560.subarray-sum-equals-k.md)
263263
- [0609.find-duplicate-file-in-system](./problems/609.find-duplicate-file-in-system.md)
264+
- [0611.valid-triangle-number](./problems/611.valid-triangle-number.md)
264265
- [0718.maximum-length-of-repeated-subarray](./problems/718.maximum-length-of-repeated-subarray.md)
265266
- [0785.is-graph-bipartite](./problems/785.is-graph-bipartite.md) 🆕
266267
- [0820.short-encoding-of-words](./problems/820.short-encoding-of-words.md) 🖊

problems/611.valid-triangle-number.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
## 题目地址(611. 有效三角形的个数)
2+
3+
https://leetcode-cn.com/problems/valid-triangle-number/
4+
5+
## 题目描述
6+
7+
```
8+
给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。
9+
10+
示例 1:
11+
12+
输入: [2,2,3,4]
13+
输出: 3
14+
解释:
15+
有效的组合是:
16+
2,3,4 (使用第一个 2)
17+
2,3,4 (使用第二个 2)
18+
2,2,3
19+
注意:
20+
21+
数组长度不超过1000。
22+
数组里整数的范围为 [0, 1000]。
23+
24+
```
25+
26+
## 前置知识
27+
28+
- 排序
29+
- 双指针
30+
- 二分法
31+
- 三角形边的关系
32+
33+
## 暴力法(超时)
34+
35+
### 思路
36+
37+
首先要有一个数学前提: `如果三条线段中任意两条的和都大于第三边,那么这三条线段可以组成一个三角形`。即给定三个线段 a,b,c,如果满足 a + b > c and a + c > b and b + c > a,则线段 a,b,c 可以构成三角形,否则不可以。
38+
39+
力扣中有一些题目是需要一些数学前提的,不过这些数学前提都比较简单,一般不会超过高中数学知识,并且也不会特别复杂。一般都是小学初中知识即可。
40+
41+
> 如果你在面试中碰到不知道的数学前提,可以寻求面试官提示试试。
42+
43+
### 关键点解析
44+
45+
- 三角形边的关系
46+
- 三层循环确定三个线段
47+
48+
### 代码
49+
50+
代码支持: Python
51+
52+
```py
53+
class Solution:
54+
def is_triangle(self, a, b, c):
55+
if a == 0 or b == 0 or c == 0: return False
56+
if a + b > c and a + c > b and b + c > a: return True
57+
return False
58+
def triangleNumber(self, nums: List[int]) -> int:
59+
n = len(nums)
60+
ans = 0
61+
for i in range(n - 2):
62+
for j in range(i + 1, n - 1):
63+
for k in range(j + 1, n):
64+
if self.is_triangle(nums[i], nums[j], nums[k]): ans += 1
65+
66+
return ans
67+
```
68+
69+
**复杂度分析**
70+
71+
- 时间复杂度:$O(N ^ 3)$,其中 N 为 数组长度。
72+
- 空间复杂度:$O(1)$
73+
74+
## 优化的暴力法
75+
76+
### 思路
77+
78+
暴力法的时间复杂度为 $O(N ^ 3)$, 其中 $N$ 最大为 1000。一般来说, $O(N ^ 3)$ 的算法在数据量 <= 500 是可以 AC 的。1000 的数量级则需要考虑 $O(N ^ 2)$ 或者更好的解法。
79+
80+
OK,到这里了。我给大家一个干货。 应该是其他博主不太会提的。原因可能是他们不知道, 也可能是他们觉得太小儿科不需要说。
81+
82+
1. 由于前面我根据数据规模推测到到了解法的复杂度区间是 $N ^ 2$, $N ^ 2 * logN$,不可能是 $N$ (WHY?)。
83+
2. 降低时间复杂度的方法主要有: `空间换时间``排序换时间`(我们一般都是使用基于比较的排序方法)。而`排序换时间`仅仅在总体复杂度大于 $O(NlogN)$ 才适用(原因不用多说了吧?)。
84+
85+
这里由于总体的时间复杂度是 $O(N ^ 3)$,因此我自然想到了`排序换时间`。当我们对 nums 进行一次排序之后,我发现:
86+
87+
- is_triangle 函数有一些判断是无效的
88+
89+
```py
90+
def is_triangle(self, a, b, c):
91+
if a == 0 or b == 0 or c == 0: return False
92+
# a + c > b 和 b + c > a 是无效的判断,因为恒成立
93+
if a + b > c and a + c > b and b + c > a: return True
94+
return False
95+
```
96+
97+
- 因此我们的目标变为找到`a + b > c`即可,因此第三层循环是可以提前退出的。
98+
99+
```py
100+
for i in range(n - 2):
101+
for j in range(i + 1, n - 1):
102+
k = j + 1
103+
while k < n and num[i] + nums[j] > nums[k]:
104+
k += 1
105+
ans += k - j - 1
106+
```
107+
108+
- 这也仅仅是减枝而已,复杂度没有变化。通过进一步观察,发现 k 没有必要每次都从 j + 1 开始。而是从上次找到的 k 值开始就行。原因很简单, 当 nums[i] + nums[j] > nums[k] 时,我们想要找到下一个满足 nums[i] + nums[j] > nums[k] 的 新的 k 值,由于进行了排序,因此这个 k 肯定比之前的大(单调递增性),因此上一个 k 值之前的数都是无效的,可以跳过。
109+
110+
```py
111+
for i in range(n - 2):
112+
k = i + 2
113+
for j in range(i + 1, n - 1):
114+
while k < n and nums[i] + nums[j] > nums[k]:
115+
k += 1
116+
ans += k - j - 1
117+
```
118+
119+
由于 K 不会后退,因此最内层循环总共最多执行 N 次,因此总的时间复杂度为 $O(N ^ 2)$。
120+
121+
> 这个复杂度分析有点像单调栈,大家可以结合起来理解。
122+
123+
### 关键点分析
124+
125+
- 排序
126+
127+
### 代码
128+
129+
```py
130+
class Solution:
131+
def triangleNumber(self, nums: List[int]) -> int:
132+
n = len(nums)
133+
ans = 0
134+
nums.sort()
135+
for i in range(n - 2):
136+
if nums[i] == 0: continue
137+
k = i + 2
138+
for j in range(i + 1, n - 1):
139+
while k < n and nums[i] + nums[j] > nums[k]:
140+
k += 1
141+
ans += k - j - 1
142+
return ans
143+
```
144+
145+
**复杂度分析**
146+
147+
- 时间复杂度:$O(N ^ 2)$
148+
- 空间复杂度:取决于排序算法
149+
150+
更多题解可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 30K star 啦。
151+
152+
关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。
153+
154+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg)

0 commit comments

Comments
 (0)