力扣每日一题21.08.04有效三角形的个数
文章目录
611.有效三角形的个数
题目描述
给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。
示例1
输入:
[2, 2, 3, 4]
输出:
3
解释:
有效的组合是:
2, 3, 4(使用第一个2)
2, 3, 4(使用第二个2)
2, 2, 3
注意:
1.数组长度不超过1000
2.数组里整数的范围为[0, 1000]。
思路:
排序+遍历(肯定超时)、排序+二分查找、排序+双指针
排序+遍历(没有时间限制的情况下可以用)
思路:对于三个正整数a, b, c而言,如果要作为三角形的三条边,必须满足两边之和大于第三边的规则,因此有a+b>c、a+c>b、b+c>a均成立。
如果对三条边进行升序排序,使得满足a<=b<=c,则a+c>b、b+c>a必定成立,只需要保证a+b>c即可满足条件。
所以,可以对给定的nums数组进行排序,枚举出边a和b。假定a和b对应的nums数组的元素index分别是i和j,则i<j,因为依据题意同一个位置的值不能取两次,而第三条边c则需要满足c<a+b,可以在[j+1, len(nums)-1]的范围内从右往左遍历,找出最大的满足边c<a+b的nums对应元素index,假定这个index为k,则在[j+1, k]范围内的index对应的nums元素都可以作为边c,将该范围长度k-j累加到结果中。当枚举完成后返回累加的结果即可。另外考虑到不能取到index相同的值,作为三条边,所以i应该在[0, len(nums)-3],j应该在[1, len(nums)-2],k应该在[2, len(nums)-1]。还有一个需要考虑的是nums数组为非负整数,所以如果遍历到0边,可以将j和k都设置为0,则k-j同样为0,不会对结果产生影响。
python实现
class Solution:
def triangleNumber(self, nums: List[int]) -> int:
nums = sorted(nums)
n = len(nums)
sum = 0
for i in range(n-2):
for j in range(i+1, n-1):
for k in range(n-1, j, -1):
if nums[i] != 0 and nums[j] != 0 and nums[k] != 0 and nums[i] + nums[j] > nums[k]:
sum += k-j
else:
sum += 0
return sum
排序+二分查找
这种思路是对上面的思路的一种优化,整体思想与上面的思路一样,只不过将三层遍历改为两层遍历,第三层遍历查找边c改为在[j+1, len(nums)-1]范围内用二分查找找到最大的边c的下标k。同样需要对0边进行操作。
python实现
class Solution:
def triangleNumber(self, nums: List[int]) -> int:
# 排序+二层遍历+二分查找
nums = sorted(nums)
n = len(nums)
sum = 0
for i in range(n-2):
for j in range(i+1, n-1):
left, right, k = j+1, n-1, j
while left <= right:
mid = (left + right) // 2
if nums[mid] < nums[i] + nums[j]:
k = mid
left = mid + 1
else:
right = mid - 1
sum += max(k - j, 0)
return sum
排序+双指针
该思路是对二分查找的优化,整体思路与排序+二分查找思路一样,但是将二分查找改为双指针遍历。
假定a=nums[i],b=nums[j],最大满足nums[k]<nums[i]+nums[j]的下标k。如果固定i,随着j的递增,不等式右侧也是递增的,所以k也是递增的。
则可以将j和k看做两个同向移动的指针。将排序+二分查找进行优化:
- 一重循环枚举i。固定i,使用双指针维护j和k,初始值均为i;
- 每次将j向右移动一个位置,并尝试不断向右移动k,使得k是最大满足nums[k]<nums[i] + nums[j]的下标。将max(k-j, 0)累加到结果。
python实现
class Solution:
def triangleNumber(self, nums: List[int]) -> int:
# 排序+双指针
nums = sorted(nums)
n = len(nums)
sum = 0
for i in range(n):
k = i
for j in range(i + 1, n):
while k + 1 < n and nums[k + 1] < nums[i] + nums[j]:
k += 1
sum += max(k - j, 0)
return sum