阿里面试算法题合集二

作者: Tim在路上 | 来源:发表于2020-06-26 13:29 被阅读0次

    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

    注意这里求的是机器人走的范围,而不是路径,所以每走一个新的格都要加1

     boolean[][] vis; 
        int m , n , k;
        int max = 1;
        public int movingCount(int m, int n, int k) {
            vis = new boolean[m][n];
            this.m = m;
            this.n = n;
            this.k = k;
            dfs(0,0);
            return max;
        }
    
        int[][] next = {{0,1},{0,-1},{1,0},{-1,0}};
        public void dfs(int x, int y){
            vis[x][y] = true;
            for(int i=0;i<4;i++){
                int xx = x + next[i][0];
                int yy = y + next[i][1];
                int x_1 = xx / 10;
                int x_0 = xx % 10;
                int y_1 = yy / 10;
                int y_0 = yy % 10;
                if(xx < 0 || xx >=m || yy < 0 || yy >= n || 
                x_0 + x_1 + y_0 + y_1 > k){
                    continue;
                }
                if(!vis[xx][yy]){
                    dfs(xx,yy);
                    max++;
                }
            }
        }
    

    从(0,0) 统计所有访问的点,可以同时进行dfs两个方向,避免已经访问过的点,从上向下进行dfs

    boolean[][] vis; 
        int m , n , k;
        // 这道题可以简化为,调用dfs,同时想两个方向进行dfs i+1, j+1
        public int movingCount(int m, int n, int k) {
            vis = new boolean[m][n];
            this.m = m;
            this.n = n;
            this.k = k;
            return dfs(0,0,0,0);
        }
    
        public int dfs(int x, int y, int s1, int s2){
            if(x >=m || y >= n || s1 + s2 > k || vis[x][y]){
                return 0;
            }
            vis[x][y] = true;
            return 1 + dfs(x+1,y, (x+1) % 10 + (x + 1)/10, s2) + dfs(x,y+1,s1, (y+1)/10 + (y+1)%10);
        }
    

    79.单词搜索

    给定一个二维网格和一个单词,找出该单词是否存在于网格中。

    单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

    示例:

    board =
    [
    ['A','B','C','E'],
    ['S','F','C','S'],
    ['A','D','E','E']
    ]

    给定 word = "ABCCED", 返回 true

     // 采用回溯,
        boolean[][] vis;
        int m,n;
        char[][] board;
        public boolean exist(char[][] board, String word) {
            this.m = board.length;
            this.n = board[0].length;
            this.board = board;
            for(int i=0;i<m;i++){
                for(int j=0;j<n;j++){
                    if(board[i][j] == word.charAt(0)){
                        vis = new boolean[m][n];
                        vis[i][j] = true;
                        boolean flag = dfs(word,i,j,1);
                        if(flag){
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    
        int[][] next = {{0,1},{0,-1},{-1,0},{1,0}};
        public boolean dfs(String word,int x, int y, int k){
            if(k >= word.length()){
                return true;
            }
            for(int i=0;i<4;i++){
                int xx = x + next[i][0];
                int yy = y + next[i][1];
                if(xx < 0 || xx >= m || yy < 0 || yy >= n || vis[xx][yy] || word.charAt(k)
            != board[xx][yy]){
                    continue;
                }
                vis[xx][yy] = true;
                boolean flag = dfs(word,xx,yy,k+1);
                if(flag){
                    return true;
                }
                //这里必须回溯,实质上还是走路径的问题
                vis[xx][yy] = false;
            }
            return false;
        }
    

    38. 分割回文串

    给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

    返回 s 所有可能的分割方案。

    示例:

    输入: "aab"
    输出:
    [
    ["aa","b"],
    ["a","a","b"]
    ]

     // 采用回溯, s.substring(i) s.substring(i,len), 如果分割的两个正好都是回文添加结果集,否则递归
        public List<List<String>> partition(String s) {
            List<List<String>> res = new ArrayList<>();
            if(s == null || s.length() == 0){
                return res;
            }
            backTrack(0,s,new LinkedList<String>(),res);
            return res;
        }
    
        public void backTrack(int j,String s,LinkedList<String> list,List<List<String>>  res){
            
            if(j == s.length()){
                res.add(new ArrayList<>(list));
                return;
            }
            for(int i=j;i<s.length();i++){
                String first = s.substring(j,i+1);
                if(check(first)){
                    list.offer(first);
                    backTrack(i+1,s,list,res);
                    list.removeLast();
                }
            }
        }
    
        public boolean check(String str){
            int i,j;
            for(i=0,j=str.length()-1;i<j;i++,j--){
                if(str.charAt(i)!=str.charAt(j)){
                    return false;
                }
            }
            return true;
        }
    

    47. 全排列2

    给定一个可包含重复数字的序列,返回所有不重复的全排列。

    示例:

    输入: [1,1,2]
    输出:
    [
    [1,1,2],
    [1,2,1],
    [2,1,1]
    ]

    数组中有相同的数,可以使用canSwap 进行判断是否相同

    List<List<Integer>> res = new ArrayList<>();
        public List<List<Integer>> permuteUnique(int[] nums) {
            if(nums == null || nums.length == 0){
                return res;
            }
            permutition(nums,0);
            return res;
        }
    
        public void permutition(int[] nums, int cur){
            if(cur == nums.length){
                List<Integer> list = new ArrayList<>();
                for(int i=0;i<nums.length;i++){
                    list.add(nums[i]);
                }
                res.add(list);
                return;
            }else{
                for(int i=cur;i<nums.length;i++){
                    if(canSwap(nums,cur,i)){
                         swap(nums,cur,i);
                         permutition(nums,cur+1);
                         swap(nums,cur,i);
                    }
                }
            }
        }
    
        public boolean canSwap(int[] nums, int start, int end){
            for(int i=start;i < end;i++){
                if(nums[i] == nums[end]){
                    return false;
                }
            }
            return true;
        }
    
        public void swap(int[] nums, int i, int j){
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }
    

    46. 全排列

    给定一个 没有重复 数字的序列,返回其所有可能的全排列。

    示例:

    输入: [1,2,3]
    输出:
    [
    [1,2,3],
    [1,3,2],
    [2,1,3],
    [2,3,1],
    [3,1,2],
    [3,2,1]
    ]

    // 没有重复数的全排列
        List<List<Integer>> res = new ArrayList<>();
        public List<List<Integer>> permute(int[] nums) {
            if(nums == null || nums.length == 0){
                return res;
            }
            permutition(nums,0);
            return res;
        }
    
        public void permutition(int[] nums, int cur){
            if(cur == nums.length){
                List<Integer> list = new ArrayList<>();
                for(int x: nums){
                    list.add(x);
                }
                res.add(list);
                return;
            }else{
                for(int i=cur;i<nums.length;i++){
                    swap(nums,cur,i);
                    permutition(nums,cur+1);
                    swap(nums,cur,i);
                }
            }
        }  
    
        public void swap(int[] nums , int i , int j){
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }
    

    39. 组合总和

    给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

    candidates 中的数字可以无限制重复被选取。

    说明:

    所有数字(包括 target)都是正整数。
    解集不能包含重复的组合。
    示例 1:

    输入: candidates = [2,3,6,7], target = 7,
    所求解集为:
    [
    [7],
    [2,2,3]
    ]

     List<List<Integer>> res = new ArrayList<>();
        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            if(candidates == null || candidates.length == 0){
                return res;
            }
            Arrays.sort(candidates);
            combination(candidates,target,0,new Stack<Integer>());
            return res;
        }
    
        public void combination(int[] num,int target, int cur, Stack<Integer> stack){
            if(target == 0){
                res.add(new ArrayList<>(stack));
                return;
            }
            for(int i=cur;i<num.length && target - num[i] >= 0;i++){
                stack.add(num[i]);
                // 这里传i, 数组里面的数可以重复使用
                combination(num,target-num[i],i,stack);
                stack.pop();
            }
        }
    

    40. 组合总和2

    给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

    candidates 中的每个数字在每个组合中只能使用一次。

    说明:

    所有数字(包括目标数)都是正整数。
    解集不能包含重复的组合。
    示例 1:

    输入: candidates = [10,1,2,7,6,1,5], target = 8,
    所求解集为:
    [
    [1, 7],
    [1, 2, 5],
    [2, 6],
    [1, 1, 6]
    ]

    // 数组中有重复的数,和排列一样进行筛选
        List<List<Integer>> res = new ArrayList<>();
        public List<List<Integer>> combinationSum2(int[] candidates, int target) {
            if(candidates == null || candidates.length == 0){
                return res;
            }
            Arrays.sort(candidates);
            combination(0,candidates,target,new LinkedList<Integer>());
            return res;
        }
    
        public void combination(int cur, int[] candidates, int target, LinkedList<Integer> list ){
            if(target == 0){
                res.add(new ArrayList<>(list));
                return;
            }
    
            for(int i=cur;i<candidates.length && target - candidates[i] >=0 ; i++){
                if(check(candidates,cur,i)){
                    list.add(candidates[i]);
                    // 这里 i + 1 是数组里面的数不可以重复使用 combination(i+1,candidates,target-candidates[i],list);
                    list.removeLast();
                }
            }
    
        }
    
        public boolean check(int[] nums, int cur, int end){
            for(int i=cur;i<end;i++){
                if(nums[i] == nums[end]){
                    return false;
                }
            }
            return true;
        }
    

    90. 子集2

    给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

    说明:解集不能包含重复的子集。

    示例:

    输入: [1,2,2]
    输出:
    [
    [2],
    [1],
    [1,2,2],
    [2,2],
    [1,2],
    []
    ]

     Set<List<Integer>> set = new HashSet<>();
        public List<List<Integer>> subsetsWithDup(int[] nums) {
            List<List<Integer>> res = new ArrayList<>();
            if(nums == null || nums.length == 0){
                return res;
            }
            Arrays.sort(nums);
            int len = nums.length;
            int max = 1 << len;
            for(int i=0;i<max;i++){
                List<Integer> list = new ArrayList<>();
                for(int j=0;j<nums.length;j++){
                    if(((i >> j) & 1) == 1){
                        list.add(nums[j]);
                    }
                }
                set.add(list);
            }
            for(List<Integer> l : set){
                res.add(l);
            }
            return res;
        }
    

    /// 子集

     List<List<Integer>> res = new ArrayList<>();
        public List<List<Integer>> subsetsWithDup(int[] nums) {
            if(nums == null || nums.length == 0){
                return res;
            }
            Arrays.sort(nums);
            subsets(0,nums,new LinkedList<Integer>());
            return res;
        }
    
        public void subsets(int cur, int[] nums, LinkedList<Integer> list){
            res.add(new ArrayList<>(list));
            for(int i=cur;i<nums.length;i++){
                if(i > cur && nums[i] == nums[i-1]){
                    continue;
                }
                list.add(nums[i]);
                subsets(i+1,nums,list);
                list.removeLast();
            }
        }
    

    78. 子集

    给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

    说明:解集不能包含重复的子集。

    示例:

    输入: nums = [1,2,3]
    输出:
    [
    [3],
    [1],
    [2],
    [1,2,3],
    [1,3],
    [2,3],
    [1,2],
    []
    ]

     public List<List<Integer>> subsets(int[] nums) {
            List<List<Integer>> res = new ArrayList<>();
            if(nums == null || nums.length == 0){
                return res;
            }
            int len = nums.length;
            int max = 1 << len;
            for(int i=0;i<max;i++){
                List<Integer> list = new ArrayList<>();
                for(int j=0;j<nums.length;j++){
                    if(((i >> j) & 1) == 1){
                        list.add(nums[j]);
                    }
                }
                res.add(list);
            }
            return res;
        }
    

    子集就是组合

     List<List<Integer>> res = new ArrayList<>();
        public List<List<Integer>> subsets(int[] nums) {
            if(nums == null || nums.length == 0){
                return res;
            }
            subsets(0,nums,new LinkedList<Integer>());
            return res;
        }
    
        public void subsets(int cur, int[] nums, LinkedList<Integer> list){
            res.add(new ArrayList<>(list));
            for(int i = cur;i<nums.length;i++){
                list.add(nums[i]);
                subsets(i+1,nums,list);
                list.removeLast();
            }
        }
    

    494. 目标和

    给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

    返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

    示例:

    输入:nums: [1, 1, 1, 1, 1], S: 3
    输出:5

    int count = 0;
        public int findTargetSumWays(int[] nums, int S) {
            findTarget(0,nums,S,0);
            return count;
        }
    
        public void findTarget(int cur, int[] nums, int S,int sum){
            if(cur == nums.length){
                if(sum == S)
                count++;
                return;
            }
            findTarget(cur+1,nums,S, sum-nums[cur]);
            findTarget(cur+1,nums,S, sum+nums[cur]);
        }
    

    相关文章

      网友评论

        本文标题:阿里面试算法题合集二

        本文链接:https://www.haomeiwen.com/subject/mntufktx.html