zoukankan      html  css  js  c++  java
  • 【LeetCode】 31

    31. 下一个排列

    实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

    如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

    必须原地修改,只允许使用额外常数空间。

    以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
    1,2,31,3,2
    3,2,11,2,3
    1,1,51,5,1

        public void nextPermutation(int[] nums) {
            int i = nums.length - 2;
            //找到第一个不再递增的位置
            while (i >= 0 && nums[i + 1] <= nums[i]) {
                i--;
            }
            //如果到了最左边,就直接倒置输出
            if (i < 0) {
                reverse(nums, 0);
                return;
            }
            //找到刚好大于 nums[i]的位置
            int j = nums.length - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            //交换
            swap(nums, i, j);
            //利用倒置进行排序
            reverse(nums, i + 1);
    
        }
    
        private void swap(int[] nums, int i, int j) {
            int temp = nums[j];
            nums[j] = nums[i];
            nums[i] = temp;
        }
    
        private void reverse(int[] nums, int start) {
            int i = start, j = nums.length - 1;
            while (i < j) {
                swap(nums, i, j);
                i++;
                j--;
            }
        }
    

    32. 最长有效括号

    给定一个只包含 '('')' 的字符串,找出最长的包含有效括号的子串的长度。

    示例 1:

    输入: "(()"
    输出: 2
    解释: 最长有效括号子串为 "()"
    

    示例 2:

    输入: ")()())"
    输出: 4
    解释: 最长有效括号子串为 "()()"
    
        //")()(" --> 1001
        public int longestValidParentheses(String s) {
            Stack<Integer> stack = new Stack<>();
            int n = s.length();
            char[] chs = s.toCharArray();
            int[] mark = new int[n];
            int left = 0, len = 0, ans = 0;
            for(int i = 0; i < n; i ++){
                if(chs[i] == '(') stack.push(i);
                else{
                    //没有匹配的左括号的右括号,置1
                    if(stack.isEmpty())mark[i] = 1;
                    //如果有匹配的左括号,弹出
                    else stack.pop();
                }
            }
            // System.out.println(Arrays.toString(mark));
            //多余的左括号,置1
            while(!stack.isEmpty()){
                mark[stack.pop()] = 1;
            }
            //找到最长连续0的长度
            for(int i = 0; i < n; i ++){
                if(mark[i] == 1){
                    len = 0;
                    continue;
                }
                len ++;
                ans = Math.max(ans , len);
            }
            return ans;
        }
    

    33. 搜索旋转排序数组

    假设按照升序排序的数组在预先未知的某个点上进行了旋转。

    ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

    搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1

    你可以假设数组中不存在重复的元素。

    你的算法时间复杂度必须是 O(log n) 级别。

    示例 1:

    输入: nums = [4,5,6,7,0,1,2], target = 0
    输出: 4
    

    示例 2:

    输入: nums = [4,5,6,7,0,1,2], target = 3
    输出: -1
    
        public int search(int[] nums, int target) {
            if(nums.length == 0) return -1;
            int l = 0, r = nums.length -1;
            //找到最小值的索引位置
            while(l < r){
                int mid = l + r >> 1;
                if( nums[mid] <= nums[nums.length -1 ]) 
                    r = mid;
                else 
                    l = mid + 1;
            }//此时l == r  == k
            if( target <= nums[nums.length -1]) 
                //[k,len-1]
                r = nums.length -1; 
            else {
                //左边那段中寻找[0,k-1]
                l = 0;
                r --; //因为上面二分完之后找到了第二段的第一个索引,因此r = r-1
            }
            while(l < r){
                int mid = l + r >> 1;
                if( nums[mid] >= target) r = mid;
                else l = mid + 1;
            }
            //二分得出的是第一个>=target的值,需要判断一下是否就是寻找的那个数
            if( nums[l]  ==  target) return l;
            return -1;
        }
    

    34. 在排序数组中查找元素的第一个和最后一个位置

    给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

    你的算法时间复杂度必须是 O(log n) 级别。

    如果数组中不存在目标值,返回 [-1, -1]

    示例 1:

    输入: nums = [5,7,7,8,8,10], target = 8
    输出: [3,4]
    

    示例 2:

    输入: nums = [5,7,7,8,8,10], target = 6
    输出: [-1,-1]
    
        public int[] searchRange(int[] nums, int t) {
            if(nums.length == 0) return new int[]{-1, -1};
            int l = 0, r = nums.length -1;
            //找第一个数
            while(l < r){
                int mid = l + r >>> 1;
                //假设mid位置为8,前面可能还有,r = mid
                if(nums[mid] >= t){
                    r = mid;
                }else{
                    l = mid + 1;
                }
            }
            //将会二分出大于等于t的第一个数如果不等于t 则不存在
            if(nums[r] != t) return new int[]{-1, -1};
            int start = r;
    
            l = 0;
            r = nums.length -1;
            while(l < r){
                //这里条件 l = mid, 注意这里向上取整
                int mid = l + r + 1 >>> 1;
                //找到第一个 <= target的数
                if( nums[mid] <= t){
                    l = mid;
                }else{
                    r = mid - 1;
                }
            }
            int end = r;
            return new int[]{start, end};
        } 
    

    35. 搜索插入位置

    给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

    你可以假设数组中无重复元素。

    示例 1:

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

    示例 2:

    输入: [1,3,5,6], 2
    输出: 1
    
        public int searchInsert(int[] nums, int target) {
            if(nums.length == 0 || nums[nums.length-1] < target) return nums.length;
            int l = 0, r = nums.length -1;
            //找到 >= target的第一个坐标
            while( l < r){
                int mid = l + r >> 1;
                if( nums[mid] >= target){
                    r = mid;
                }else{
                    l = mid + 1;
                }
            }
            return r;
        }
    

    36. 有效的数独

    判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

    1. 数字 1-9 在每一行只能出现一次。
    2. 数字 1-9 在每一列只能出现一次。
    3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

    img

    上图是一个部分填充的有效的数独。

    数独部分空格内已填入了数字,空白格用 '.' 表示。

    示例 2:

    输入:
    [
      ["8","3",".",".","7",".",".",".","."],
      ["6",".",".","1","9","5",".",".","."],
      [".","9","8",".",".",".",".","6","."],
      ["8",".",".",".","6",".",".",".","3"],
      ["4",".",".","8",".","3",".",".","1"],
      ["7",".",".",".","2",".",".",".","6"],
      [".","6",".",".",".",".","2","8","."],
      [".",".",".","4","1","9",".",".","5"],
      [".",".",".",".","8",".",".","7","9"]
    ]
    输出: false
    解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
         但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
    

    说明:

    • 一个有效的数独(部分已被填充)不一定是可解的。
    • 只需要根据以上规则,验证已经填入的数字是否有效即可。
    • 给定数独序列只包含数字 1-9 和字符 '.'
    • 给定数独永远是 9x9 形式的。
        public boolean isValidSudoku(char[][] board) {
            // 记录某行,某位数字是否已经被摆放
            boolean[][] row = new boolean[9][9];
            // 记录某列,某位数字是否已经被摆放
            boolean[][] col = new boolean[9][9];
            // 记录某 3x3 宫格内,某位数字是否已经被摆放
            boolean[][] block = new boolean[9][9];
            for(int i = 0; i < 9; i ++){
                for(int j = 0; j < 9; j ++){
                    //.的情况不用考虑
                    if(board[i][j] == '.') continue;
                    //数字为1-9
                    int num = board[i][j] - '1';
                    //映射到子数独中
                    int index = i / 3 * 3 + j / 3;
                    if(row[i][num] || col[j][num] || block[index][num]) 
                        return false;
                    else{
                        row[i][num] = true;
                        col[j][num] = true;
                        block[index][num] = true;
                    }
                }
            }
            return true;
        }
    

    37. 解数独

    编写一个程序,通过填充空格来解决数独问题。

    一个数独的解法需遵循如下规则

    1. 数字 1-9 在每一行只能出现一次。
    2. 数字 1-9 在每一列只能出现一次。
    3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

    空白格用 '.' 表示。

    img

    一个数独。

    img

    答案被标成红色。

    提示:

    • 给定的数独序列只包含数字 1-9 和字符 '.'
    • 你可以假设给定的数独只有唯一解。
    • 给定数独永远是 9x9 形式的。
        public void solveSudoku(char[][] board) {
            boolean flag = dfs(board, 0 ,0);
            return;
        }
        boolean dfs(char[][] board, int i , int j){
            if(i == 9) return true;
            //到最后一列,换下一行
            if(j == 9) return dfs(board, i + 1, 0); 
            if(board[i][j] != '.') return dfs(board, i , j + 1);
            //选择列表1 - 9
            for(char c = '1'; c <= '9'; c ++){ 
                if (!isValid(board, i ,j ,c)) continue; 
                //选择
                board[i][j] = c; 
                //下一层
                if (dfs(board, i, j + 1)) return true;
                //撤销选择
                board[i][j] = '.';
            }
            return false;
        }
        boolean isValid(char[][] board , int x , int y ,char n){
            for (int i = 0;i < 9; i ++){
                //列重复
                if (board[i][y] == n) return false;
                //行重复
                if (board[x][i] == n) return false;
            }
            //找到九宫格左上元素,依次遍历
            for (int i = x / 3 * 3; i < x / 3 * 3 + 3; i ++){ 
                for (int j = y / 3 * 3;j < y / 3 * 3 + 3; j ++){
                    if (board[i][j] == n) return false;
                }
            }
            return true;
        }
    

    38. 外观数列

    给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。

    注意:整数序列中的每一项将表示为一个字符串。

    「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:

    1.     1
    2.     11
    3.     21
    4.     1211
    5.     111221
    

    第一项是数字 1

    描述前一项,这个数是 1 即 “一个 1 ”,记作 11

    描述前一项,这个数是 11 即 “两个 1 ” ,记作 21

    描述前一项,这个数是 21 即 “一个 2 一个 1 ” ,记作 1211

    描述前一项,这个数是 1211 即 “一个 1 一个 2 两个 1 ” ,记作 111221

    示例 1:

    输入: 1
    输出: "1"
    解释:这是一个基本样例。
    

    示例 2:

    输入: 4
    输出: "1211"
    解释:当 n = 3 时,序列是 "21",其中我们有 "2" 和 "1" 两组,"2" 可以读作 "12",也就是出现频次 = 1 而 值 = 2;类似 "1" 可以读作 "11"。所以答案是 "12" 和 "11" 组合在一起,也就是 "1211"。
    
        public String countAndSay(int n) {
            String s = "1";
            //循环n - 1次
            for(int i = 0; i < n - 1 ; i ++){
                StringBuilder sb = new StringBuilder();
                //每一次记录相同一段的个数
                for(int j = 0; j < s.length(); j ++){
                    int k = j;
                    while(k < s.length() && s.charAt(k) == s.charAt(j)){
                        k++;
                    }
                    //个数
                    String count = k - j + "";
                    //个数 + 字符
                    sb.append(count + s.charAt(j));
                    j = k - 1;
                }
                s= sb.toString();
            }
            return s;
        }
    

    39. 组合总和

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

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

    说明:

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

    示例 1:

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

    https://www.cnblogs.com/summerday152/p/13615904.html

        public List<List<Integer>> combinationSum(int[] arr, int t) {
            List<List<Integer>> res = new ArrayList<>();
            List<Integer> path = new ArrayList<>();
            Arrays.sort(arr);//排序是剪枝的前提
            dfs(res,path,0,arr,t);
            return res;
        }
        
        void dfs(List<List<Integer>> res,List<Integer> path,int s,int[] arr, int t){
            if(t <= 0){
                if(t == 0){
                    res.add(new ArrayList<>(path));
                }
                return;
            }
            for(int i = s; i < arr.length; i++){
                if(arr[i] > t){ //由于数组已经有序,当前这个数应该小于等于剩余数t
                    break;
                }
                path.add(arr[i]);
                dfs(res,path,i,arr,t-arr[i]); //因为
                path.remove(path.size()-1);
            }  
        }
    

    40. 组合总和 II

    https://www.cnblogs.com/summerday152/p/13615904.html

    给定一个数组 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]
    ]
    
        public List<List<Integer>> combinationSum2(int[] arr, int t) {
            List<List<Integer>> res = new ArrayList<>();
            List<Integer> path = new ArrayList<>();
            Arrays.sort(arr);//排序是剪枝的前提
            dfs(res,path,0,arr,t);
            return res;
        }
    	// s 可以看成层数, i可以看成这一层从第几个开始
        void dfs(List<List<Integer>> res,List<Integer> path,int s,int[] arr, int t){
            if(t <= 0){
                if(t == 0){
                    res.add(new ArrayList<>(path));
                }
                return;
            }
            for(int i = s; i < arr.length; i++){ 
                
                if(arr[i] > t){ //由于数组已经有序,当前这个数应该小于等于剩余数t
                    break;
                }
                //保证递归树的同一个层级不出现相同的元素
                if(i > s && arr[i] == arr[i - 1]) continue;
                path.add(arr[i]);
                dfs(res,path,i+1,arr,t-arr[i]); 
                path.remove(path.size()-1);
            }  
        }
    
  • 相关阅读:
    Sum Root to Leaf Numbers
    Sum Root to Leaf Numbers
    Sort Colors
    Partition List
    Binary Tree Inorder Traversal
    Binary Tree Postorder Traversal
    Remove Duplicates from Sorted List II
    Remove Duplicates from Sorted List
    Search a 2D Matrix
    leetcode221
  • 原文地址:https://www.cnblogs.com/summerday152/p/13771456.html
Copyright © 2011-2022 走看看