大厂面试真题详解:三角形计数

给定一个整数数组,在该数组中,寻找三个数,分别代表三角形三条边的长度,问,可以寻找到多少组这样的三个数来组成三角形?

在线评测地址:领扣题库官网

样例 1:

输入: [3, 4, 6, 7]
输出: 3
解释:
可以组成的是 (3, 4, 6), 
           (3, 6, 7),
           (4, 6, 7)
样例 2:
输入: [4, 4, 4, 4]
输出: 4
解释:
任何三个数都可以构成三角形
所以答案为 C(3, 4) = 4

题目理解

  • 我们先考虑“三条边能构成三角形”的充分必要条件。我们首先想到的是“三角形的任意两边之和大于第三边,三角形的任意两边之差小于第三边”。如果从这个角度理解,代码编写比较麻烦,因为“任意”二字就要求我们去检验各种组合。
  • 如果我们仔细分析一下这个条件,就不难证明它等效于“较短的两边之和大于最长边”。对于升序排列的三个数[a, b, c],我们只需判断a + b > c是否成立,就知道它们是否能构成三角形。

解题思路

  • 最直观的方法是暴力法,三重循环枚举,时间复杂度为O(n^3)。
  • 如果先将数组排好序,二重循环固定较短两边a和b,然后再利用二分查找找到最大的满足a + b > c的c,这样可以将时间复杂度优化至O(n^2logn)
  • 这里我们采用双指针方法,可以继续降低时间复杂度。首先固定最大边的位置 i,然后在 [0, i-1]之间利用双指针找到满足条件的三边。时间复杂度为O(n^2)。

算法流程

  • 首先对数组进行升序排列。
  • 从右向左遍历最大边,固定最大边的位置i

    • 建立双指针left和right,初始分别指向0和i-1
    • 如果S[left] + S[right] > S[i],说明三者可以构成三角形。与此同时,最小边的索引为left+1, left+2,...,right-1时,都能与S[right]和S[i]构成三角形。所以满足条件的三角形找到了right-left个。然后right指针左移进入下一轮。
    • 如果S[left] + S[right] <= S[i],说明不能构成三角形。left指针右移进入下一轮。

复杂度分析

  • 时间复杂度为O(n^2)。外层遍历最大边是n,内层循环移动双指针是n,所以总复杂度为O(n^2)。
  • 空间复杂度为O(1),只需占用常量空间。
public class Solution {
    /**
     * @param S: A list of integers
     * @return: An integer
     */
    public int triangleCount(int[] S) {
        int res = 0;
        int left = 0, right = S.length - 1;
        // 先对S进行排序
        Arrays.sort(S);
        // 最大边从右向左遍历
        for (int i = 0; i < S.length; i++) {
            // 建立双指针
            left = 0;
            right = i - 1;
            while (left < right) {
                // 可以构成三角形
                if (S[left] + S[right] > S[i]) {
                    res += (right - left);
                    right --;
                }
                // 不能构成三角形
                else {
                    left ++;
                }
            }
        }
        return res;
    }
}

更多题解参考:九章官网solution

上一篇:大厂面试真题详解:最近公共祖先 II


下一篇:算法面试真题详解:搜索二维