1、题目链接:
11. 盛最多水的容器
11. Container With Most Water
2、题目信息:
给你 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。
示例:
输入:[1,8,6,2,5,4,8,3,7] 输出:49
Given n non-negative integers a1, a2, …, an , where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant the container and n is at least 2.
The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.
Example:
Input: [1,8,6,2,5,4,8,3,7] Output: 49
3、解题思路:
1、蛮力法
遍历所有的组成,选出最大值,时间复杂度为O(n2),空间复杂度O(1);
从第一项开始,使用双层嵌套循环,只取其中最大的值;
代码实现
class Solution { public int maxArea(int[] height) { int maxarea = 0, len = height.length, area = 0; int i = 0, j = 0; do { j = i + 1; do { area = (j - i) * Math.min(height[i], height[j]); maxarea = Math.max(area, maxarea); } while (j++ < len-1); } while (i++ < len-2); return maxarea; } }
2、双指针法:
- 用两个变量从前后分别遍历数组,差值作为矩形的宽,两边高度低者作为高;
- 两个变量或者说指针的移动规则是低者移动,高者固定,因为无论怎么变,最高的要保留下来用以适配低者;
- 代码复杂度:时间复杂度O(n), 空间复杂度O(1)。
代码实现
class Solution { public int maxArea(int[] height) { int maxarea = 0, area = 0; //定义变量,maxarea 用于保存最大数,area 保存过程面积 int front = 0, after = height.length-1; //定义用于遍历数组的双指针 while (front < after) { //front == after 无参考价值,所以条件不需要 "<=" area = (after - front) * (height[front] > height[after] ? height[after--] : height[front++]); //用于计算过程中面积大小。 maxarea = Math.max(maxarea, area); //maxarea 保留最大面积 /* 能更简洁 maxarea = Math.max( maxarea, (after - front) * (height[front] > height[after] ? height[after--] : height[front++]) ); */ } return maxarea; } }
1、题目链接
15. 三数之和
15. 3sum
2、题目信息:
给你一个包含 n 个整数的数组
nums
,判断nums
中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
Given an array
nums
of n integers, are there elements a, b, c innums
such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4], A solution set is: [ [-1, 0, 1], [-1, -1, 2] ]
3、解题思路
本题一开始拿到,以为难点在于找到匹配项,但是在自己写蛮力解法的时候发现,可以找到所有的匹配项,但是问题在于怎么去除重复项。第一次刷这道题是看过代码去复刻的,没有很深很强的印象,但是自己来写、仔细思考的时候就会发现很多问题。
1、蛮力法(Leetcode 测试超时)
使用循环嵌套,遍历所有情况,并排除所有非答案情况;
时间复杂度:O(n),空间复杂度:O()
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> ans = new ArrayList<>(); int len = nums.length; if (len < 3 || nums == null) return ans; // 如果数组没有三个元素,则不用讨论 Arrays.sort(nums); // 使用库函数对数组进行排序 if (nums[len-1] < 0 || nums[0] > 0) return ans; // 排序之后如果最大值仍小于零或者最小值已经大于零,则不需要执行后续步骤了 for (int i = 0; i < len-2; i++) { // 最外层循环,指代三数之和为零的最小值 if (nums[i] > 0) return ans; // 如果已经最小数已经大于零了,就没必要继续找了 if (i > 0 && nums[i] == nums[i-1]) continue; // 用于寻找下一个不同的最小数 for (int j = i+1; j < len-1; j++) { // 第二层循环,寻找中间数 for (int k = j+1; k < len; k++) { // 寻找最大数 if (nums[k] + nums[j] + nums[i] == 0) { // 如果三数之和等于零,则添加到 ans 里 ans.add(Arrays.asList(nums[i], nums[j], nums[k])); break; } } // while 循环用于找寻找下一个不同的中间数,使用 len-2 作为限值是因为循环之后还有一个 j++,可能会超出数组限制 while (j < len-2 && nums[j] == nums[j+1]) j++; } } return ans; //返回目标列表 } }
2、双指针法
使用双层循环嵌套,外层循环用于定三数中的最小数,内层循环使用双指针法,简化双重循环。
在第二层循环嵌套中,使用双指针,
high
&low
,分别从数组最后一个属和外层嵌套后一个数开始遍历;判断是否可得和为零,
如果为零,则将其加入列表中;
如果大于零,则说明右指针
high
大了,只需将其左移一位在进行判断;如果小于零,则说明左指针
low
小了,将其右移一位即可;由于左右指针移动方向的下一个数可能相等,则加入
while循环
进行筛选,选出下一个不相等的数再进行之后的运算。时间复杂度:O(n2);空间复杂度:O(1)。
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> ans = new ArrayList<>(); int len = nums.length; if (len < 3 || nums == null) return ans; // 如果数组没有三个元素,则不用讨论 Arrays.sort(nums); if (nums[len-1] < 0 || nums[0] > 0) return ans; // 排序之后如果最大值仍小于零或者最小值已经大于零,则不需要执行后续步骤了 for (int i = 0; i < len-2; i++) { if (nums[i] > 0) break; // 如果已经最小数已经大于零了,就没必要继续找了,或者直接 return ans if (i > 0 && nums[i] == nums[i-1]) continue; // 用于寻找下一个不同的最小数 int lo = i+1, hi = len-1, sum = -nums[i]; // 定义两个指针,分别是 i 的后一个数和数组最后一个数,sum 用于简化运算 while (lo < hi) { // 左指针一定小于右指针,角标和值都一样 if (nums[lo] + nums[hi] == sum) { //如果满足题目,则添加进 ans 列表 ans.add(Arrays.asList(nums[i], nums[lo], nums[hi])); // 两个 while循环 都是为了寻找下一个不同的数 while (lo < hi && nums[lo] == nums[lo++]); while (lo < hi && nums[hi] == nums[hi--]); } else if (nums[lo] + nums[hi] < sum) { // 如果三数和小于零,则找下一个更大的的左指针值 while (lo < hi && nums[lo] == nums[lo++]); } else { // 如果三数和大于零,则找下一个更小的右指针 while (hi > lo && nums[hi] == nums[hi--]); } } } return ans; } }
递归
自顶向下编程
回溯法
解题思路:
1、递归
自顶向下编程思想;
目标:熟悉递归格式
public void recur(int level, int param) { // terminator 结束条件 if (level > MAX_LEVEL) return; // process current logic 逻辑过程 process(level, param); // drill down 下次递归方法 recur(level + 1, param); // reverse status 重置状态 }
思路:
- 确定使用递归进行调用;
- 获取所有情况,过滤其中不符合要求的项;
- 将符合要求的结果添加到最后的传参中。
时间复杂度:O(2n),空间复杂度:O(n)
class Solution { public List<String> generateParenthesis(int n) { List<String> ans = new ArrayList<>(); // 列举所有的情况 _generate(0, 2 * n, "", ans); return ans; } public void _generate(int level, int max, String s, List<String> list) { // terminator if (level >= max) { System.out.println(s); list.add(s); return; } // process current logic // String s1 = s + "("; // String s2 = s + ")"; // drill down _generate(level + 1, max, s + "(", list); _generate(level + 1, max, s + ")", list); // reverse status // nothing to do } }
class Solution { public List<String> generateParenthesis(int n) { List<String> ans = new