2021/11/30
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
解答:
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums.length==0){
return new int[]{-1,-1};
}
int[] ans = new int[2];
ans[0] = findLeft(nums, target);
if(ans[0]==-1){
return new int[]{-1,-1};
}
ans[1] = findRight(nums, target);
return ans;
}
public int findLeft(int[] nums, int target){
int L = 0, R = nums.length-1;
while(L<R){
int mid = (L+R)>>>1;
if(nums[mid]>target){
R = mid-1;
}else if(nums[mid]<target){
L = mid+1;
}else{
R = mid;
}
}
if(nums[L]==target){
return L;
}
return -1;
}
public int findRight(int[] nums, int target){
int L = 0, R = nums.length-1;
while(L<R){
int mid = (L+R+1)>>>1;
if(nums[mid]>target){
R = mid-1;
}else if(nums[mid]<target){
L = mid+1;
}else{
L = mid;
}
}
return L;
}
}
总结:
使用传统的二分法进行两次查找,需要注意的是,在findRight时,中点要向上取整。样例[5,7,7,8,8,10]中,当L=3,R=4时,如果使用向下取整,mid=L,就会出现L=L的死循环。
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:
输入:nums = [1], target = 0
输出:-1
提示:
1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-10^4 <= target <= 10^4
进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?
解答:
class Solution {
public int search(int[] nums, int target) {
int L=0, R=nums.length-1;
while(L<=R){
int mid = (L+R)>>>1;
if(nums[mid]==target){
return mid;
}
if(nums[mid]>=nums[0]){
if(nums[mid]>target&&target>=nums[0]){
R = mid-1;
}else{
L = mid+1;
}
}else{
if(nums[mid]<target&&nums[0]>target){
L = mid+1;
}else{
R = mid-1;
}
}
}
return -1;
}
}
总结:
可以把数组分成左右两段,两段数组都是递增的,且右段一定小于左段。
当nums[mid]>=nums[0]时,说明中值在左段。当target
当nums[mid] 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性: 每行中的整数从左到右按升序排列。 示例 1: 示例 2: 提示: 解答: 总结: 同一般二分法,将矩阵转化为数组即可。 2021/12/01 已知一个长度为 注意,数组 给你一个元素值 互不相同 的数组 示例 1: 示例 2: 示例 3: 提示: 解答: 总结: 与之前的旋转矩阵类似,只要理解右半段一定小于左半段即可。 峰值元素是指其值严格大于左右相邻值的元素。 给你一个整数数组 你可以假设 你必须实现时间复杂度为 示例 1: 示例 2: 提示: 解答: 总结: 题中给出条件 2021/12/02 存在一个按升序排列的链表,给你这个链表的头节点 返回同样按升序排列的结果链表。 示例 1: 示例 2: 提示: 解答: 总结: 读懂题意,是要删除所有重复元素,而不是去重。 给你一个包含 **注意:**答案中不可以包含重复的三元组。 示例 1: 示例 2: 示例 3: 提示: 解答: 2021/12/03 给定 如果相等,返回 **注意:**如果对空文本输入退格字符,文本继续为空。 示例 1: 示例 2: 示例 3: 示例 4: 解答1: 解答2: 给定两个由一些 闭区间 组成的列表, 返回这 两个区间列表的交集 。 形式上,闭区间 两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。例如, 示例 1: 示例 2: 示例 3: 示例 4: 提示: 解答: 给你 **说明:**你不能倾斜容器。 示例 1: 示例 2: 示例 3: 示例 4: 提示: 解答1: 解答2: 总结: 解答1中,遍历了有两两组合的情况,找到最大容器。而实际上,每次只需要让数字小的指针移动即可。因为数字大的指针如果进行移动,之后的容量一定会越来越小(高被另一边限制,宽逐渐缩短)。让数字小的指针移动,才可能找到更大的容量。这样无需遍历所有组合就能找到最大容量。 2021/12/04 给定两个字符串 异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。 示例 1: 示例 2: 提示: 解答: 总结: 思路: 给定一个正整数数组 请找出该数组内乘积小于 示例 1: 示例 2: 提示: 解答1: 解答2: 总结: 解答1需要右指针左移,而实际上,如果开头到结尾的乘积可以满足条件,那么里面的每一个都可以满足条件。 给定一个含有 找出该数组中满足其和 示例 1: 示例 2: 示例 3: 提示: 进阶: 解答: 2021/12/05 给你一个由 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网格的四条边均被水包围。 示例 1: 示例 2: 提示: 解答: 有 省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。 给你一个 返回矩阵中 省份 的数量。 示例 1: 示例 2: 提示: 解答: 2021/12/06 给定一个二叉树 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 初始状态下,所有 next 指针都被设置为 进阶: 示例: 提示: 解答: 给你两棵二叉树 二叉树 示例 1: 示例 2: 提示: 解答: 2021/12/07 难度中等143 给你一个 二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即, 畅通路径的长度 是该路径途经的单元格总数。 示例 1: 示例 2: 示例 3: 提示: 解答: 总结: 找某一点是否存在,选择dfs;找最短,选择bfs。 给你一个 m x n 的矩阵 board ,由若干字符 ‘X’ 和 ‘O’ ,找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。 示例 1: 示例 2: 提示: 解答: 给你一个有 二维数组的第 译者注:有向图是有方向的,即规定了 a→b 你就不能从 b→a 。 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 解答: 2021/12/08 给你一个整数数组 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1: 示例 2: 提示: 解答: 给你一个整数数组 解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。 示例 1: 示例 2: 提示: 解答1: 解答2: 2021/12/09 给定一个可包含重复数字的序列 示例 1: 示例 2: 提示: 解答: 给定一个无重复元素的正整数数组 对于给定的输入,保证和为 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 解答: 解答2: 总结: 简单的回溯,不做解释。解答2中,提前判断 给定一个数组 **注意:**解集不能包含重复的组合。 示例 1: 示例 2: 提示: 解答: 2021/12/11 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。 给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。 示例 1: 示例 2: 示例 3: 提示: 解答: 给定一个非负整数数组 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标。 示例 1: 示例 2: 提示: 解答: 2021/12/12 给你一个非负整数数组 数组中的每个元素代表你在该位置可以跳跃的最大长度。 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 假设你总是可以到达数组的最后一个位置。 示例 1: 示例 2: 提示: 解答: 一个机器人位于一个 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径? 示例 1: 示例 2: 示例 3: 示例 4: 提示: 解答: 2021/12/13 给你一个字符串 示例 1: 示例 2: 示例 3: 示例 4: 提示: 解答: 如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。 给你一个整数数组 子数组 是数组中的一个连续序列。 示例 1: 示例 2: 提示: 解答: 2021/12/14 一条包含字母 要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如, 注意,消息不能分组为 给你一个只含数字的 非空 字符串 题目数据保证答案肯定是一个 32 位 的整数。 示例 1: 示例 2: 示例 3: 示例 4: 提示: 解答: 给你一个字符串 **说明:**拆分时可以重复使用字典中的单词。 示例 1: 示例 2: 示例 3: 提示: 解答: 2021/12/15 给你一个整数数组 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如, 示例 1: 示例 2: 示例 3: 提示: 进阶: 解答: 给定一个未排序的整数数组,找到最长递增子序列的个数。 示例 1: 示例 2: 注意: 给定的数组长度不超过 2000 并且结果一定是32位有符号整数。 解答: 2021/12/16 给定两个字符串 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。 示例 1: 示例 2: 示例 3: 提示: 解答: 给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。 示例: 提示: 解答: 2021/12/17 给你两个单词 你可以对一个单词进行如下三种操作: 示例 1: 示例 2: 提示: 解答: 给你一个整数数组 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 你可以认为每种硬币的数量是无限的。 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 解答: 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 示例 1: 示例 2: 说明: 你可以假设 n 不小于 2 且不大于 58。 解答: 2021/12/18 给你两个整数 示例 1: 示例 2: 示例 3: 提示: 解答: 2021/12/19 给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组。 实现 示例: 提示: 解答: 编写一个算法来判断一个数 「快乐数」定义为: 如果 示例 1: 示例 2: 提示: 解答: 给你一个数组 示例 1: 示例 2: 提示: 解答: 2021/12/28 给定一个 示例 2: 提示: 解答: 你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。 假设你有 你可以通过调用 示例 1: 示例 2: 提示: 解答: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 解答: 解答2: 2021/12/29 给你一个按 非递减顺序 排序的整数数组 示例 1: 示例 2: 提示: 进阶: 解答: 给你一个数组,将数组中的元素向右轮转 示例 1: 示例 2: 提示: 进阶: 解答: 2021/12/30 给定一个数组 示例: 说明: 解答1: 优化: 给定一个已按照 非递减顺序排列 的整数数组 函数应该以长度为 你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。 示例 1: 示例 2: 示例 3: 提示: 解答: 2021/12/31 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 不要给另外的数组分配额外的空间,你必须**原地修改输入数组**、使用 O(1) 的额外空间解决这一问题。 示例 1: 示例 2: 提示: 解答: 给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。 示例: 提示: 解答: 2021/01/01 给定一个头结点为 如果有两个中间结点,则返回第二个中间结点。 示例 1: 示例 2: 提示: 解答: 给你一个链表,删除链表的倒数第 示例 1: 示例 2: 示例 3: 提示: **进阶:**你能尝试使用一趟扫描实现吗? 解答: 2021/01/02 给定一个字符串 示例 1: 示例 2: 示例 3: 示例 4: 提示: 解答: 给你两个字符串 换句话说, 示例 1: 示例 2: 提示: 解答: 2021/01/04 给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。 示例 1: 注意: 合并必须从两个树的根节点开始。 解答: 给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下: 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 初始状态下,所有 next 指针都被设置为 进阶: 示例: 提示: 解答: 2021/01/06 给定一个由 两个相邻元素间的距离为 示例 1: 示例 2: 提示: 解答1: 解答2: 在给定的网格中,每个单元格可以有以下三个值之一: 每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。 返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 示例 1: 示例 2: 示例 3: 提示: 解答: 2021/01/08 给定两个整数 你可以按 任何顺序 返回答案。 示例 1: 示例 2: 提示: 解答: 给定一个不含重复数字的数组 示例 1: 示例 2: 示例 3: 提示: 解答: 给定一个字符串 提示: 解答: 2021/01/09 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? **注意:**给定 n 是一个正整数。 示例 1: 示例 2: 解答: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 示例 1: 示例 2: 提示: 解答: 给定一个三角形 每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 示例 1: 示例 2: 提示: 进阶: 解答: 2021/01/10 给你一个整数 如果存在一个整数 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: **进阶:**你能够不使用循环/递归解决此问题吗? 解答: 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 提示: 示例 1: 示例 2: 示例 3: 提示: 进阶: 解答: 解答2:74.搜索二维矩阵
每行的第一个整数大于前一行的最后一个整数。输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false
m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
if(m==0) return false;
int n = matrix[0].length;
int L=0, R=m*n-1;
while(L<=R){
int mid = (L+R)>>>1;
int midV = matrix[mid/n][mid%n];
if(midV==target) return true;
else{
if(midV<target){
L = mid+1;
}else{
R = mid-1;
}
}
}
return false;
}
}
day2-二分查找
153.寻找旋转排序数组中的最小值
4
次,则可以得到 [4,5,6,7,0,1,2]
7
次,则可以得到 [0,1,2,4,5,6,7]
[a[0], a[1], a[2], ..., a[n-1]]
旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]
。nums
,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。
输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums
中的所有整数 互不相同nums
原来是一个升序排序的数组,并进行了 1
至 n
次旋转class Solution {
public int findMin(int[] nums) {
int L=0, R=nums.length-1;
while(L<R){
int mid = L+(R-L)/2;
if(nums[mid]<nums[R]){
R=mid;
}else{
L=mid+1;
}
}
return nums[L];
}
}
162.寻找峰值
nums
,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。nums[-1] = nums[n] = -∞
。O(log n)
的算法来解决此问题。输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。
输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5
解释:你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。
1 <= nums.length <= 1000
-231 <= nums[i] <= 231 - 1
i
都有 nums[i] != nums[i + 1]
class Solution {
public int findPeakElement(int[] nums) {
int L=0, R=nums.length-1;
while(L<R){
int mid = (L+R)>>>1;
if(nums[mid]>nums[mid+1]){
R = mid;
}else{
L = mid+1;
}
}
return L;
}
}
nums[-1] = nums[n] = -∞
,说明数组中一定存在峰,且每个坡一定会有它的峰。所以直接用nums[mid]>nums[mid+1]
判断它是上下坡即可。day3-双指针
82.删除排序链表中的重复元素 II
head
,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
输入:head = [1,1,1,2,3]
输出:[2,3]
[0, 300]
内-100 <= Node.val <= 100
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode node = new ListNode(0,head);
ListNode t = node;
while(t.next!=null&&t.next.next!=null){
if(t.next.val==t.next.next.val){
int num = t.next.val;
t.next = t.next.next;
while(t.next!=null&&t.next.val==num){
t.next = t.next.next;
}
}else{
t = t.next;
}
}
return node.next;
}
}
15. 三数之和
n
个整数的数组 nums
,判断 nums
中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?请你找出所有和为 0
且不重复的三元组。输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
输入:nums = []
输出:[]
输入:nums = [0]
输出:[]
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
int n = nums.length;
if(n<=2){
return ans;
}
Arrays.sort(nums);
for(int i = 0; i < n-2; i++) {
if(nums[i]>0) break;
if(i>0&&nums[i]==nums[i-1]) continue;
int L = i+1, R = n-1;
while (L<R){
int sum = nums[i]+nums[L]+nums[R];
if(sum==0){
ans.add(new ArrayList<>(Arrays.asList(nums[i],nums[L],nums[R])));
while(L<R&&nums[L]==nums[++L]);
while(L<R&&nums[R]==nums[--R]);
}else if(sum<0){
while(L<R&&nums[L]==nums[++L]);
}else{
while(L<R&&nums[R]==nums[--R]);
}
}
}
return ans;
}
}
day4-双指针
844. 比较含退格的字符串
s
和 t
两个字符串,当它们分别被输入到空白的文本编辑器后,请你判断二者是否相等。#
代表退格字符。true
;否则,返回 false
。输入:s = "ab#c", t = "ad#c"
输出:true
解释:S 和 T 都会变成 “ac”。
输入:s = "ab##", t = "c#d#"
输出:true
解释:s 和 t 都会变成 “”。
输入:s = "a##c", t = "#a#c"
输出:true
解释:s 和 t 都会变成 “c”。
输入:s = "a#c", t = "b"
输出:false
解释:s 会变成 “c”,但 t 仍然是 “b”。
//直接使用StringBuilder修改字符串
class Solution {
public boolean backspaceCompare(String s, String t) {
return fun(s).equals(fun(t));
}
public String fun(String s){
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(c=='#'){
if(stringBuilder.length()>0){
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
}else{
stringBuilder.append(c);
}
}
return stringBuilder.toString();
}
}
//双指针逆序遍历
class Solution {
public boolean backspaceCompare(String s, String t) {
int t1 = s.length()-1, t2 = t.length()-1;
int count1 = 0 , count2 = 0;
while (t1>=0||t2>=0){
while (t1>=0){
if (s.charAt(t1)=='#'){
count1++;
t1--;
}else if(count1>0){
count1--;
t1--;
}else {
break;
}
}
while (t2>=0){
if (t.charAt(t2)=='#'){
count2++;
t2--;
}else if(count2>0){
count2--;
t2--;
}else {
break;
}
}
if(t1>=0&&t2>=0){
if(s.charAt(t1)!=t.charAt(t2)){
return false;
}
}else{
if(t1>=0||t2>=0){
return false;
}
}
t1--;
t2--;
}
return true;
}
}
986. 区间列表的交集
firstList
和 secondList
,其中 firstList[i] = [starti, endi]
而 secondList[j] = [startj, endj]
。每个区间列表都是成对 不相交 的,并且 已经排序 。[a, b]
(其中 a <= b
)表示实数 x
的集合,而 a <= x <= b
。[1, 3]
和 [2, 4]
的交集为 [2, 3]
。输入:firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]]
输出:[[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
输入:firstList = [[1,3],[5,9]], secondList = []
输出:[]
输入:firstList = [], secondList = [[4,8],[10,12]]
输出:[]
输入:firstList = [[1,7]], secondList = [[3,10]]
输出:[[3,7]]
0 <= firstList.length, secondList.length <= 1000
firstList.length + secondList.length >= 1
0 <= starti < endi <= 109
endi < starti+1
0 <= startj < endj <= 109
endj < startj+1
class Solution {
public int[][] intervalIntersection(int[][] firstList, int[][] secondList) {
List<int[]> ans = new ArrayList<>();
int i=0, j=0;
while (i<firstList.length&&j<secondList.length){
//取大的左边界,小的右边界,即为交集
int L = Math.max(firstList[i][0], secondList[j][0]);
int R = Math.min(firstList[i][1], secondList[j][1]);
if(L<=R){
ans.add(new int[]{L,R});
}
//右边界小的,前往下一区间
if(firstList[i][1]<secondList[j][1]){
i++;
}else {
j++;
}
}
return ans.toArray(new int[ans.size()][]);
}
}
11. 盛最多水的容器
n
个非负整数 a1,a2,...,a``n
,每个数代表坐标中的一个点 (i, ai)
。在坐标内画 n
条垂直线,垂直线 i
的两个端点分别为 (i, ai)
和 (i, 0)
。找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
输入:height = [1,1]
输出:1
输入:height = [4,3,2,1,4]
输出:16
输入:height = [1,2,1]
输出:2
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
class Solution {
public int maxArea(int[] height) {
int sum = 0,temp = 0;
for(int i=0;i<height.length-1;i++){
for(int j=i+1;j<height.length;j++){
temp = Math.min(height[i],height[j]);
temp *= j-i;
if(temp>sum) sum = temp;
}
}
return sum;
}
}
class Solution {
public int maxArea(int[] height) {
int L = 0, R = height.length-1;
int ans = 0;
while (L<R){
int temp = Math.min(height[L],height[R])*(R-L);
ans = Math.max(ans, temp);
if(height[L]<height[R]){
L++;
}else{
R--;
}
}
return ans;
}
}
day5-滑动窗口
438. 找到字符串中所有字母异位词
s
和 p
,找到 s
中所有 p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
1 <= s.length, p.length <= 3 * 104
s
和 p
仅包含小写字母class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
int n = s.length(), m = p.length();
int[] a = new int[26];
for (int i = 0; i < p.length(); i++) {
a[p.charAt(i)-'a']++;
}
int t=0;
for(int L=0, R=0; R<n; R++){
if(--a[s.charAt(R)-'a']>=0){
t++;
}
if(t==m){
ans.add(L);
}
if(R-L+1==m){
if(++a[s.charAt(L)-'a']>0){
t--;
}
L++;
}
}
return ans;
}
}
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
int n = s.length(), m = p.length();
int[] a = new int[26];
int[] b = new int[26];
for (int i = 0; i < p.length(); i++) {
a[p.charAt(i)-'a']++;
}
return ans;
}
}
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
int n = s.length(), m = p.length();
int[] a = new int[26];
for (int i = 0; i < p.length(); i++) {
a[p.charAt(i)-'a']++;
}
int t=0;
for(int L=0, R=0; R<n; R++){
if(a[s.charAt(R)-'a']>0){
a[s.charAt(R)-'a']--;
t++;
}
if(t==m){
ans.add(L);
}
//无法还原数组
}
return ans;
}
}
if(--a[s.charAt(R)-'a']>=0){
t++;
}
if(t==m){
ans.add(L);
}
if(R-L+1==m){
//还原后大于零,说明该字符是目标字符,移除滑动窗口,计数器减一。
if(++a[s.charAt(L)-'a']>0){
t--;
}
L++;
}
713. 乘积小于K的子数组
nums
和整数 k
。k
的连续的子数组的个数。输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8个乘积小于100的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。
输入: nums = [1,2,3], k = 0
输出: 0
1 <= nums.length <= 3 * 104
1 <= nums[i] <= 1000
0 <= k <= 106
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if(k<=1) return 0;
int sum = 1, ans = 0;
int n = nums.length;
for(int L=0, R=0;;){
sum*=nums[R];
if(sum<k){
ans++;
R++;
}else{
L++;
R=L;
sum=1;
}
if(R==n){
L++;
R=L;
sum=1;
}
if(L>=n){
break;
}
}
return ans;
}
}
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if (k <= 1) return 0;
int l = 0, r = 0, base = 1, ans = 0;
int length = nums.length;
while (r < length) {
base *= nums[r++];
while (base >= k) base /= nums[l++];
ans += r - l;
}
return ans;
}
}
209. 长度最小的子数组
n
个正整数的数组和一个正整数 target
。≥ target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度**。**如果不存在符合条件的子数组,返回 0
。输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
输入:target = 4, nums = [1,4,4]
输出:1
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
O(n)
时间复杂度的解法, 请尝试设计一个 O(n log(n))
时间复杂度的解法。class Solution {
public int minSubArrayLen(int target, int[] nums) {
int n = nums.length;
if (n == 0) {
return 0;
}
int ans = Integer.MAX_VALUE;
int L = 0, R = 0;
int sum = 0;
while (R < n) {
sum += nums[R];
while (sum >= target) {
ans = Math.min(ans, R - L + 1);
sum -= nums[L];
L++;
}
R++;
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}
day6-广度优先搜索 / 深度优先搜索
200. 岛屿数量
'1'
(陆地)和 '0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j]
的值为 '0'
或 '1'
class Solution {
int[] xx = {0,0,1,-1}, yy = {1,-1,0,0};
public void dfs(char[][] grid, int i, int j){
grid[i][j]='0';
for (int t = 0; t < 4; t++) {
int x = i+xx[t], y = j+yy[t];
if(x>=0&&y>=0&&x<grid.length&&y<grid[0].length&&grid[x][y]=='1'){
dfs(grid, x, y);
}
}
}
public int numIslands(char[][] grid) {
int ans = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++){
if(grid[i][j]=='1'){
ans++;
dfs(grid,i,j);
}
}
}
return ans;
}
}
547. 省份数量
n
个城市,其中一些彼此相连,另一些没有相连。如果城市 a
与城市 b
直接相连,且城市 b
与城市 c
直接相连,那么城市 a
与城市 c
间接相连。n x n
的矩阵 isConnected
,其中 isConnected[i][j] = 1
表示第 i
个城市和第 j
个城市直接相连,而 isConnected[i][j] = 0
表示二者不直接相连。输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3
1 <= n <= 200
n == isConnected.length
n == isConnected[i].length
isConnected[i][j]
为 1
或 0
isConnected[i][i] == 1
isConnected[i][j] == isConnected[j][i]
class Solution {
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length;
boolean[] v = new boolean[n];
int ans = 0;
for (int i=0;i<n;i++){
if(!v[i]){
v[i] = true;
dfs(isConnected, v, n, i);
ans++;
}
}
return ans;
}
private void dfs(int[][] isConnected, boolean[] v, int n, int i) {
for (int j=0;j<n;j++){
if(isConnected[i][j]==1&&!v[j]&&i!=j){
v[j] = true;
dfs(isConnected, v, n, j);
}
}
}
}
day7-广度优先搜索 / 深度优先搜索
117. 填充每个节点的下一个右侧节点指针 II
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
NULL
。NULL
。
输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。
6000
-100 <= node.val <= 100
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/
class Solution {
public Node connect(Node root) {
if(root==null) return null;
Queue<Node> q = new LinkedList<>();
q.add(root);
while (!q.isEmpty()){
int n = q.size();
Node last = null;
for (int i = 0; i < n; i++) {
Node t = q.poll();
if(t.left!=null){
q.add(t.left);
}
if(t.right!=null){
q.add(t.right);
}
if(i!=0){
last.next = t;
}
last = t;
}
}
return root;
}
}
572. 另一棵树的子树
root
和 subRoot
。检验 root
中是否包含和 subRoot
具有相同结构和节点值的子树。如果存在,返回 true
;否则,返回 false
。tree
的一棵子树包括 tree
的某个节点和这个节点的所有后代节点。tree
也可以看做它自身的一棵子树。输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true
输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
输出:false
root
树上的节点数量范围是 [1, 2000]
subRoot
树上的节点数量范围是 [1, 1000]
-104 <= root.val <= 104
-104 <= subRoot.val <= 104
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
return dfs(root, subRoot);
}
public boolean dfs(TreeNode root, TreeNode subRoot){
if(root==null){
return false;
}
return check(root,subRoot)||dfs(root.left,subRoot)||dfs(root.right,subRoot);
}
public boolean check(TreeNode root, TreeNode subRoot) {
if(root==null&&subRoot==null){
return true;
}
if(root==null||subRoot==null|| root.val!=subRoot.val){
return false;
}
return check(root.left,subRoot.left)&&check(root.right,subRoot.right);
}
}
day8-广度优先搜索 / 深度优先搜索
1091. 二进制矩阵中的最短路径
n x n
的二进制矩阵 grid
中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1
。(0, 0)
)到 右下角 单元格(即,(n - 1, n - 1)
)的路径,该路径同时满足下述要求:
0
。输入:grid = [[0,1],[1,0]]
输出:2
输入:grid = [[0,0,0],[1,1,0],[1,1,0]]
输出:4
输入:grid = [[1,0,0],[1,1,0],[1,1,0]]
输出:-1
n == grid.length
n == grid[i].length
1 <= n <= 100
grid[i][j]
为 0
或 1
class Solution {
public int shortestPathBinaryMatrix(int[][] grid) {
int n = grid.length;
if(grid.length==0||grid[0][0]==1||grid[n-1][n-1]==1){
return -1;
}
int xx[] = {0,0,1,1,1,-1,-1,-1}, yy[] = {1,-1,0,1,-1,0,1,-1};
Queue<int[]> q = new LinkedList<>();
q.add(new int[]{0,0});
int ans = 1;
while (!q.isEmpty()){
int m = q.size();
for (int i = 0; i < m; i++) {
int t[] = q.poll();
int a = t[0], b = t[1];
if(a==n-1&&b==n-1){
return ans;
}
for(int j=0;j<8;j++){
int x = a+xx[j], y = b+yy[j];
if(x>=0&&y>=0&&x<n&&y<n&&grid[x][y]==0){
grid[x][y]=1;
q.add(new int[]{x,y});
}
}
}
ans++;
}
return -1;
}
}
130. 被围绕的区域
输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
输入:board = [["X"]]
输出:[["X"]]
m == board.length
n == board[i].length
1 <= m, n <= 200
board[i][j] 为 'X' 或 'O'
class Solution {
int xx[] = {0,0,1,-1}, yy[] = {1,-1,0,0};
public void solve(char[][] board) {
int n = board.length, m = board[0].length;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if((i==0||i==n-1||j==0||j==m-1)&&board[i][j]=='O'){
dfs(board,i,j);
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if(board[i][j]=='O') {
board[i][j] = 'X';
}
if(board[i][j]=='*') {
board[i][j] = 'O';
}
}
}
}
public void dfs(char[][] board, int i, int j){
board[i][j] = '*';
for (int k=0;k<4;k++){
int x = i+xx[k], y = j+yy[k];
if(x>=0&&x<board.length&&y>=0&&y<board[0].length&&board[x][y]=='O'){
dfs(board,x,y);
}
}
}
}
797. 所有可能的路径
n
个节点的 有向无环图(DAG),请你找出所有从节点 0
到节点 n-1
的路径并输出(不要求按特定顺序)i
个数组中的单元都表示有向图中 i
号节点所能到达的下一些节点,空就是没有下一个结点了。输入:graph = [[1,2],[3],[3],[]]
输出:[[0,1,3],[0,2,3]]
解释:有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3
输入:graph = [[4,3,1],[3,2,4],[3],[4],[]]
输出:[[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]
输入:graph = [[1],[]]
输出:[[0,1]]
输入:graph = [[1,2,3],[2],[3],[]]
输出:[[0,1,2,3],[0,2,3],[0,3]]
输入:graph = [[1,3],[2],[3],[]]
输出:[[0,1,2,3],[0,3]]
n == graph.length
2 <= n <= 15
0 <= graph[i][j] < n
graph[i][j] != i
(即,不存在自环)graph[i]
中的所有元素 互不相同class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> temp = new ArrayList<>();
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
temp.add(0);
dfs(graph, 0);
return ans;
}
public void dfs(int[][] graph, int t){
if(t == graph.length-1){
ans.add(new ArrayList<>(temp));
return;
}
for(int next : graph[t]){
temp.add(next);
dfs(graph, next);
temp.remove(temp.size()-1);
}
}
}
day9-递归 / 回溯
78. 子集
nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
输入:nums = [0]
输出:[[],[0]]
1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums
中的所有元素 互不相同class Solution {
List<Integer> temp = new ArrayList<Integer>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> subsets(int[] nums) {
dfs(0,nums);
return ans;
}
public void dfs(int n, int[] nums){
if(n==nums.length){
ans.add(new ArrayList<Integer>(temp));
return;
}
temp.add(nums[n]);
dfs(n+1,nums);
temp.remove(temp.size()-1);
dfs(n+1,nums);
}
}
90. 子集 II
nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
输入:nums = [0]
输出:[[],[0]]
1 <= nums.length <= 10
-10 <= nums[i] <= 10
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
ans.add(new ArrayList<>());
Arrays.sort(nums);
int start = 1;
for (int i = 0; i < nums.length; i++) {
List<List<Integer>> ans_tmp = new ArrayList<>();
for (int j = 0; j < ans.size(); j++) {
List<Integer> list = new ArrayList<>(ans.get(j));
if (i > 0 && nums[i] == nums[i - 1] && j < start) {
continue;
}
list.add(nums[i]);
ans_tmp.add(list);
}
start = ans.size();
ans.addAll(ans_tmp);
}
return ans;
}
}
class Solution {
List<Integer> t = new ArrayList<Integer>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
dfs(false, 0, nums);
return ans;
}
public void dfs(boolean choosePre, int cur, int[] nums) {
if (cur == nums.length) {
ans.add(new ArrayList<Integer>(t));
return;
}
dfs(false, cur + 1, nums);
if (!choosePre && cur > 0 && nums[cur - 1] == nums[cur]) {
return;
}
t.add(nums[cur]);
dfs(true, cur + 1, nums);
t.remove(t.size() - 1);
}
}
day10-递归 / 回溯
47. 全排列 II
nums
,按任意顺序 返回所有不重复的全排列。输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
1 <= nums.length <= 8
-10 <= nums[i] <= 10
class Solution {
boolean[] vis;
public void fun(int[] nums, List<List<Integer>> ans, List<Integer> temp, int index){
if(index==nums.length){
ans.add(new ArrayList<Integer>(temp));
return;
}
for(int i=0;i<nums.length;i++){
if(vis[i]||(i>0&&nums[i]==nums[i-1]&&!vis[i-1])){
continue;
}
temp.add(nums[i]);
vis[i]=true;
fun(nums,ans,temp,index+1);
vis[i]=false;
temp.remove(index);
}
}
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> temp = new ArrayList<>();
vis = new boolean[nums.length];
Arrays.sort(nums);
fun(nums, ans, temp, 0);
return ans;
}
}
39. 组合总和
candidates
和一个正整数 target
,找出 candidates
中所有可以使数字和为目标数 target
的唯一组合。candidates
中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。target
的唯一组合数少于 150
个。输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
输入: candidates = [2], target = 1
输出: []
输入: candidates = [1], target = 1
输出: [[1]]
输入: candidates = [1], target = 2
输出: [[1,1]]
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate
中的每个元素都是独一无二的。1 <= target <= 500
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> temp = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
fun(candidates,0, target);
return ans;
}
public void fun(int[] candidates, int index, int target){
if(target==0){
ans.add(new ArrayList<Integer>(temp));
return;
}
if(target<0||index>=candidates.length){
return;
}
temp.add(candidates[index]);
fun(candidates,index, target-candidates[index]);
temp.remove(temp.size()-1);
fun(candidates,index+1, target);
}
}
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> temp = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
fun(candidates,0, target);
return ans;
}
public void fun(int[] candidates, int index, int target){
if(index>=candidates.length){
return;
}
if(target==0){
ans.add(new ArrayList<Integer>(temp));
return;
}
fun(candidates,index+1, target);
if(target-candidates[index]>=0){
temp.add(candidates[index]);
fun(candidates,index, target-candidates[index]);
temp.remove(temp.size()-1);
}
}
}
target-candidates[index]>=0
,速度大大提升。40. 组合总和 II
candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。candidates
中的每个数字在每个组合中只能使用一次。输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]
1 <= candidates.length <= 100
1 <= candidates[i] <= 50
1 <= target <= 30
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> temp = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
fun(candidates,0, target);
return ans;
}
public void fun(int[] candidates, int index, int target){
if(target==0){
ans.add(new ArrayList<Integer>(temp));
return;
}
for (int i = index; i < candidates.length; i++) {
if(target-candidates[i]<0){
break;
}
if(i>index&&candidates[i]==candidates[i-1]){
continue;
}
temp.add(candidates[i]);
fun(candidates, i+1, target-candidates[i]);
temp.remove(temp.size()-1);
}
}
}
day12-动态规划
213. 打家劫舍 II
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
输入:nums = [0]
输出:0
1 <= nums.length <= 100
0 <= nums[i] <= 1000
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n==1) return nums[0];
if(n==2) return Math.max(nums[0], nums[1]);
return Math.max(fun(nums, 0, n-1), fun(nums, 1, n));
}
public int fun(int[] nums, int idx, int n){
int a = nums[idx], b = Math.max(nums[idx+1], nums[idx]);
for(int i=idx+2;i<n;i++){
int temp = b;
b = Math.max(nums[i]+a, b);
a = temp;
}
return b;
}
}
55. 跳跃游戏
nums
,你最初位于数组的 第一个下标 。输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105
class Solution {
public boolean canJump(int[] nums) {
int n = nums.length, index = n-1;
for(int i=n-2;i>=0;i--){
if(nums[i]>=index-i){
index = i;
}
}
if(index==0) return true;
return false;
}
}
day13-动态规划
45. 跳跃游戏 II
nums
,你最初位于数组的第一个位置。输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
输入: nums = [2,3,0,1,4]
输出: 2
1 <= nums.length <= 104
0 <= nums[i] <= 1000
class Solution {
public int jump(int[] nums) {
int ans=0, max=0, end=0;
for(int i=0;i<nums.length-1;i++){
max = Math.max(max, i+nums[i]);
if(i == end){
end = max;
ans++;
}
}
return ans;
}
}
62. 不同路径
m x n
网格的左上角 (起始点在下图中标记为 “Start” )。输入:m = 3, n = 7
输出:28
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下
输入:m = 7, n = 3
输出:28
输入:m = 3, n = 3
输出:6
1 <= m, n <= 100
2 * 109
class Solution {
public int uniquePaths(int m, int n) {
int[][] num = new int[m][n];
for(int i=0;i<m;i++){
num[i][0] = 1;
}
for(int i=0;i<n;i++){
num[0][i] = 1;
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
num[i][j] = num[i-1][j]+num[i][j-1];
}
}
return num[m-1][n-1];
}
}
day14-动态规划
5. 最长回文子串
s
,找到 s
中最长的回文子串。输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
输入:s = "cbbd"
输出:"bb"
输入:s = "a"
输出:"a"
输入:s = "ac"
输出:"a"
1 <= s.length <= 1000
s
仅由数字和英文字母(大写和/或小写)组成class Solution {
public String longestPalindrome(String s) {
int n = s.length();
boolean[][] dp = new boolean[n][n];
String ans = "";
for(int l=0;l<n;l++){
for(int i=0;i+l<n;i++){
int j=i+l;
if(l==0) dp[i][j]=true;
else if(l==1) dp[i][j]=(s.charAt(i)==s.charAt(j));
else{
dp[i][j]=(s.charAt(i)==s.charAt(j)&&dp[i+1][j-1]);
}
if(dp[i][j]&&l+1>ans.length()){
ans = s.substring(i,i+l+1);
}
}
}
return ans;
}
}
413. 等差数列划分
[1,3,5,7,9]
、[7,7,7,7]
和 [3,-1,-5,-9]
都是等差数列。nums
,返回数组 nums
中所有为等差数组的 子数组 个数。输入:nums = [1,2,3,4]
输出:3
解释:nums 中有三个子等差数组:[1, 2, 3]、[2, 3, 4] 和 [1,2,3,4] 自身。
输入:nums = [1]
输出:0
1 <= nums.length <= 5000
-1000 <= nums[i] <= 1000
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
int n = nums.length;
if (n == 1) {
return 0;
}
int t = nums[0] - nums[1], count = 0;
int ans = 0;
for (int i = 2; i < n; ++i) {
if (nums[i - 1] - nums[i] == t) {
count++;
} else {
t = nums[i - 1] - nums[i];
count = 0;
}
ans += count;
}
return ans;
}
}
day15-动态规划
91. 解码方法
A-Z
的消息通过以下映射进行了 编码 :'A' -> 1
'B' -> 2
...
'Z' -> 26
"11106"
可以映射为:
"AAJF"
,将消息分组为 (1 1 10 6)
"KJF"
,将消息分组为 (11 10 6)
(1 11 06)
,因为 "06"
不能映射为 "F"
,这是由于 "6"
和 "06"
在映射中并不等价。s
,请计算并返回 解码 方法的 总数 。输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。
输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
输入:s = "0"
输出:0
解释:没有字符映射到以 0 开头的数字。
含有 0 的有效映射是 'J' -> "10" 和 'T'-> "20" 。
由于没有字符,因此没有有效的方法对此进行解码,因为所有数字都需要映射。
输入:s = "06"
输出:0
解释:"06" 不能映射到 "F" ,因为字符串含有前导 0("6" 和 "06" 在映射中并不等价)。
1 <= s.length <= 100
s
只包含数字,并且可能包含前导零。class Solution {
public int numDecodings(String s) {
int n = s.length();
if(n==0) return 0;
int[] dp = new int[n];
char[] c = s.toCharArray();
if(c[0]=='0') return 0;
dp[0]=1;
for(int i=1;i<n;i++){
if(c[i]!='0'){
dp[i]=dp[i-1];
}
int temp = (c[i]-'0')+(c[i-1]-'0')*10;
if(temp>=10&&temp<=26){
if(i==1){
dp[i]++;
}else{
dp[i]+=dp[i-2];
}
}
}
return dp[n-1];
}
}
139. 单词拆分
s
和一个字符串列表 wordDict
作为字典,判定 s
是否可以由空格拆分为一个或多个在字典中出现的单词。输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
注意你可以重复使用字典中的单词。
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false
1 <= s.length <= 300
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s
和 wordDict[i]
仅有小写英文字母组成wordDict
中的所有字符串 互不相同class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> set = new HashSet<>(wordDict);
int n = s.length();
boolean[] dp = new boolean[n+1];
dp[0] = true;
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
if(dp[j]&&set.contains(s.substring(j,i))){
dp[i]=true;
break;
}
}
}
return dp[n];
}
}
day16-动态规划
300. 最长递增子序列
nums
,找到其中最长严格递增子序列的长度。[3,6,2,7]
是数组 [0,3,1,6,2,2,7]
的子序列。输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
输入:nums = [0,1,0,3,2,3]
输出:4
输入:nums = [7,7,7,7,7,7,7]
输出:1
1 <= nums.length <= 2500
-104 <= nums[i] <= 104
O(n2)
的解决方案吗?O(n log(n))
吗?class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
if(n==0) return 0;
int[] dp = new int[n];
dp[0] = 1;
int ans = 1;
for(int i=1;i<n;i++){
dp[i] = 1;
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i] = Math.max(dp[i], dp[j]+1);
}
}
ans = Math.max(ans, dp[i]);
}
return ans;
}
}
673. 最长递增子序列的个数
输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
输入: [2,2,2,2,2]
输出: 5
解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
class Solution {
public int findNumberOfLIS(int[] nums) {
int n = nums.length;
if(n==0) return 0;
int[] dp = new int[n];
int[] count = new int[n];
int max = 1;
for(int i=0;i<n;i++){
dp[i] = 1;
count[i] = 1;
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
if(dp[i]<dp[j]+1){
dp[i] = dp[j]+1;
count[i] = count[j];
}else if(dp[i]==dp[j]+1){
count[i] += count[j];
}
}
}
max = Math.max(max, dp[i]);
}
int ans = 0;
for(int i=0;i<n;i++){
if(dp[i]==max){
ans += count[i];
}
}
return ans;
}
}
day17-动态规划
1143. 最长公共子序列
text1
和 text2
,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0
。
"ace"
是 "abcde"
的子序列,但 "aec"
不是 "abcde"
的子序列。输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。
1 <= text1.length, text2.length <= 1000
text1
和 text2
仅由小写英文字符组成。class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
int[][] dp = new int[m+1][n+1];
for(int i=1;i<=m;i++){
char c1 = text1.charAt(i-1);
for(int j=1;j<=n;j++){
char c2 = text2.charAt(j-1);
if(c1==c2){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[m][n];
}
}
583. 两个字符串的删除操作
输入: "sea", "eat"
输出: 2
解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea "
class Solution {
public int minDistance(String text1, String text2) {
int m = text1.length(), n = text2.length();
int[][] dp = new int[m+1][n+1];
for(int i=1;i<=m;i++){
char c1 = text1.charAt(i-1);
for(int j=1;j<=n;j++){
char c2 = text2.charAt(j-1);
if(c1==c2){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
int t = dp[m][n];
return m-t+n-t;
}
}
day18-动态规划
72. 编辑距离
word1
和 word2
,请你计算出将 word1
转换成 word2
所使用的最少操作数 。
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
0 <= word1.length, word2.length <= 500
word1
和 word2
由小写英文字母组成class Solution {
public int minDistance(String word1, String word2) {
int n = word1.length();
int m = word2.length();
// 有一个字符串为空串
if (n * m == 0) {
return n + m;
}
// DP 数组
int[][] D = new int[n + 1][m + 1];
// 边界状态初始化
for (int i = 0; i < n + 1; i++) {
D[i][0] = i;
}
for (int j = 0; j < m + 1; j++) {
D[0][j] = j;
}
// 计算所有 DP 值
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < m + 1; j++) {
int left = D[i - 1][j] + 1;
int down = D[i][j - 1] + 1;
int left_down = D[i - 1][j - 1];
if (word1.charAt(i - 1) != word2.charAt(j - 1)) {
left_down += 1;
}
D[i][j] = Math.min(left, Math.min(down, left_down));
}
}
return D[n][m];
}
}
322. 零钱兑换
coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。-1
。输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
输入:coins = [2], amount = 3
输出:-1
输入:coins = [1], amount = 0
输出:0
输入:coins = [1], amount = 1
输出:1
输入:coins = [1], amount = 2
输出:2
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
class Solution {
public int coinChange(int[] coins, int amount) {
int n = coins.length;
int[] dp = new int[amount+1];
Arrays.fill(dp, amount+1);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < n; j++) {
if(i>=coins[j]){
dp[i] = Math.min(dp[i-coins[j]]+1,dp[i]);
}
}
}
return dp[amount]>amount ? -1:dp[amount];
}
}
343. 整数拆分
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
class Solution {
public int integerBreak(int n) {
int[] dp = new int[n+1];
for(int i=2;i<=n;i++){
int t = 0;
for(int j=1;j<i;j++){
t = Math.max(t,Math.max(j*(i-j),j*dp[i-j]));
}
dp[i] = t;
}
return dp[n];
}
}
day19-动态规划
201. 数字范围按位与
left
和 right
,表示区间 [left, right]
,返回此区间内所有数字 按位与 的结果(包含 left
、right
端点)。输入:left = 5, right = 7
输出:4
输入:left = 0, right = 0
输出:0
输入:left = 1, right = 2147483647
输出:0
0 <= left <= right <= 231 - 1
class Solution {
public int rangeBitwiseAnd(int left, int right) {
while(left<right){
right = right&(right-1);
}
return right;
}
}
day20-其他
384. 打乱数组
Solution
class:
Solution(int[] nums)
使用整数数组 nums
初始化对象int[] reset()
重设数组到它的初始状态并返回int[] shuffle()
返回数组随机打乱后的结果输入
["Solution", "shuffle", "reset", "shuffle"]
[[[1, 2, 3]], [], [], []]
输出
[null, [3, 1, 2], [1, 2, 3], [1, 3, 2]]
解释
Solution solution = new Solution([1, 2, 3]);
solution.shuffle(); // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。例如,返回 [3, 1, 2]
solution.reset(); // 重设数组到它的初始状态 [1, 2, 3] 。返回 [1, 2, 3]
solution.shuffle(); // 随机返回数组 [1, 2, 3] 打乱后的结果。例如,返回 [1, 3, 2]
1 <= nums.length <= 200
-106 <= nums[i] <= 106
nums
中的所有元素都是 唯一的5 * 104
次 reset
和 shuffle
class Solution {
int[] nums;
int n;
Random random = new Random();
public Solution(int[] nums) {
this.nums = nums;
n = nums.length;
}
public int[] reset() {
return nums;
}
public int[] shuffle() {
int[] ans = nums.clone();
for(int i=0;i<n;i++){
swap(ans, i, i+random.nextInt(n-i));
}
return ans;
}
public void swap(int[] arr, int i, int j){
int c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(nums);
* int[] param_1 = obj.reset();
* int[] param_2 = obj.shuffle();
*/
202. 快乐数
n
是不是快乐数。
n
是快乐数就返回 true
;不是,则返回 false
。输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
输入:n = 2
输出:false
1 <= n <= 231 - 1
class Solution {
private int getNext(int n){
int sum=0;
while(n>0){
int t = n%10;
n = n/10;
sum += t*t;
}
return sum;
}
public boolean isHappy(int n) {
Set<Integer> s = new HashSet<>();
while(n!=1&&!s.contains(n)){
s.add(n);
n = getNext(n);
}
return n==1;
}
}
149. 直线上最多的点数
points
,其中 points[i] = [xi, yi]
表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。输入:points = [[1,1],[2,2],[3,3]]
输出:3
输入:points = [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出:4
1 <= points.length <= 300
points[i].length == 2
-104 <= xi, yi <= 104
points
中的所有点 互不相同class Solution {
public int maxPoints(int[][] points) {
int n = points.length;
int ans = 1;
for(int i=0;i<n;i++){
int[] x = points[i];
for(int j=i+1;j<n;j++){
int[] y = points[j];
int temp = 2;
for(int k=j+1;k<n;k++){
int[] z = points[k];
int s1 = (y[1]-x[1])*(z[0]-y[0]);
int s2 = (z[1]-y[1])*(y[0]-x[0]);
if(s1==s2) temp++;
}
ans = Math.max(ans,temp);
}
}
return ans;
}
}
day21-二分查找
704. 二分查找
n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
示例 1:输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
nums
中的所有元素是不重复的。n
将在 [1, 10000]
之间。nums
的每个元素都将在 [-9999, 9999]
之间。class Solution {
public int search(int[] nums, int target) {
int L = 0, R = nums.length-1;
while(L<=R){
int mid = (R+L)/2;
if(nums[mid]>target){
R = mid-1;
}else if(nums[mid]<target){
L = mid+1;
}else{
return mid;
}
}
return -1;
}
}
278. 第一个错误的版本
n
个版本 [1, 2, ..., n]
,你想找出导致之后所有版本出错的第一个错误的版本。bool isBadVersion(version)
接口来判断版本号 version
是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
输入:n = 1, bad = 1
输出:1
1 <= bad <= n <= 231 - 1
/* The isBadVersion API is defined in the parent class VersionControl.
boolean isBadVersion(int version); */
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int L = 1, R = n;
while(L<R){
int mid = L+(R-L)/2;//不能使用(L+R)/2,会溢出
if(isBadVersion(mid)){
R = mid;
}else{
L = mid+1;
}
}
return L;
}
}
35. 搜索插入位置
O(log n)
的算法。输入: nums = [1,3,5,6], target = 5
输出: 2
输入: nums = [1,3,5,6], target = 2
输出: 1
输入: nums = [1,3,5,6], target = 7
输出: 4
输入: nums = [1,3,5,6], target = 0
输出: 0
输入: nums = [1], target = 0
输出: 0
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
为无重复元素的升序排列数组-104 <= target <= 104
class Solution {
public int searchInsert(int[] nums, int target) {
int L = 0, R = nums.length-1, mid=0;
while(L<=R){
mid = L+(R-L)/2;
int num = nums[mid];
if(num>target){
R = mid-1;
}else if(num<target){
L = mid+1;
}else{
return mid;
}
}
//判断:L==R时,当前值小于target,L右移,L为插入位置;当前值大于target,当前位置为插入位置;
return L>mid ? L : mid;
}
}
class Solution {
public int searchInsert(int[] nums, int target) {
int L = 0, R = nums.length-1;
while(L<=R){
int mid = L+(R-L)/2;
if(nums[mid]<target){
L = mid+1;
}else{
R = mid-1;
}
}
//同1理,当前值大于target,当前位置L为插入位置。所以L始终为插入位置。
return L;
}
}
day22-双指针
977. 有序数组的平方
nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
已按 非递减顺序 排序
O(n)
的算法解决本问题class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int L=0, R=n-1, idx=n-1;
int[] ans = new int[n];
while(L<=R){
int a=nums[L]*nums[L], b=nums[R]*nums[R];
if(a>b){
ans[idx--] = a;
L++;
}else{
ans[idx--] = b;
R--;
}
}
return ans;
}
}
189. 轮转数组
k
个位置,其中 k
是非负数。输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
O(1)
的 原地 算法解决这个问题吗?class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
int m = n, i = 0, j = 0, temp = 0, num = nums[i];
if(k%n == 0) return;
while(m--!=0){
i = (i+k)%n;
temp = nums[i];
nums[i] = num;
num = temp;
if (i == j) {
i = ++j;
num = nums[i];
temp = nums[i];
}
}
}
}
day23-双指针
283. 移动零
nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
class Solution {
public void moveZeroes(int[] nums) {
int n=nums.length, L=0, R=0;
//非零数向前移,最后补零
while(R<n){
if(nums[R]!=0){
if(L!=R){
nums[L]=nums[R];
L++;
R++;
}else{
L++;
R++;
}
}else{
R++;
}
}
while(L<n){
nums[L++]=0;
}
}
}
class Solution {
public void moveZeroes(int[] nums) {
int n=nums.length, L=0, R=0;
while(R<n){
if(nums[R]!=0){
if(L!=R){
nums[L]=nums[R];
}
L++;
}
R++;
}
while(L<n){
nums[L++]=0;
}
}
}
167. 两数之和 II - 输入有序数组
numbers
,请你从数组中找出两个数满足相加之和等于目标数 target
。2
的整数数组的形式返回这两个数的下标值*。*numbers
的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length
。输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
输入:numbers = [2,3,4], target = 6
输出:[1,3]
输入:numbers = [-1,0], target = -1
输出:[1,2]
2 <= numbers.length <= 3 * 104
-1000 <= numbers[i] <= 1000
numbers
按 非递减顺序 排列-1000 <= target <= 1000
class Solution {
public int[] twoSum(int[] numbers, int target) {
int[] temp = new int[2];
int L=0,R=numbers.length-1;
while(numbers[L]+numbers[R]!=target){
if(numbers[L]+numbers[R]<target){
L++;
}else{
R--;
}
}
temp[0]=L+1;
temp[1]=R+1;
return temp;
}
}
day24-双指针
344. 反转字符串
s
的形式给出。输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
1 <= s.length <= 105
s[i]
都是 ASCII 码表中的可打印字符class Solution {
public void reverseString(char[] s) {
int L=0, R=s.length-1;
while(L<R){
char temp = s[L];
s[L] = s[R];
s[R] = temp;
L++;R--;
}
}
}
557. 反转字符串中的单词 III
输入:"Let's take LeetCode contest"
输出:"s'teL ekat edoCteeL tsetnoc"
class Solution {
public String reverseWords(String s) {
StringBuffer ans = new StringBuffer();
for(int i=0;i<s.length();i++){
int j=i;
while(i<s.length()&&s.charAt(i)!=' ') i++;
int k=i-1;
while(j<=k){
ans.append(s.charAt(k));
k--;
}
if(i!=s.length())ans.append(" ");
}
return ans.toString();
}
}
day25-双指针
876. 链表的中间结点
head
的非空单链表,返回链表的中间结点。输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
1
和 100
之间。/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode middleNode(ListNode head) {
ListNode L = head, R = head;
boolean flag = true;
while(R.next!=null){
R = R.next;
if(flag){
L = L.next;
}
flag = !flag;
}
return L;
}
}
19. 删除链表的倒数第 N 个结点
n
个结点,并且返回链表的头结点。输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
输入:head = [1], n = 1
输出:[]
输入:head = [1,2], n = 1
输出:[1]
sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode L = new ListNode(0,head), R = head;
ListNode ans = L;
while(n-->0){
R = R.next;
}
while(R!=null){
R = R.next;
L = L.next;
}
L.next = L.next.next;
return ans.next;
}
}
day26-滑动窗口
3. 无重复字符的最长子串
s
,请你找出其中不含有重复字符的 最长子串 的长度。输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
输入: s = ""
输出: 0
0 <= s.length <= 5 * 104
s
由英文字母、数字、符号和空格组成class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character, Integer> m = new HashMap<>();
int L=0, R=0, ans=0;
while(R<s.length()){
char c = s.charAt(R);
if(m.containsKey(c)){
L = Math.max(L,m.get(c)+1);
}
m.put(c,R);
ans = Math.max(ans, R-L+1);
R++;
}
return ans;
}
}
567. 字符串的排列
s1
和 s2
,写一个函数来判断 s2
是否包含 s1
的排列。如果是,返回 true
;否则,返回 false
。s1
的排列之一是 s2
的 子串 。输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").
输入:s1= "ab" s2 = "eidboaoo"
输出:false
1 <= s1.length, s2.length <= 104
s1
和 s2
仅包含小写字母class Solution {
public boolean checkInclusion(String s1, String s2) {
int n = s1.length(), m = s2.length();
if (n > m) {
return false;
}
int[] nums1 = new int[26];
int[] nums2 = new int[26];
for (int i = 0; i < n; ++i) {
++nums1[s1.charAt(i) - 'a'];
++nums2[s2.charAt(i) - 'a'];
}
if (Arrays.equals(nums1, nums2)) {
return true;
}
for (int i = n; i < m; ++i) {
nums2[s2.charAt(i) - 'a']++;
nums2[s2.charAt(i - n) - 'a']--;
if (Arrays.equals(nums1, nums2)) {
return true;
}
}
return false;
}
}
day28-广度优先搜索 / 深度优先搜索
617. 合并二叉树
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
}
if (root2 == null) {
return root1;
}
TreeNode t = new TreeNode(root1.val + root2.val);
t.left = mergeTrees(root1.left, root2.left);
t.right = mergeTrees(root1.right, root2.right);
return t;
}
}
116. 填充每个节点的下一个右侧节点指针
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
NULL
。NULL
。
输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。
4096
-1000 <= node.val <= 1000
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/
class Solution {
public Node connect(Node root) {
if(root==null) return root;
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
int n = queue.size();
while(n-->0){
Node t = queue.poll();
if(n!=0){
t.next = queue.peek();
}
if(t.left!=null){
queue.add(t.left);
}
if(t.right!=null){
queue.add(t.right);
}
}
}
return root;
}
}
day29-广度优先搜索 / 深度优先搜索
542. 01 矩阵
0
和 1
组成的矩阵 mat
,请输出一个大小相同的矩阵,其中每一个格子是 mat
中对应位置元素到最近的 0
的距离。1
。输入:mat = [[0,0,0],[0,1,0],[0,0,0]]
输出:[[0,0,0],[0,1,0],[0,0,0]]
输入:mat = [[0,0,0],[0,1,0],[1,1,1]]
输出:[[0,0,0],[0,1,0],[1,2,1]]
m == mat.length
n == mat[i].length
1 <= m, n <= 104
1 <= m * n <= 104
mat[i][j] is either 0 or 1.
mat
中至少有一个 0
class Solution {
int[] xx = {0,0,1,-1}, yy = {1,-1,0,0};
public int[][] updateMatrix(int[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
Queue<int[]> queue = new LinkedList<>();
//将所有的1替换为-1, 将所有0加入队列中
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == 1){
matrix[i][j] = -1;
} else {
queue.add(new int[]{i,j});
}
}
}
while (!queue.isEmpty()){
int[] point = queue.poll();
int a = point[0];
int b = point[1];
for (int i = 0; i < 4; i++) {
int x = a + xx[i];
int y = b + yy[i];
if (x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] == -1){
matrix[x][y] = matrix[a][b] + 1;
queue.add(new int[]{x,y});
}
}
}
return matrix;
}
}
class Solution {
public int[][] updateMatrix(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
int[][] ans = new int[m][n];
for(int i=0;i<m;i++){
Arrays.fill(ans[i],Integer.MAX_VALUE/2);
}
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(matrix[i][j]==0){
ans[i][j]=0;
}
}
}
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(i-1>=0){
ans[i][j] = Math.min(ans[i][j], ans[i-1][j]+1);
}
if(j-1>=0){
ans[i][j] = Math.min(ans[i][j], ans[i][j-1]+1);
}
}
}
for(int i=m-1;i>=0;i--){
for(int j=n-1;j>=0;j--){
if(i+1<m){
ans[i][j] = Math.min(ans[i][j], ans[i+1][j]+1);
}
if(j+1<n){
ans[i][j] = Math.min(ans[i][j], ans[i][j+1]+1);
}
}
}
return ans;
}
}
994. 腐烂的橘子
0
代表空单元格;1
代表新鲜橘子;2
代表腐烂的橘子。-1
。输入:[[2,1,1],[1,1,0],[0,1,1]]
输出:4
输入:[[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。
输入:[[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
1 <= grid.length <= 10
1 <= grid[0].length <= 10
grid[i][j]
仅为 0
、1
或 2
class Solution {
public int orangesRotting(int[][] grid) {
int M = grid.length;
int N = grid[0].length;
Queue<int[]> queue = new LinkedList<>();
int count = 0; // count 表示新鲜橘子的数量
for (int r = 0; r < M; r++) {
for (int c = 0; c < N; c++) {
if (grid[r][c] == 1) {
count++;
} else if (grid[r][c] == 2) {
queue.add(new int[]{r, c});
}
}
}
int round = 0; // round 表示腐烂的轮数,或者分钟数
while (count > 0 && !queue.isEmpty()) {
round++;
int n = queue.size();
for (int i = 0; i < n; i++) {
int[] orange = queue.poll();
int r = orange[0];
int c = orange[1];
if (r-1 >= 0 && grid[r-1][c] == 1) {
grid[r-1][c] = 2;
count--;
queue.add(new int[]{r-1, c});
}
if (r+1 < M && grid[r+1][c] == 1) {
grid[r+1][c] = 2;
count--;
queue.add(new int[]{r+1, c});
}
if (c-1 >= 0 && grid[r][c-1] == 1) {
grid[r][c-1] = 2;
count--;
queue.add(new int[]{r, c-1});
}
if (c+1 < N && grid[r][c+1] == 1) {
grid[r][c+1] = 2;
count--;
queue.add(new int[]{r, c+1});
}
}
}
if (count > 0) {
return -1;
} else {
return round;
}
}
}
day30-递归 / 回溯
77. 组合
n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
输入:n = 1, k = 1
输出:[[1]]
1 <= n <= 20
1 <= k <= n
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
for(int i=1;i<=n-k+1;i++){
List<Integer> temp = new ArrayList<Integer>();
temp.add(i);
ans.add(temp);
}
for(int i=0;i<k-1;i++){
int m = ans.size();
for(int j=0;j<m;j++){
List<Integer> temp = new ArrayList<>(ans.get(0));
for(int p=temp.get(temp.size()-1)+1;n-p>=k-i-2;p++){
temp.add(p);
ans.add(new ArrayList<>(temp));
temp.remove(temp.size()-1);
}
ans.remove(0);
}
}
return ans;
}
}
46. 全排列
nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
输入:nums = [0,1]
输出:[[0,1],[1,0]]
输入:nums = [1]
输出:[[1]]
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums
中的所有整数 互不相同class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> temp = new ArrayList<Integer>();
for(int num:nums){
temp.add(num);
}
int n = nums.length;
fun(n, ans, temp, 0);
return ans;
}
public void fun(int n, List<List<Integer>> ans, List<Integer> temp, int first){
if(first==n){
ans.add(new ArrayList<Integer>(temp));
}
for(int i=first;i<n;i++){
Collections.swap(temp,first,i);
fun(n,ans,temp,first+1);
Collections.swap(temp,first,i);
}
}
}
784. 字母大小写全排列
S
,通过将字符串S
中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。示例:
输入:S = "a1b2"
输出:["a1b2", "a1B2", "A1b2", "A1B2"]
输入:S = "3z4"
输出:["3z4", "3Z4"]
输入:S = "12345"
输出:["12345"]
S
的长度不超过12
。S
仅由数字和字母组成。class Solution {
List<String> ans = new ArrayList();
public List<String> letterCasePermutation(String s) {
getStr(0, s, new StringBuffer());
return ans;
}
public void getStr(int index, String s, StringBuffer sb){
if(index == s.length()){
ans.add(sb.toString());
return;
}
char ch = s.charAt(index);
sb.append(ch);
getStr(index + 1, s, sb);
sb.deleteCharAt(sb.length() - 1);
if(!Character.isDigit(ch)){
ch = (char)(ch - 'a' >= 0 ? ch - 32 : ch + 32);
sb.append(ch);
getStr(index + 1, s, sb);
sb.deleteCharAt(sb.length() - 1);
}
}
}
day31-动态规划
70. 爬楼梯
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
class Solution {
public int climbStairs(int n) {
if(n==1) return 1;
if(n==2) return 2;
int[] sum = new int[n];
sum[0] = 1;
sum[1] = 2;
for(int i=2;i<n;i++){
sum[i] = sum[i-1]+sum[i-2];
}
return sum[n-1];
}
}
198. 打家劫舍
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
1 <= nums.length <= 100
0 <= nums[i] <= 400
class Solution {
public int rob(int[] nums) {
if(nums.length==0||nums==null) return 0;
int n = nums.length;
if(n==1) return nums[0];
int a = nums[0], b = Math.max(nums[0], nums[1]);
for(int i=2;i<n;i++){
int temp = b;
b = Math.max(b, nums[i]+a);
a = temp;
}
return b;
}
}
120. 三角形最小路径和
triangle
,找出自顶向下的最小路径和。i
,那么下一步可以移动到下一行的下标 i
或 i + 1
。输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
输入:triangle = [[-10]]
输出:-10
1 <= triangle.length <= 200
triangle[0].length == 1
triangle[i].length == triangle[i - 1].length + 1
-104 <= triangle[i][j] <= 104
O(n)
的额外空间(n
为三角形的总行数)来解决这个问题吗?class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[] dp = new int[n + 1];
for (int i = n - 1; i >= 0; i--) {
for (int j = 0; j <= i; j++) {
dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);
}
}
return dp[0];
}
}
day31-位运算
231. 2 的幂
n
,请你判断该整数是否是 2 的幂次方。如果是,返回 true
;否则,返回 false
。x
使得 n == 2x
,则认为 n
是 2 的幂次方。输入:n = 1
输出:true
解释:20 = 1
输入:n = 16
输出:true
解释:24 = 16
输入:n = 3
输出:false
输入:n = 4
输出:true
输入:n = 5
输出:false
-231 <= n <= 231 - 1
class Solution {
public boolean isPowerOfTwo(int n) {
return n>0 && (n&(n-1))==0;
}
}
191. 位1的个数
-3
。输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
32
的 二进制串 。
public class Solution {
public int hammingWeight(int n) {
int ans = 0;
for(int i=0;i<32;i++){
if((n&(1<<i))!=0) ans++;
}
return ans;
}
}
public class Solution {
public int hammingWeight(int n) {
int ans = 0;
while(n!=0){
n &= n-1;
ans++;
}
return ans;
}
}