17.给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:输入:digits = ""
输出:[]
示例 3:输入:digits = "2"
输出:["a","b","c"]
提示:
0 <= digits.length <= 4
digits[i] 是范围 ['2', '9'] 的一个数字。
class Solution {
public List<String> letterCombinations(String digits) {
List<String> tab=new ArrayList<>();
if(digits.length()==0){
return tab;
}
//建立哈希表存储
Map<Character,String> hashMap=new HashMap<>();
hashMap.put('2',"abc");
hashMap.put('3',"def");
hashMap.put('4',"ghi");
hashMap.put('5',"jkl");
hashMap.put('6',"mno");
hashMap.put('7',"pqrs");
hashMap.put('8',"tuv");
hashMap.put('9',"wxyz");
//回溯一下呢?
func(tab,digits,hashMap,0,new StringBuilder());
return tab;
}
//n标识从第几个字符开始,sb用于拼接
public void func(List<String> tab,String digits,Map<Character,String> map,int n,StringBuilder sb){
//第n个字符的映射,如果够长了,就添加到列表中
if(n==digits.length()){
tab.add(sb.toString());
}else{
//否则就添加
char c=digits.charAt(n);
//找到这个字符对应的字符串
String s=map.get(c);
int length=s.length();
for(int i=0;i<length;i++){
sb.append(s.charAt(i));
func(tab,digits,map,n+1,sb);
//回溯,比如23,现在sb里有个a,然后调用func sb变成ad,这个时候满足长度,添加到tab,
//然后这个调用结束,再删除d,然后sb里还是a,sb变成ae,这个时候满足长度,添加到tab,
//然后这个调用结束,再删除e,然后sb里还是a,sb变成af,这个时候满足长度,添加到tab,
//然后这个调用结束,再删除f,小for循环也结束,走到大for循环,直至回溯完成。
sb.deleteCharAt(n);
}
}
}
}
22.数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:输入:n = 1
输出:["()"]
提示:
1 <= n <= 8
class Solution {
public List<String> generateParenthesis(int n) {
//也是一个回溯题
List<String> res=new ArrayList<>();
func(res,n,0,0,new StringBuilder());
return res;
}
public void func(List<String> res,int n,int indexLeft,int indexRight,StringBuilder sb){
//如果左括号和右括号都达到n个,就加入列表
if(sb.length()==2*n){
res.add(sb.toString());
return;
}
if(indexLeft<n){
sb.append("(");
func(res,n,indexLeft+1,indexRight,sb);
sb.deleteCharAt(sb.length()-1);
}
if(indexRight<indexLeft){
sb.append(")");
func(res,n,indexLeft,indexRight+1,sb);
sb.deleteCharAt(sb.length()-1);
}
}
}
32.给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
示例 2:输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
示例 3:输入:s = ""
输出:0
提示:
0 <= s.length <= 3 * 104
s[i] 为 '(' 或 ')'
1.一些令人窒息的动态规划
class Solution {
public int longestValidParentheses(String s) {
//动态规划是什么,我是谁
int n=s.length();
int max=0;
if(n==0){
return 0;
}
int[] res=new int[n];
for(int i=1;i<n;i++){
if(s.charAt(i)==')'){
if(s.charAt(i-1)=='('){
//如果一开局就来了一对,必须初始化为2,因为-2越界
if(i<2){
res[i]+=2;
}else{
res[i]=res[i-2]+2;
}
}else{
//前一个右括号对称的点的前一个的结果和前一个右括号的结果再加2,相当于不仅考虑了里面的,还考虑了外面的。
//如果对称的点不再列表内,就直接里面加2,否则还要算上外面的。
if(i-1-res[i-1]>=0&&s.charAt(i-1-res[i-1])=='('){
if(i-2-res[i-1]>=0){
res[i]=res[i-1-res[i-1]-1]+2+res[i-1];
}else{
res[i]=2+res[i-1];
}
}
}
}
max=Math.max(res[i],max);
}
return max;
}
}
2.栈
class Solution {
public int longestValidParentheses(String s) {
//栈应该比较好搞
Stack<Integer> stack=new Stack<>();
//如果是左括号,直接压i,是右括号,弹出,弹完为空则压栈就是没匹配到的右括号,不为空说明匹配到了,结果是当前下标减栈顶。
//如果栈为空,说明当前的右括号为没有被匹配的右括号,我们将其下标放入栈中来更新我们之前提到的「最后一个没有被匹配的右括号的下标」
//如果栈不为空,当前右括号的下标减去栈顶元素即为「以该右括号为结尾的最长有效括号的长度」
int n=s.length();
if(n==0){return 0;}
int max=0;
stack.push(-1);
for(int i=0;i<n;i++){
if(s.charAt(i)=='('){
stack.push(i);
}else{
stack.pop();
if(stack.isEmpty()){
stack.push(i);
}else{
max=Math.max(max,i-stack.peek());
}
}
}
return max;
}
}
其实这个题目第一想法就是双指针,但是没有考虑周全。
class Solution {
public int longestValidParentheses(String s) {
//有点像接雨水的意思了
int n=s.length();
if(n==0){return 0;}
int left=0;
int right=0;
int max1=0;
int max2=0;
for(int i=0;i<n;i++){
if(s.charAt(i)=='('){
left++;
}else{
right++;
}
if(left==right){
max1=Math.max(max1,left*2);
}else if(right>left){
left=0;
right=0;
}
}
left=0;
right=0;
for(int i=n-1;i>=0;i--){
if(s.charAt(i)=='('){
left++;
}else{
right++;
}
if(left==right){
max2=Math.max(max2,left*2);
}else if(left>right){
left=0;
right=0;
}
}
return Math.max(max1,max2);
}
}
34.给定一个按照升序排列的整数数组 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) {
int[] res=new int[]{-1,-1};
int l=binarySearch(nums,target,true);
//???为什么减1,因为false找的是大于目标的第一个,所以后面还要满足r的值为target,如果不满足意味着压根没有目标值。
int r=binarySearch(nums,target,false)-1;
//为什么要写个这个条件???
if(l<=r&&r<nums.length&&nums[l]==target&&nums[r]==target){
res[0]=l;
res[1]=r;
}
return res;
}
public int binarySearch(int[] nums,int target,boolean flag){
//找最小值
int left=0;
int right=nums.length-1;
int temp;
//???为什么是这么多,只要不在范围内?为什么-1不行
int res=nums.length;
while(left<=right){
temp=(left+right)/2;
//false找大于目标的第一个,true找大于等于目标的第一个
if(nums[temp]>target||(flag&&nums[temp]>=target)){
res=temp;
right=temp-1;
}else{
left=temp+1;
}
}
return res;
}
}
39.给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 1:
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
示例 2:输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:输入: candidates = [2], target = 1
输出: []
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都 互不相同
1 <= target <= 500
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res=new ArrayList<>();
List<Integer> temp=new ArrayList<>();
func(res,candidates,target,temp,0);
return res;
}
//感觉可以回溯一下
public void func(List<List<Integer>> res,int[] candidates,int target,List<Integer> temp,int index){
int n=candidates.length;
//走完了整个组合,结束
if(index==n){
return;
}
//如果此时目标已经减到0,就可以加进去返回了
if(target==0){
res.add(new ArrayList<>(temp));
return;
}
//往下走
func(res,candidates,target,temp,index+1);
if(target-candidates[index]>=0){
//继续走
temp.add(candidates[index]);
func(res,candidates,target-candidates[index],temp,index);
//回溯
temp.remove(temp.size()-1);
}
}
}
62.一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7
输出:28
示例 2:输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下
示例 3:输入:m = 7, n = 3
输出:28
示例 4:输入:m = 3, n = 3
输出:6
提示:
1 <= m, n <= 100
题目数据保证答案小于等于 2 * 109
递归肯定是可以的qwq
class Solution {
public int uniquePaths(int m, int n) {
//思路:递归可以,如果第一排的话只可能是右移来,第一列是下移;其他都有两种可能。
if(m==1){
return 1;
}
if(n==1){
return 1;
}
return uniquePaths(m-1,n)+uniquePaths(m,n-1);
}
}
一个经典的超时出现了!好耶!
那么动态规划一下:
class Solution {
public int uniquePaths(int m, int n) {
//动态规划打表了
int[][] res=new int[m][n];
for(int i=0;i<n;i++){
res[0][i]=1;
}
for(int j=0;j<m;j++){
res[j][0]=1;
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
res[i][j]=res[i-1][j]+res[i][j-1];
}
}
return res[m-1][n-1];
}
}
笑死,排列组合还给高中老师了。
class Solution {
public int uniquePaths(int m, int n) {
long ans=1;
for(int x=m,y=1;y<n;y++,x++){
ans=ans*x/y;
}
return (int)ans;
}
}
55.给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
提示:
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
记录当前能达到的最远路径,只要大于等于数组最后一个值的下标即可。
注意:要判断当前i是否能达到!!!
class Solution {
public boolean canJump(int[] nums) {
int n=nums.length;
int max=0;
if(n==1){
return true;
}
for(int i=0;i<n-1;i++){
//要判断当前的i是否是能到达的地方!!!
if(i<=max){
max=Math.max(max,i+nums[i]);
if(max>=n-1){
return true;
}
}
}
return false;
}
}
105.给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
示例 2:输入: preorder = [-1], inorder = [-1]
输出: [-1]
提示:
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder 和 inorder 均 无重复 元素
inorder 均出现在 preorder
preorder 保证 为二叉树的前序遍历序列
inorder 保证 为二叉树的中序遍历序列
九日集训做过的题,浅写一个递归.注意使用哈希表减少遍历
class Solution {
Map<Integer,Integer> hashMap=new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
//做一个哈希表快速找根节点,以防每次都遍历
int n=preorder.length;
for(int i=0;i<n;i++){
hashMap.put(inorder[i],i);
}
return func(preorder,inorder,0,n-1,0,n-1);
}
//做一个递归函数
public TreeNode func(int[] preorder,int[] inorder,int pl,int pr,int il,int ir){
//退出递归的条件
if(pl>pr){
return null;
}
//先找到中序遍历中根节点的位置
int preroot=pl;
int inroot=hashMap.get(preorder[preroot]);
int leftlength=inroot-il;
int rightLength=ir-inroot;
TreeNode root=new TreeNode(preorder[preroot]);
root.left=func(preorder,inorder,pl+1,pl+leftlength,il,inroot-1);
root.right=func(preorder,inorder,pl+leftlength+1,pr,inroot+1,ir);
return root;
}
}
迭代真的就是儿子论
128.给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
判断这个数的前一个数没有!!!
class Solution {
public int longestConsecutive(int[] nums) {
//快乐哈希
int n=nums.length;
if(n==0){
return 0;
}
Set<Integer> hashSet=new HashSet<>();
for(int i=0;i<n;i++){
hashSet.add(nums[i]);
}
//遍历
int maxLength=1;
//hashSet可以直接foreach遍历
for(int i:hashSet){
//这个判断就是核心!!
if(!hashSet.contains(i-1)){
//从连续的入口开始
int k=1;
int num=i;
while(hashSet.contains(num+1)){
num++;
k++;
}
maxLength=Math.max(k,maxLength);
}
}
return maxLength;
}
}
560.给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
示例 2:输入:nums = [1,2,3], k = 3
输出:2
提示:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
笑死,子数组的概念是原数组连续元素构成的数组。
暴力遍历居然没超时,crazy!!
class Solution {
public int subarraySum(int[] nums, int k) {
//先搞出个枚举来
int length=nums.length;
if(length==1){
if(nums[0]==k){
return 1;
}else{
return 0;
}
}
int res=0;
int temp=0;
for(int i=0;i<length;i++){
temp=nums[i];
if(temp==k){
res++;
}
int j=i+1;
while(j<length){
temp+=nums[j];
j++;
if(temp==k){
res++;
}
}
}
return res;
}
}
哈希一下
class Solution {
public int subarraySum(int[] nums, int k) {
Map<Integer,Integer> hashMap=new HashMap<>();
int res=0;
int count=0;
//为什么呢,防止漏掉前面几个,比如说第一个就是k,如果不加(0,1)就会被漏掉。
hashMap.put(0,1);
for(int num:nums){
count+=num;
if(hashMap.containsKey(count-k)){
res+=hashMap.get(count-k);
}
//如果已经有这个数,这个数的急促上再加1
hashMap.put(count,hashMap.getOrDefault(count,0)+1);
}
return res;
}
}