LeetCode Top100之11,15题

11. 盛最多水的容器

① 题目描述
  • 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i
    的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
  • 说明:你不能倾斜容器,且 n 的值至少为 2。
    在这里插入图片描述
  • 示例:

输入: [1,8,6,2,5,4,8,3,7]
输出: 49

② 暴力求解
  • 盛水容器的体积,是由start和end中的较小值start和end之间的distance决定,与中间的line高度无关(中间的line不是容器壁,较高的line不会阻碍容器装水)。
public int maxArea(int[] height) {
    
    
    int result = 0;
    for (int i = 0; i < height.length - 1; i++) {
    
    
        for (int j = i + 1; j < height.length; j++) {
    
    
            int dis = j - i;
            int temp = Math.min(height[i], height[j]) * dis;
            if (temp > result) {
    
    
                result = temp;
            }
        }
    }
    return result;
}
③ 双指针法
  • 具体示意图见力扣上的solution
  • 总体思想:
    ① 使用left的right两个指针,分别指向line数组的左边和右边;最初我们考虑由最外围两条线段构成的区域,但是为了获得area的最大化,我们应该继续寻找更长的两条线段之间的区域。
    ② 如果向内侧移动较长line的指针,新的区域受限于较短line而不会获得任何增加;所以应该向内侧移动较短line的指针,移动指向较短线段的指针尽管造成了矩形宽度的减小,但却可能会有助于面积的增大。
    ③ 因为移动较短线段的指针会得到一条相对较长的线段,这可以克服由宽度减小而引起的面积减小。
  • 代码如下:
public int maxArea(int[] height) {
    
    
    int left = 0, right = height.length - 1;
    int result = 0;
    while (left < right) {
    
    
        int temp = Math.min(height[left], height[right]) * (right - left);
        result = (result < temp) ? temp : result;
        if (height[left] < height[right]) {
    
    
            left++;
        } else {
    
    
            right--;
        }
    }
    return result;
}

15. 三数之和

① 题目描述
  • 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0?找出所有满足条件且不重复的三元组。
  • 注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[ [-1, 0, 1], [-1, -1, 2] ]

② 使用暴力解法(Time Limit Exceeded)
  • 使用3层循环,查找满足条件的3个数。需要使用isDuplicate()方法,避免结果中包含重复的三元组。
  • 时间复杂度:n 表示 num 的个数,三个循环 O(n³),而 isInList 也需要 O(n),总共就是 O(n^4)
public List<List<Integer>> threeSum(int[] nums) {
    
    
    List<List<Integer>> result = new ArrayList<>();
    int len = nums.length;
    if (len < 3 || nums == null) {
    
    
        return result;
    }
    for (int i = 0; i < len - 2; i++) {
    
    
        for (int j = i + 1; j < len - 1; j++) {
    
    
            for (int k = j + 1; k < len; k++) {
    
    
                if (nums[i] + nums[j] + nums[k] == 0) {
    
    
                    List<Integer> temp = new ArrayList<>();
                    temp.add(nums[i]);
                    temp.add(nums[j]);
                    temp.add(nums[k]);
                    if (!isDuplicate(result, temp)) {
    
    
                        result.add(temp);
                    }
                }
            }
        }
    }
    return result;
}
public boolean isDuplicate(List<List<Integer>> res, List<Integer> target) {
    
    
    Collections.sort(target);
    for (int i = 0; i < res.size(); i++) {
    
    
        List<Integer> temp = res.get(i);
        Collections.sort(temp);
        boolean flag = false;
        for (int j = 0; j < 3; j++) {
    
    
            if (temp.get(j) != target.get(j)) {
    
    
                flag = true;
                break;
            }
        }
        if (!flag) {
    
    
            return true;
        }
    }
    return false;
}
③ 使用多指针(有人说是双指针,我更倾向于它是三指针)
  • 先对nums进行sort,然后用一个指针i从头开始遍历nums,作为第一个数;使用left指针(left=i+1)和right指针(right=n-1),固定后面的区域,通过夹逼查找满足要求的num,构成三元组。
  • 如果三个指针指向的数相加为零,开始移动指针:
    ① 当nums[left] == nums[left + 1]时,left要向内侧移动,直至遇到新的不同的数停止,避免构成的三元组重复;right指针也是如此。
    ② 指针i作为固定的三元组中的第一个数,也要跳过重复的数字,直至遇到新的不同的数字停止。
  • 如果sum > 0,说明right指针指向的数过大,向内侧移动right指针。
  • 如果sum < 0,说明left指针指向的数过小,向内侧移动left指针。
public List<List<Integer>> threeSum(int[] nums) {
    
    
    List<List<Integer>> result = new ArrayList<>();
    int len = nums.length;
    if (len < 3 || nums == null) {
    
    
        return result;
    }
    Arrays.sort(nums);
    for (int i = 0; i < len - 2; i++) {
    
    
        if (i > 0 && nums[i] == nums[i - 1]) {
    
    
            continue;
        }
        int left = i + 1, right = len - 1;
        while (left < right) {
    
    
            int sum = nums[i] + nums[left] + nums[right];
            if (sum == 0) {
    
    
                List<Integer> temp = new ArrayList<>();
                temp.add(nums[i]);
                temp.add(nums[left]);
                temp.add(nums[right]);
                result.add(temp);
                while (left < right && nums[left] == nums[left + 1]) {
    
    
                    left++;
                }
                while (left < right && nums[right] == nums[right - 1]) {
    
    
                    right--;
                }
                left++;
                right--;
            } else if (sum > 0) {
    
    
                right--;
            } else
                left++;
        }
    }
    return result;
}

猜你喜欢

转载自blog.csdn.net/u014454538/article/details/90312928