剑指offer
面试题13: 机器人的运动范围
面试题13. 机器人的运动范围
地上有一个m行n列的方格,从坐标
[0,0]到坐标[m-1,n-1]。一个机器人从坐标[0, 0]的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?示例 1:
输入:m = 2, n = 3, k = 1 输出:3示例 2:
输入:m = 3, n = 1, k = 0 输出:1提示:
1 <= n,m <= 1000 <= k <= 20
解答
//测试能通过  提交就报错  离谱
class Solution {
    public static int res=0;
    public int movingCount(int m, int n, int k) {
         int  [][]nums=new int[m][n];
         dfs(i,j,m,n,k,nums);
        return res;
    }
     private static void dfs(int i,int j,int m, int n, int k,int  [][]nums) {
        if(i<0 || i>=m || j<0 || j>=n ||  !canWalk(i,j,k) || nums[i][j]==-1){
            return;
        }
        res++;
        nums[i][j]=-1;
        dfs(i+1,j,m,n,k,nums);
        dfs(i-1,j,m,n,k,nums);
        dfs(i,j-1,m,n,k,nums);
        dfs(i,j+1,m,n,k,nums);
    }
    private static boolean canWalk(int i, int j,int k) {
        String s = String.valueOf(i);
        String s1 = String.valueOf(j);
        StringBuilder builder=new StringBuilder();
        builder.append(s);
        builder.append(s1);
        int add=0;
        String temp=builder.toString();
        for (int l = 0; l <temp.length() ; l++) {
            add+=temp.charAt(l)-'0';
            if(add>k){
                return false;
            }
        }
        return true;
    }
}
解答2
class Solution {
    public int movingCount(int m, int n, int k) {
         int  [][]nums=new int[m][n];
         return  dfs(0,0,m,n,k,nums);  
    }
     private static int dfs(int i,int j,int m, int n, int k,int  [][]nums) {
        if(i<0 || i>=m || j<0 || j>=n ||   (i/10 + i%10 + j/10 + j%10) > k || nums[i][j]==-1){
            return 0;
        }
    
        nums[i][j]=-1;
      return  dfs(i+1,j,m,n,k,nums)+
                dfs(i-1,j,m,n,k,nums)+
                dfs(i,j-1,m,n,k,nums)+
                dfs(i,j+1,m,n,k,nums)+1;
    }   
}
剑指 Offer II 119. 最长连续序列
剑指 Offer II 119. 最长连续序列
难度中等40收藏分享切换为英文接收动态反馈
给定一个未排序的整数数组
nums,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。示例 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 <= 104-109 <= nums[i] <= 109
解答
class Solution {
    public int longestConsecutive(int[] nums) {
        if(nums.length<=1){
          return nums.length;
        }
         int []dp=new int[nums.length];
         int max=1;
         Arrays.sort(nums);
         dp[0]=1;
        for (int i = 1; i < nums.length; i++) {
            if(nums[i]==nums[i-1]+1){
                dp[i]=dp[i-1]+1;
                max=Math.max(dp[i],max);
            }else if(nums[i]==nums[i-1]){
                dp[i]=dp[i-1];
            }
            else{
                dp[i]=1;
            }
        }
        return max;
    }
}
剑指 Offer II 098. 路径的数目
剑指 Offer II 098. 路径的数目
难度中等24收藏分享切换为英文接收动态反馈
一个机器人位于一个
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
解答
// ps  做过很多遍了
class Solution {
    public int uniquePaths(int m, int n) {
        int [][]dp=new int[m][n];
        for (int i = 0; i <m ; i++) {
            dp[i][0]=1;
        }
        for (int i = 0; i <n ; i++) {
            dp[0][i]=1;
        }
  
        for (int i = 1; i < m; i++) {
            for (int j = 1; j <n ; j++) {
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
}
剑指offerII 099. 最小路径之和
剑指 Offer II 099. 最小路径之和
难度中等26收藏分享切换为英文接收动态反馈
给定一个包含非负整数的
*m* x *n*网格grid,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。说明:一个机器人每次只能向下或者向右移动一步。
示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]] 输出:7 解释:因为路径 1→3→1→1→1 的总和最小。示例 2:
输入:grid = [[1,2,3],[4,5,6]] 输出:12
解答
class Solution {
    public int minPathSum(int[][] grid) {
        int [][]dp=new int[grid.length][grid[0].length];
        dp[0][0]=grid[0][0];
        for (int i = 1; i <grid.length ; i++) {
           dp[i][0]=dp[i-1][0]+grid[i][0];
        }
        for (int i = 1; i <grid[0].length ; i++) {
            dp[0][i]=dp[0][i-1]+grid[0][i];
        }
        for (int i = 1; i <grid.length ; i++) {
            for (int j = 1; j <grid[0].length ; j++) {
                dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
            }
        }
        return dp[grid.length-1][grid[0].length-1];
    }
}
剑指 Offer II 080. 含有 k 个元素的组合
剑指 Offer II 080. 含有 k 个元素的组合
难度中等27收藏分享切换为英文接收动态反馈
给定两个整数
n和k,返回1 ... n中所有可能的k个数的组合。示例 1:
输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]示例 2:
输入: n = 1, k = 1 输出: [[1]]提示:
1 <= n <= 201 <= k <= n
解答
class Solution {
    public List<List<Integer>> combine(int n, int k) {
        int index=1;
        List<List<Integer>> res=new ArrayList<>();
        Deque<Integer> path=new LinkedList<>();
        backTrack(res,path,index,n,k);
        return res;
    }
     private static void backTrack(List<List<Integer>> res, Deque<Integer> path, int index, int n, int k) {
        if(path.size()==k){
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = index; i <=n; i++) {
            path.add(i);
            backTrack(res,path,i+1,n,k);
            path.removeLast();
        }
    }
}
剑指 Offer II 081. 允许重复选择元素的组合
剑指 Offer II 081. 允许重复选择元素的组合
难度中等29收藏分享切换为英文接收动态反馈
给定一个无重复元素的正整数数组
candidates和一个正整数target,找出candidates中所有可以使数字和为目标数target的唯一组合。
candidates中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是不同的。对于给定的输入,保证和为
target的唯一组合数少于150个。示例 1:
输入: candidates = [2,3,6,7], target = 7 输出: [[7],[2,2,3]]示例 2:
输入: candidates = [2,3,5], target = 8 输出: [[2,2,2,2],[2,3,3],[3,5]]示例 3:
输入: candidates = [2], target = 1 输出: []示例 4:
输入: candidates = [1], target = 1 输出: [[1]]示例 5:
输入: candidates = [1], target = 2 输出: [[1,1]]
解答
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
         List<List<Integer>> res=new ArrayList<>();
        Deque<Integer> path=new LinkedList<>();
        Arrays.sort(candidates);
        int begin=0;
        int sum=0;
        backTrack(res,path,candidates,begin,target,sum);
        return res;
    }
     private static void backTrack(List<List<Integer>> res, Deque<Integer> path, int[] candidates, int begin, int target,int sum) {
          if(sum>target) {
              return;
          }
          if(sum==target){
              res.add(new ArrayList<>(path));
              return;
            }
        for (int i = begin; i <candidates.length ; i++) {
            path.add(candidates[i]);
            backTrack(res,path,candidates,i,target,sum+candidates[i]);
            path.removeLast();
        }
    }
}
剑指 Offer II 082. 含有重复元素集合的组合
剑指 Offer II 082. 含有重复元素集合的组合
难度中等25收藏分享切换为英文接收动态反馈
给定一个可能有重复数字的整数数组
candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
candidates中的每个数字在每个组合中只能使用一次,解集不能包含重复的组合。示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8, 输出: [ [1,1,6], [1,2,5], [1,7], [2,6] ]示例 2:
输入: candidates = [2,5,2,1,2], target = 5, 输出: [ [1,2,2], [5] ]提示:
1 <= candidates.length <= 1001 <= candidates[i] <= 501 <= target <= 30
解答
class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res=new ArrayList<>();
        Deque<Integer> path=new LinkedList<>();
        Arrays.sort(candidates);
        boolean []used=new boolean[candidates.length];
        int begin=0;
        int sum=0;
        backTrack(res,path,used,candidates,begin,target,sum);
        return res;
    }
    private static void backTrack(List<List<Integer>> res, Deque<Integer> path,  boolean []used,int[] candidates, int begin, int target, int sum) {
        if(sum>target) {
            return;
        }
        if(sum==target && !res.contains(path)){
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = begin; i <candidates.length ; i++) {
            if(used[i]){
                continue;
            }
            used[i]=true;
            path.add(candidates[i]);
            backTrack(res,path,used,candidates,i,target,sum+candidates[i]);
            path.removeLast();
            used[i]=false;
        }
    }
}
剑指 Offer II 006. 排序数组中两个数字之和
剑指 Offer II 006. 排序数组中两个数字之和
难度简单43收藏分享切换为英文接收动态反馈
给定一个已按照 升序排列 的整数数组
numbers,请你从数组中找出两个数满足相加之和等于目标数target。函数应该以长度为
2的整数数组的形式返回这两个数的下标值。numbers的下标 从 0 开始计数 ,所以答案数组应当满足0 <= answer[0] < answer[1] < numbers.length。假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次。
示例 1:
输入:numbers = [1,2,4,6,10], target = 8 输出:[1,3] 解释:2 与 6 之和等于目标数 8 。因此 index1 = 1, index2 = 3 。示例 2:
输入:numbers = [2,3,4], target = 6 输出:[0,2]示例 3:
输入:numbers = [-1,0], target = -1 输出:[0,1]提示:
2 <= numbers.length <= 3 * 104-1000 <= numbers[i] <= 1000numbers按 递增顺序 排列-1000 <= target <= 1000- 仅存在一个有效答案
 
解答
class Solution {
    public int[] twoSum(int[] numbers, int target) {
         int []res=new int[2];
      int left=0;
      int right=numbers.length-1;
      int nums1=0,nums2=0;
          while (left<=right){
              nums1=numbers[left];
              nums2=numbers[right];
              if(nums1+nums2==target){
                  res[0]=left;
                  res[1]=right;
                  break;
              }else if(nums1+nums2>target){
                  right--;
              }else{
                  left++;
              }
          }
          return res;
    }
}
剑指 Offer II 091. 粉刷房子
剑指 Offer II 091. 粉刷房子
难度中等124
假如有一排房子,共
n个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个
n x 3的正整数矩阵costs来表示的。例如,
costs[0][0]表示第 0 号房子粉刷成红色的成本花费;costs[1][2]表示第 1 号房子粉刷成绿色的花费,以此类推。请计算出粉刷完所有房子最少的花费成本。
示例 1:
输入: costs = [[17,2,17],[16,16,5],[14,3,19]] 输出: 10 解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色。 最少花费: 2 + 5 + 3 = 10。示例 2:
输入: costs = [[7,6,2]] 输出: 2提示:
costs.length == ncosts[i].length == 31 <= n <= 1001 <= costs[i][j] <= 20
解答
/**
**/
 
class Solution {
    public int minCost(int[][] costs) {
        int [][]dp=new int[costs.length][3];
        dp[0][0]=costs[0][0];
        dp[0][1]=costs[0][1];
        dp[0][2]=costs[0][2];
        for (int i = 1; i <costs.length ; i++) {
            dp[i][0]=Math.min(dp[i-1][1],dp[i-1][2])+costs[i][0];
            dp[i][1]=Math.min(dp[i-1][2],dp[i-1][0])+costs[i][1];
            dp[i][2]=Math.min(dp[i-1][0],dp[i-1][1])+costs[i][2];
        }
        return Math.min(Math.min(dp[costs.length-1][1],dp[costs.length-1][0]),dp[costs.length-1][2]);
    }
}
剑指 Offer II 092. 翻转字符
剑指 Offer II 092. 翻转字符
难度中等42收藏分享切换为英文接收动态反馈
如果一个由
'0'和'1'组成的字符串,是以一些'0'(可能没有'0')后面跟着一些'1'(也可能没有'1')的形式组成的,那么该字符串是 单调递增 的。我们给出一个由字符
'0'和'1'组成的字符串 s,我们可以将任何'0'翻转为'1'或者将'1'翻转为'0'。返回使 s 单调递增 的最小翻转次数。
示例 1:
输入:s = "00110" 输出:1 解释:我们翻转最后一位得到 00111.示例 2:
输入:s = "010110" 输出:2 解释:我们翻转得到 011111,或者是 000111。示例 3:
输入:s = "00011000" 输出:2 解释:我们翻转得到 00000000。
解答
class Solution {
    public int minFlipsMonoIncr(String s) {
        // dp[i][0] 表示前i为 为0的最小翻转次数
       // dp[i][1] 表示前i为 为1的最小翻转次数
        int [][]dp=new int[s.length()][2];
        dp[0][0]=s.charAt(0)=='0'? 0:1;
        dp[0][1]=s.charAt(0)=='1'? 0:1;
        for(int i=1;i<s.length();i++){
            char c=s.charAt(i);
            if(c=='0'){
              dp[i][0]=dp[i-1][0];
              dp[i][1]=dp[i-1][1]+1;
            }else{
                //注意  这里要跟dp[i-1][1],dp[i-1][0]比较  因为如果为1的情况下前一位既可以为0 也可以为1  所以获取一个较小的
                dp[i][1]=Math.min(dp[i-1][1],dp[i-1][0]);
                dp[i][0]=dp[i-1][0]+1;
            }
        }
          return Math.min(dp[s.length()-1][1], dp[s.length()-1][0]);
    }
}
剑指 Offer II 073. 狒狒吃香蕉
剑指 Offer II 073. 狒狒吃香蕉
难度中等36收藏分享切换为英文接收动态反馈
狒狒喜欢吃香蕉。这里有
n堆香蕉,第i堆中有piles[i]根香蕉。警卫已经离开了,将在h小时后回来。狒狒可以决定她吃香蕉的速度
k(单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉k根。如果这堆香蕉少于k根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉,下一个小时才会开始吃另一堆的香蕉。狒狒喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。
返回她可以在
h小时内吃掉所有香蕉的最小速度k(k为整数)。示例 1:
输入:piles = [3,6,7,11], h = 8 输出:4示例 2:
输入:piles = [30,11,23,4,20], h = 5 输出:30示例 3:
输入:piles = [30,11,23,4,20], h = 6 输出:23提示:
1 <= piles.length <= 104piles.length <= h <= 1091 <= piles[i] <= 109
解答
class Solution {
    public int minEatingSpeed(int[] piles, int h) {
        int left=1;
        int max=-1;
        for (int pile : piles) {
            max=Math.max(max,pile);
        }
        int right=max;
        while (left<right){
            int mid=left+(right-left)/2;
            if(calTime(piles,mid)>h){
                left=mid+1;
            }else{
                right=mid;
            }
        }
        return left;
    }
    public static int calTime(int []piles,int mid){
        int res=0;
        for (int i = 0; i <piles.length ; i++) {
            if(piles[i]<=mid){
                res++;
            }else{
                res=res+(piles[i]/mid);
                int temp=piles[i]%mid;
                if(temp!=0){
                    res++;
                }
            }
        }
        return res;
    }
}
剑指 Offer II 066. 单词之和
剑指 Offer II 066. 单词之和
难度中等21收藏分享切换为英文接收动态反馈
实现一个
MapSum类,支持两个方法,insert和sum:
MapSum()初始化MapSum对象void insert(String key, int val)插入key-val键值对,字符串表示键key,整数表示值val。如果键key已经存在,那么原来的键值对将被替代成新的键值对。int sum(string prefix)返回所有以该前缀prefix开头的键key的值的总和。示例:
输入: inputs = ["MapSum", "insert", "sum", "insert", "sum"] inputs = [[], ["apple", 3], ["ap"], ["app", 2], ["ap"]] 输出: [null, null, 3, null, 5] 解释: MapSum mapSum = new MapSum(); mapSum.insert("apple", 3); mapSum.sum("ap"); // return 3 (apple = 3) mapSum.insert("app", 2); mapSum.sum("ap"); // return 5 (apple + app = 3 + 2 = 5)提示:
1 <= key.length, prefix.length <= 50key和prefix仅由小写英文字母组成1 <= val <= 1000- 最多调用
 50次insert和sum
解答
class MapSum {
    private Map map;
    /** Initialize your data structure here. */
    public MapSum() {
       this.map=new HashMap<>();
    }
    public void insert(String key, int val) {
        map.put(key,val);
    }
    public int sum(String prefix) {
        Set keySet = map.keySet();
        int len=prefix.length();
        int res=0;
        Iterator iterator = keySet.iterator();
        while (iterator.hasNext()){
            String  next = (String) iterator.next();
             if(next.length()<prefix.length()){
                continue;
            }
            String substring = next.substring(0, len);
            if(substring.equals(prefix)) {
                res = res + (int) map.get(next);
            }
        }
        return  res;
    }
}
/**
 * Your MapSum object will be instantiated and called as such:
 * MapSum obj = new MapSum();
 * obj.insert(key,val);
 * int param_2 = obj.sum(prefix);
 */
347 前k个高频元素
剑指 Offer II 060. 出现频率最高的 k 个数字(https://leetcode.cn/problems/g5c51o/)
347. 前 K 个高频元素
难度中等1291收藏分享切换为英文接收动态反馈
给你一个整数数组
nums和一个整数k,请你返回其中出现频率前k高的元素。你可以按 任意顺序 返回答案。示例 1:
输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2]示例 2:
输入: nums = [1], k = 1 输出: [1]提示:
1 <= nums.length <= 105k的取值范围是[1, 数组中不相同的元素的个数]- 题目数据保证答案唯一,换句话说,数组中前
 k个高频元素的集合是唯一的
解答
//小根堆
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        int []res=new int[k];
        HashMap<Integer,Integer> map=new HashMap<>();
        for (int i = 0; i <nums.length ; i++) {
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }
        Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
        PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>(new Comparator<Map.Entry<Integer, Integer>>() {
            @Override
            public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                return o2.getValue()-o1.getValue();
            }
        });
        for (Map.Entry<Integer, Integer> entry : entries) {
            queue.offer(entry);
        }
        for (int i = 0; i < k ; i++) {
            res[i]=queue.poll().getKey();
        }
        return res;
    }
}
剑指 Offer II 063. 替换单词
剑指 Offer II 063. 替换单词
难度中等25收藏分享切换为英文接收动态反馈
在英语中,有一个叫做
词根(root)的概念,它可以跟着其他一些词组成另一个较长的单词——我们称这个词为继承词(successor)。例如,词根an,跟随着单词other(其他),可以形成新的单词another(另一个)。现在,给定一个由许多词根组成的词典和一个句子,需要将句子中的所有
继承词用词根替换掉。如果继承词有许多可以形成它的词根,则用最短的词根替换它。需要输出替换之后的句子。
示例 1:
输入:dictionary = ["cat","bat","rat"], sentence = "the cattle was rattled by the battery" 输出:"the cat was rat by the bat"示例 2:
输入:dictionary = ["a","b","c"], sentence = "aadsfasf absbs bbab cadsfafs" 输出:"a a b c"示例 3:
输入:dictionary = ["a", "aa", "aaa", "aaaa"], sentence = "a aa a aaaa aaa aaa aaa aaaaaa bbb baba ababa" 输出:"a a a a a a a a bbb baba a"示例 4:
输入:dictionary = ["catt","cat","bat","rat"], sentence = "the cattle was rattled by the battery" 输出:"the cat was rat by the bat"示例 5:
输入:dictionary = ["ac","ab"], sentence = "it is abnormal that this solution is accepted" 输出:"it is ab that this solution is ac"
解答
//可以使用前缀树
class Solution {
    public String replaceWords(List<String> dictionary, String sentence) {
        String[] strings = sentence.split(" ");
        StringBuilder builder=new StringBuilder();
        dictionary.sort(new Comparator<String>() {
            public int compare(String o1, String o2) {
                return o1.length()-o2.length();
            }
        });
        for (int i = 0; i < strings.length; i++) {
            for (String s : dictionary) {
                if(strings[i].startsWith(s)){
                    strings[i]=s;
                }
            }
           if(i<strings.length-1) {
                builder.append(strings[i] + " ");
            }else{
                builder.append(strings[i]);
            }
        }
        return builder.toString();
    }
}
剑指 Offer II 062. 实现前缀树
剑指 Offer II 062. 实现前缀树
难度中等29收藏分享切换为英文接收动态反馈
**Trie**(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie()初始化前缀树对象。void insert(String word)向前缀树中插入字符串word。boolean search(String word)如果字符串word在前缀树中,返回true(即,在检索之前已经插入);否则,返回false。boolean startsWith(String prefix)如果之前已经插入的字符串word的前缀之一为prefix,返回true;否则,返回false。示例:
输入 inputs = ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] inputs = [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] 输出 [null, null, true, false, true, null, true] 解释 Trie trie = new Trie(); trie.insert("apple"); trie.search("apple"); // 返回 True trie.search("app"); // 返回 False trie.startsWith("app"); // 返回 True trie.insert("app"); trie.search("app"); // 返回 True

解答
class  TrieNode{
    boolean isEnd;
    TrieNode[] next;
    public TrieNode() {
        next = new TrieNode[26];
    }
}
class Trie {
    TrieNode root;
    /** Initialize your data structure here. */
    public Trie() {
        root=new TrieNode();
    }
    /** Inserts a word into the trie. */
    public void insert(String word) {
        TrieNode cur=root;
        
        for(char ch:word.toCharArray()){
            if(cur.next[ch-'a']==null){
                cur.next[ch-'a']=new TrieNode();
            }
           cur=cur.next[ch-'a'];
        }
        cur.isEnd=true;
    }
    /** Returns if the word is in the trie. */
    public boolean search(String word) {
        TrieNode cur=root;
        for(char ch:word.toCharArray()){
            if(cur.next[ch-'a']==null){
                return false;
            }
            cur=cur.next[ch-'a'];
        }
        return cur.isEnd;
    }
    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
        TrieNode cur=root;
        for(char ch:prefix.toCharArray()){
            if(cur.next[ch-'a']==null){
                return false;
            }
            cur=cur.next[ch-'a'];
        }
        return true;
    }
}
/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */
剑指offer II 068. 查找插入位置
剑指 Offer II 068. 查找插入位置
难度简单31收藏分享切换为英文接收动态反馈
给定一个排序的整数数组
nums和一个整数目标值target,请在数组中找到target,并返回其下标。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。请必须使用时间复杂度为
O(log n)的算法。示例 1:
输入: nums = [1,3,5,6], target = 5 输出: 2示例 2:
输入: nums = [1,3,5,6], target = 2 输出: 1示例 3:
输入: nums = [1,3,5,6], target = 7 输出: 4示例 4:
输入: nums = [1,3,5,6], target = 0 输出: 0示例 5:
输入: nums = [1], target = 0 输出: 0
解答
//二分
class Solution {
    public int searchInsert(int[] nums, int target) {
          int left=0;
        int right=nums.length-1;
        while(left<=right){
            int mid=left+(right-left)/2;
            if(nums[mid]==target){
                return mid;
            }else if(nums[mid]>target){
                right=mid-1;
            }else{
                left=mid+1;
            }
        }
        return left;
    }
}
剑指 Offer II 070. 排序数组中只出现一次的数字
剑指 Offer II 070. 排序数组中只出现一次的数字
难度中等40
给定一个只包含整数的有序数组
nums,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。你设计的解决方案必须满足
O(log n)时间复杂度和O(1)空间复杂度。示例 1:
输入: nums = [1,1,2,3,3,4,4,8,8] 输出: 2示例 2:
输入: nums = [3,3,7,7,10,11,11] 输出: 10
解答
package SwordFingerOffer;
/**
 * 剑指 Offer II 070. 排序数组中只出现一次的数字
 * 给定一个只包含整数的有序数组 nums ,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。
 *
 * 你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。
 * 示例 1:
 *
 * 输入: nums = [1,1,2,3,3,4,4,8,8]
 * 输出: 2
 * 示例 2:
 *
 * 输入: nums =  [3,3,7,7,10,11,11]
 * 输出: 10
 */
public class leetcode21 {
    public static void main(String[] args) {
        int []nums=new int[]{1,1,2,3,3,4,4,8,8};
        System.out.println(singleNonDuplicate(nums));
    }
      //异或
    public static int singleNonDuplicate(int[] nums) {
        int res=0;
        for (int i = 0; i < nums.length; i++) {
            res=res ^ nums[i];
        }
        return res;
    }
}
剑指 Offer II 035. 最小时间差
剑指 Offer II 035. 最小时间差
难度中等25收藏分享切换为英文接收动态反馈
给定一个 24 小时制(小时:分钟 **”HH:MM”**)的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示。
示例 1:
输入:timePoints = ["23:59","00:00"] 输出:1示例 2:
输入:timePoints = ["00:00","23:59","00:00"] 输出:0提示:
2 <= timePoints <= 2 * 104timePoints[i]格式为 “HH:MM
解答
 public  int findMinDifference(List<String> timePoints) {
        int []times=new int[timePoints.size()+1];
        int h=0;
        int minutes=0;
        int res=Integer.MAX_VALUE;
        for (int i=0;i<timePoints.size();i++) {
            if(timePoints.get(i).equals("00:00")){
                times[i]=24*60;
                continue;
            }
            String hour = timePoints.get(i).substring(0, 2);
            String min =  timePoints.get(i).substring(3, 5);
            h=Integer.valueOf(hour);
            minutes=Integer.valueOf(min);
            times[i]=h*60+minutes;
        }
        //数组最后添加一个元素  用于判断最小值 与最大值之间差值  比如00:35  和23:59
        times[timePoints.size()]=Integer.MAX_VALUE;
        Arrays.sort(times);
        times[timePoints.size()]=times[0]+24*60;
        for (int i = 1; i < times.length ; i++) {
            res=Math.min(res,times[i]-times[i-1]);
        }
        return res;
    }
剑指 Offer II 103. 最少的硬币数目
剑指 Offer II 103. 最少的硬币数目
难度中等46收藏分享切换为英文接收动态反馈
给定不同面额的硬币
coins和一个总金额amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回-1。你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11 输出:3 解释:11 = 5 + 5 + 1示例 2:
输入:coins = [2], amount = 3 输出:-1示例 3:
输入:coins = [1], amount = 0 输出:0示例 4:
输入:coins = [1], amount = 1 输出:1示例 5:
输入:coins = [1], amount = 2 输出:2
解答
class Solution {
    public int coinChange(int[] coins, int amount) {
        int []dp=new int[amount+1];
        int max=amount+1;
        for (int i = 0; i < dp.length; i++) {
            dp[i]=max;
        }
        dp[0]=0;
        for (int i = 1; i <=amount ; i++) {
            for (int j = 0; j <coins.length ; j++) {
                if(coins[j]<=i ){
                    dp[i]=Math.min(dp[i],dp[i-coins[j]]+1);
                }
            }
        }
        return dp[amount] >amount ? -1 : dp[amount];
    }
}
剑指 Offer II 025. 链表中的两数相加
剑指 Offer II 025. 链表中的两数相加
难度中等62收藏分享切换为英文接收动态反馈
给定两个 非空链表
l1和l2来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。可以假设除了数字 0 之外,这两个数字都不会以零开头。
示例1:
输入:l1 = [7,2,4,3], l2 = [5,6,4] 输出:[7,8,0,7]示例2:
输入:l1 = [2,4,3], l2 = [5,6,4] 输出:[8,0,7]示例3:
输入:l1 = [0], l2 = [0] 输出:[0]
解答
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        Deque<Integer> s1=new ArrayDeque<>();
        Deque<Integer> s2=new ArrayDeque<>();
        ListNode n1=l1;
        ListNode n2=l2;
        ListNode res=null;
        while(n1!=null){
            s1.push(n1.val);
            n1=n1.next;
        }
        while(n2!=null){
            s2.push(n2.val);
            n2=n2.next;
        }
        int carry=0;
        while(!s1.isEmpty()||!s2.isEmpty()||carry!=0){
            int num1=s1.isEmpty()?0:s1.pop();
            int num2=s2.isEmpty()?0:s2.pop();
            int cur=num1+num2+carry;
            carry=cur/10;
            cur=cur%10;
            ListNode tmp=new ListNode(cur);
            tmp.next=res;
            res=tmp;
        }
        return res;
    }
}
剑指 Offer 48. 最长不含重复字符的子字符串
剑指 Offer 48. 最长不含重复字符的子字符串
难度中等490
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。示例 2:
输入: "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。示例 3:
输入: "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
解答
//双指针
class Solution {
    public int lengthOfLongestSubstring(String s) {
     Map<Character,Integer> map=new HashMap<>();
        int i=-1;   //左指针
        int res=0;
        for (int j = 0; j < s.length(); j++) {
            char c=s.charAt(j);
            //如果包含重复元素 就移动左指针位置  左指针位置为 Math.max(i,map.get(c))
            if(map.containsKey(c)){
                i=Math.max(i,map.get(c));
            }
            //更新数据及索引
            map.put(s.charAt(j),j);
           //得出结果
            res=Math.max(res,j-i);
        }
        return res;
    }
}
解答2 滑动窗口
public  int lengthOfLongestSubstring(String s) {
    Set<Character> set=new HashSet<>();
    //记录结果
    int res=0;
    for (int l = 0,r=0; r < s.length();r++) {
        char c = s.charAt(r);
        //如果set集合包含重复的  就让l++  如果l++后 还是包含 就继续remove  比如 p w  时遇到w  首先移除p  然后w依然包含  继续remove w
        while (set.contains(c)){
            set.remove(s.charAt(l++));
        }
        set.add(c);
        res=Math.max(res,r-l+1);
    }
    return res;
}
剑指 Offer 57 - II. 和为s的连续正数序列
剑指 Offer 57 - II. 和为s的连续正数序列
难度简单473
输入一个正整数
target,输出所有和为target的连续正整数序列(至少含有两个数)。序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9 输出:[[2,3,4],[4,5]]示例 2:
输入:target = 15 输出:[[1,2,3,4,5],[4,5,6],[7,8]]
解答
class Solution {
    public int[][] findContinuousSequence(int target) {
         List<int []> res=new ArrayList<>();
        int left=1;
        int right=2;
        while(left < right){
            int temp=(right-left+1)*(left+right)/2;
            if(temp==target){
                int []arr=new int[right-left+1];
                for (int i = 0; i <right-left+1 ; i++) {
                    arr[i]=left+i;
                }
                res.add(arr);
                left++;
            }else if(target> temp){
                right++;
            }else{
                left++;
            }
        }
        return res.toArray(new int[res.size()][]);
    }
}
剑指 Offer 52. 两个链表的第一个公共节点
剑指 Offer 52. 两个链表的第一个公共节点
难度简单546
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在节点 c1 开始相交。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Reference of the node with value = 8 输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。示例 2:
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 输出:Reference of the node with value = 2 输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 输出:null 输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。 解释:这两个链表不相交,因此返回 null。
解答
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
class Solution {
    ListNode getIntersectionNode(ListNode headA, ListNode headB) {
          Set<ListNode> set=new HashSet<>();
        while(headA!=null){
            set.add(headA);
            headA=headA.next;
        }
        while(headB!=null){
            if(set.contains(headB)){
                return headB;
            }
            headB=headB.next;
        }
        return null;
        
    }
}
剑指 Offer II 020. 回文子字符串的个数
剑指 Offer II 020. 回文子字符串的个数
难度中等64
给定一个字符串
s,请计算这个字符串中有多少个回文子字符串。具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:s = "abc" 输出:3 解释:三个回文子串: "a", "b", "c"示例 2:
输入:s = "aaa" 输出:6 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
解答
class Solution {
    public int countSubstrings(String s) {
        if(s.length()==1){
            return 1;
        }
      //dp[i][j] 表示从i到j 构成的子串是否回文
        boolean [][]dp=new boolean[s.length()][s.length()];
        for (int i = s.length()-1; i >=0 ; i--) {
            for (int j = i; j < s.length() ; j++) {
                if(s.charAt(i)==s.charAt(j)){
                    if(j-i<3){
                        dp[i][j]=true;
                    }else{
                        dp[i][j]=dp[i+1][j-1];
                    }
                }else{
                    dp[i][j]=false;
                }
            }
        }
        int res=0;
        for (int i = 0; i < dp.length; i++) {
            for (int j = 0; j < dp[0].length ;j++) {
                if(dp[i][j]){
                    res++;
                }
            }
        }
        return res;
    }
}
剑指 Offer II 007. 数组中和为 0 的三个数
剑指 Offer II 007. 数组中和为 0 的三个数
难度中等78收藏分享切换为英文接收动态反馈
给定一个包含
n个整数的数组nums,判断nums中是否存在三个元素a,b,c,使得a + b + c = 0?请找出所有和为0且 不重复 的三元组。示例 1:
输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]]示例 2:
输入:nums = [] 输出:[]示例 3:
输入:nums = [0] 输出:[]
解答
//回溯  超时了
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res=new ArrayList<>();
        if(nums.length<3){
            return res;
        }
        Arrays.sort(nums);
        List<Integer> path=new ArrayList<>();
        dfs(res,path,0,nums);
        return res;
    }
      
    private static void dfs(List<List<Integer>> res,List<Integer> path, int index, int[] nums) {
        if(path.size()==3){
            if(getSum(path)==0) {
                res.add(new ArrayList<>(path));
                return;
            }else{
                return;
            }
        }
        for (int i = index; i < nums.length ; i++) {
            if(i>index && nums[i]==nums[i-1]){
                continue;
            }
            path.add(nums[i]);
            dfs(res,path,i+1,nums);
            path.remove(path.size()-1);
        }
    }
    public static  int getSum(List<Integer> path){
        int res=0;
        for (Integer integer : path) {
            res+=integer;
        }
        return res;
    }
}
解答2 排序加双指针
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res=new ArrayList<>();
        if(nums.length<3){
            return res;
        }
        for (int i = 0; i <nums.length; i++) {
            if(i>=1 &&  nums[i]==nums[i-1]){
                continue;
            }
            int target=-nums[i];
            int third=nums.length-1;
            for (int j = i+1; j <nums.length ; j++) {
                if(j > i+1 && nums[j]==nums[j-1]){
                    continue;
                }
                while(j<third && nums[j]+nums[third] >target){
                    --third;
                }
                if(third==j){
                    break;
                }
                if(nums[j]+nums[third]==target){
                    List<Integer> path=new ArrayList<>();
                    path.add(nums[i]);
                    path.add(nums[j]);
                    path.add(nums[third]);
                    res.add(path);
                }
            }
        }
        return res;
    }
}
剑指 Offer 63. 股票的最大利润
剑指 Offer 63. 股票的最大利润
难度中等282
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
示例 1:
输入: [7,1,5,3,6,4] 输出: 5 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。示例 2:
输入: [7,6,4,3,1] 输出: 0 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
解答
class Solution {
    public int maxProfit(int[] prices) {
       if(prices.length==1){
            return 0;
        }
        int res=0;
        int lowPrice=Integer.MAX_VALUE;
        for (int i = 1; i <prices.length ; i++) {
            lowPrice=Math.min(lowPrice,prices[i-1]);
            res=Math.max(res,prices[i]-lowPrice);
        }
        return res;
    
    }
}
剑指Offer 61.扑克牌中的顺子
剑指 Offer 61. 扑克牌中的顺子
难度简单269
从若干副扑克牌中随机抽
5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。示例 1:
输入: [1,2,3,4,5] 输出: True示例 2:
输入: [0,0,1,2,5] 输出: True
解答
class Solution {
    public boolean isStraight(int[] nums) {
       int ZeroNumber=0;  //记录大小王数目
        Arrays.sort(nums);
        for (int i = 0; i < 4; i++) {
            if(nums[i]==0){
                ZeroNumber++;
            }else  if(nums[i]==nums[i+1]){
                return  false;
            }
        }
        return nums[4]-nums[ZeroNumber]<5;
    }
}
剑指 Offer II 011. 0 和 1 个数相同的子数组
剑指 Offer II 011. 0 和 1 个数相同的子数组
难度中等94
给定一个二进制数组
nums, 找到含有相同数量的0和1的最长连续子数组,并返回该子数组的长度。示例 1:
输入: nums = [0,1] 输出: 2 说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。示例 2:
输入: nums = [0,1,0] 输出: 2 说明: [0, 1] (或 [1, 0]) 是具有相同数量 0 和 1 的最长连续子数组。
解答
class Solution {
    public int findMaxLength(int[] nums) {
          for (int i = 0; i < nums.length; i++) {
            if(nums[i]==0){
                nums[i]=-1;
            }
        }
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0,-1);
        int maxLen=0,sum=0;
        for (int i = 0; i < nums.length; i++) {
            sum+=nums[i];
            if(map.containsKey(sum)){
                maxLen=Math.max(maxLen,i-map.get(sum));
            }else{
                map.put(sum,i);
            }
        }
        return maxLen;
    }
}
剑指 Offer II 014. 字符串中的变位词
剑指 Offer II 014. 字符串中的变位词
难度中等61
给定两个字符串
s1和s2,写一个函数来判断s2是否包含s1的某个变位词。换句话说,第一个字符串的排列之一是第二个字符串的 子串 。
示例 1:
输入: s1 = "ab" s2 = "eidbaooo" 输出: True 解释: s2 包含 s1 的排列之一 ("ba").示例 2:
输入: s1= "ab" s2 = "eidboaoo" 输出: False
解答
class Solution {
    public boolean checkInclusion(String s1, String s2) {
        for (int i = 0; i <=s2.length()-s1.length() ; i++) {
            String substring = s2.substring(i, i + s1.length());
            if(isSame(substring,s1)){
                return true;
            }
        }
        return false;
    }
     private static boolean isSame(String substring,String s1) {
        int []count=new int[26];
        for (int i = 0; i <s1.length() ; i++) {
            count[s1.charAt(i)-'a']++;
        }
        for (int i = 0; i <substring.length() ; i++) {
            count[substring.charAt(i)-'a']--;
        }
        for (int i = 0; i < count.length; i++) {
            if(count[i]!=0){
                return false;
            }
        }
        return true;
    }
}
                
            





