Skip to content

[pull] master from azl397985856:master #199

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ leetcode 题解,记录自己的 leetcode 解题之路。
- [0547.friend-circles](./problems/547.friend-circles-en.md)
- [0560.subarray-sum-equals-k](./problems/560.subarray-sum-equals-k.md)
- [0609.find-duplicate-file-in-system](./problems/609.find-duplicate-file-in-system.md)
- [0611.valid-triangle-number](./problems/611.valid-triangle-number.md)
- [0718.maximum-length-of-repeated-subarray](./problems/718.maximum-length-of-repeated-subarray.md)
- [0785.is-graph-bipartite](./problems/785.is-graph-bipartite.md) 🆕
- [0820.short-encoding-of-words](./problems/820.short-encoding-of-words.md) 🖊
Expand Down
154 changes: 154 additions & 0 deletions problems/611.valid-triangle-number.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
## 题目地址(611. 有效三角形的个数)

https://leetcode-cn.com/problems/valid-triangle-number/

## 题目描述

```
给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。
示例 1:
输入: [2,2,3,4]
输出: 3
解释:
有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3
注意:
数组长度不超过1000。
数组里整数的范围为 [0, 1000]。
```

## 前置知识

- 排序
- 双指针
- 二分法
- 三角形边的关系

## 暴力法(超时)

### 思路

首先要有一个数学前提: `如果三条线段中任意两条的和都大于第三边,那么这三条线段可以组成一个三角形`。即给定三个线段 a,b,c,如果满足 a + b > c and a + c > b and b + c > a,则线段 a,b,c 可以构成三角形,否则不可以。

力扣中有一些题目是需要一些数学前提的,不过这些数学前提都比较简单,一般不会超过高中数学知识,并且也不会特别复杂。一般都是小学初中知识即可。

> 如果你在面试中碰到不知道的数学前提,可以寻求面试官提示试试。
### 关键点解析

- 三角形边的关系
- 三层循环确定三个线段

### 代码

代码支持: Python

```py
class Solution:
def is_triangle(self, a, b, c):
if a == 0 or b == 0 or c == 0: return False
if a + b > c and a + c > b and b + c > a: return True
return False
def triangleNumber(self, nums: List[int]) -> int:
n = len(nums)
ans = 0
for i in range(n - 2):
for j in range(i + 1, n - 1):
for k in range(j + 1, n):
if self.is_triangle(nums[i], nums[j], nums[k]): ans += 1

return ans
```

**复杂度分析**

- 时间复杂度:$O(N ^ 3)$,其中 N 为 数组长度。
- 空间复杂度:$O(1)$

## 优化的暴力法

### 思路

暴力法的时间复杂度为 $O(N ^ 3)$, 其中 $N$ 最大为 1000。一般来说, $O(N ^ 3)$ 的算法在数据量 <= 500 是可以 AC 的。1000 的数量级则需要考虑 $O(N ^ 2)$ 或者更好的解法。

OK,到这里了。我给大家一个干货。 应该是其他博主不太会提的。原因可能是他们不知道, 也可能是他们觉得太小儿科不需要说。

1. 由于前面我根据数据规模推测到到了解法的复杂度区间是 $N ^ 2$, $N ^ 2 * logN$,不可能是 $N$ (WHY?)。
2. 降低时间复杂度的方法主要有: `空间换时间``排序换时间`(我们一般都是使用基于比较的排序方法)。而`排序换时间`仅仅在总体复杂度大于 $O(NlogN)$ 才适用(原因不用多说了吧?)。

这里由于总体的时间复杂度是 $O(N ^ 3)$,因此我自然想到了`排序换时间`。当我们对 nums 进行一次排序之后,我发现:

- is_triangle 函数有一些判断是无效的

```py
def is_triangle(self, a, b, c):
if a == 0 or b == 0 or c == 0: return False
# a + c > b 和 b + c > a 是无效的判断,因为恒成立
if a + b > c and a + c > b and b + c > a: return True
return False
```

- 因此我们的目标变为找到`a + b > c`即可,因此第三层循环是可以提前退出的。

```py
for i in range(n - 2):
for j in range(i + 1, n - 1):
k = j + 1
while k < n and num[i] + nums[j] > nums[k]:
k += 1
ans += k - j - 1
```

- 这也仅仅是减枝而已,复杂度没有变化。通过进一步观察,发现 k 没有必要每次都从 j + 1 开始。而是从上次找到的 k 值开始就行。原因很简单, 当 nums[i] + nums[j] > nums[k] 时,我们想要找到下一个满足 nums[i] + nums[j] > nums[k] 的 新的 k 值,由于进行了排序,因此这个 k 肯定比之前的大(单调递增性),因此上一个 k 值之前的数都是无效的,可以跳过。

```py
for i in range(n - 2):
k = i + 2
for j in range(i + 1, n - 1):
while k < n and nums[i] + nums[j] > nums[k]:
k += 1
ans += k - j - 1
```

由于 K 不会后退,因此最内层循环总共最多执行 N 次,因此总的时间复杂度为 $O(N ^ 2)$。

> 这个复杂度分析有点像单调栈,大家可以结合起来理解。
### 关键点分析

- 排序

### 代码

```py
class Solution:
def triangleNumber(self, nums: List[int]) -> int:
n = len(nums)
ans = 0
nums.sort()
for i in range(n - 2):
if nums[i] == 0: continue
k = i + 2
for j in range(i + 1, n - 1):
while k < n and nums[i] + nums[j] > nums[k]:
k += 1
ans += k - j - 1
return ans
```

**复杂度分析**

- 时间复杂度:$O(N ^ 2)$
- 空间复杂度:取决于排序算法

更多题解可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 30K star 啦。

关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。

![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg)