classSolution { public: inttriangleNumber(vector<int>& nums){ int n = nums.size(); sort(nums.begin(), nums.end()); int ans = 0; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { int left = j + 1, right = n - 1, k = j; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] < nums[i] + nums[j]) { k = mid; left = mid + 1; } else { right = mid - 1; } } ans += k - j; } } return ans; } };
publicclassSolution { publicintTriangleNumber(int[] nums) { int n = nums.Length; Array.Sort(nums); int ans = 0; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { int left = j + 1, right = n - 1, k = j; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] < nums[i] + nums[j]) { k = mid; left = mid + 1; } else { right = mid - 1; } } ans += k - j; } } return ans; } }
[sol1-Python3]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution: deftriangleNumber(self, nums: List[int]) -> int: n = len(nums) nums.sort() ans = 0 for i inrange(n): for j inrange(i + 1, n): 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 ans += k - j return ans
var triangleNumber = function(nums) { const n = nums.length; nums.sort((a, b) => a - b); let ans = 0; for (let i = 0; i < n; ++i) { for (let j = i + 1; j < n; ++j) { let left = j + 1, right = n - 1, k = j; while (left <= right) { const mid = Math.floor((left + right) / 2); if (nums[mid] < nums[i] + nums[j]) { k = mid; left = mid + 1; } else { right = mid - 1; } } ans += k - j; } } return ans; };
[sol1-Golang]
1 2 3 4 5 6 7 8 9
functriangleNumber(nums []int) (ans int) { sort.Ints(nums) for i, v := range nums { for j := i + 1; j < len(nums); j++ { ans += sort.SearchInts(nums[j+1:], v+nums[j]) } } return }
inttriangleNumber(int* nums, int numsSize) { qsort(nums, numsSize, sizeof(int), cmp); int ans = 0; for (int i = 0; i < numsSize; ++i) { for (int j = i + 1; j < numsSize; ++j) { int left = j + 1, right = numsSize - 1, k = j; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] < nums[i] + nums[j]) { k = mid; left = mid + 1; } else { right = mid - 1; } } ans += k - j; } } return ans; }
复杂度分析
时间复杂度:O(n^2 \log n),其中 n 是数组 nums 的长度。我们需要 O(n \log n) 的时间对数组 nums 进行排序,随后需要 O(n^2 \log n) 的时间使用二重循环枚举 a, b 的下标以及使用二分查找得到 c 的下标范围。
空间复杂度:O(\log n),即为排序需要的栈空间。
方法二:排序 + 双指针
思路与算法
我们可以对方法一进行优化。
我们将当 a = \textit{nums}[i], b = \textit{nums}[j] 时,最大的满足 nums}[k] < \textit{nums}[i] + \textit{nums}[j] 的下标 k 记为 k_{i, j。可以发现,如果我们固定 i,那么随着 j 的递增,不等式右侧 nums}[i] + \textit{nums}[j] 也是递增的,因此 k_{i, j 也是递增的。
这样一来,我们就可以将 j 和 k 看成两个同向(递增)移动的指针,将方法一进行如下的优化:
我们使用一重循环枚举 i。当 i 固定时,我们使用双指针同时维护 j 和 k,它们的初始值均为 i;
与方法一中「二分查找的失败」类似,方法二的双指针中,也会出现不存在满足 nums}[k] < \textit{nums}[i] + \textit{nums}[j] 的下标的情况。此时,指针 k 不会出现在指针 j 的右侧,即 k - j \leq 0,因此我们需要将 k - j 与 0 中的较大值累加入答案,防止错误的负数出现。
代码
[sol2-C++]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution { public: inttriangleNumber(vector<int>& nums){ int n = nums.size(); sort(nums.begin(), nums.end()); int ans = 0; for (int i = 0; i < n; ++i) { int k = i; for (int j = i + 1; j < n; ++j) { while (k + 1 < n && nums[k + 1] < nums[i] + nums[j]) { ++k; } ans += max(k - j, 0); } } return ans; } };
[sol2-Java]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution { publicinttriangleNumber(int[] nums) { intn= nums.length; Arrays.sort(nums); intans=0; for (inti=0; i < n; ++i) { intk= i; for (intj= i + 1; j < n; ++j) { while (k + 1 < n && nums[k + 1] < nums[i] + nums[j]) { ++k; } ans += Math.max(k - j, 0); } } return ans; } }
[sol2-C#]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
publicclassSolution { publicintTriangleNumber(int[] nums) { int n = nums.Length; Array.Sort(nums); int ans = 0; for (int i = 0; i < n; ++i) { int k = i; for (int j = i + 1; j < n; ++j) { while (k + 1 < n && nums[k + 1] < nums[i] + nums[j]) { ++k; } ans += Math.Max(k - j, 0); } } return ans; } }
[sol2-Python3]
1 2 3 4 5 6 7 8 9 10 11 12
classSolution: deftriangleNumber(self, nums: List[int]) -> int: n = len(nums) nums.sort() ans = 0 for i inrange(n): k = i for j inrange(i + 1, n): while k + 1 < n and nums[k + 1] < nums[i] + nums[j]: k += 1 ans += max(k - j, 0) return ans
[sol2-JavaScript]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
var triangleNumber = function(nums) { const n = nums.length; nums.sort((a, b) => a - b); let ans = 0; for (let i = 0; i < n; ++i) { let k = i; for (let j = i + 1; j < n; ++j) { while (k + 1 < n && nums[k + 1] < nums[i] + nums[j]) { ++k; } ans += Math.max(k - j, 0); } } return ans; };
functriangleNumber(nums []int) (ans int) { n := len(nums) sort.Ints(nums) for i, v := range nums { k := i for j := i + 1; j < n; j++ { for k+1 < n && nums[k+1] < v+nums[j] { k++ } ans += max(k-j, 0) } } return }
funcmax(a, b int)int { if a > b { return a } return b }
[sol2-C]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
intcmp(int *a, int *b) { return *a - *b; }
inttriangleNumber(int* nums, int numsSize) { qsort(nums, numsSize, sizeof(int), cmp); int ans = 0; for (int i = 0; i < numsSize; ++i) { int k = i; for (int j = i + 1; j < numsSize; ++j) { while (k + 1 < numsSize && nums[k + 1] < nums[i] + nums[j]) { ++k; } ans += fmax(k - j, 0); } } return ans; }
复杂度分析
时间复杂度:O(n^2),其中 n 是数组 nums 的长度。我们需要 O(n \log n) 的时间对数组 nums 进行排序,随后需要 O(n^2) 的时间使用一重循环枚举 a 的下标以及使用双指针维护 b, c 的下标。