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);
            }  
        }
    
  • 相关阅读:
    Mysql面对高并发修改的问题处理【2】
    HSF处理流程分析
    com.jcraft.jsch.JSchException: invalid privatekey
    linux常用命令
    VPS教程:VPS主机能PING通但是SSH无法连接
    Windows 和Linux 不同操作系统的VPS有哪些区别,如何选择?
    Windows VPS有哪些?
    VPS教程:搭建个人云笔记服务器
    VPS搭个人网盘,seafile、kodexplorer、h5ai谁更合适?
    VPS教程:搭建个人网盘—seafile
  • 原文地址:https://www.cnblogs.com/summerday152/p/13771456.html
Copyright © 2011-2022 走看看