LeetCode 题目解析+优秀代码+剑指 Offer 模块

LeetCode 题目解析

11、盛最多水的容器 [Container With Most Water]

1、题目链接:

11. 盛最多水的容器

11. Container With Most Water

2、题目信息:

给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

img

图中垂直线代表输入数组 [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.

img

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、双指针法:

  1. 用两个变量从前后分别遍历数组,差值作为矩形的宽,两边高度低者作为高;
  2. 两个变量或者说指针的移动规则是低者移动,高者固定,因为无论怎么变,最高的要保留下来用以适配低者;
  3. 代码复杂度:时间复杂度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;
    }
}

15、三数之和[3sum]

1、题目链接

15. 三数之和

15. 3sum

2、题目信息:

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 abc ,使得 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 in nums 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;
    }
}

22. 括号生成

递归 自顶向下编程 回溯法

解题思路:

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 重置状态
}

思路:

  1. 确定使用递归进行调用;
  2. 获取所有情况,过滤其中不符合要求的项;
  3. 将符合要求的结果添加到最后的传参中。

时间复杂度: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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值