【LeetCode】 31 - 40题解

31. 下一个排列

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

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

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

以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,31,3,2
3,11,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,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,4,6,7] 可能变为 [4,7,2] )。

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

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

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

示例 1:

输入: nums = [4,2],target = 0
输出: 4

示例 2:

输入: nums = [4,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,8,10],target = 8
输出: [3,4]

示例 2:

输入: nums = [5,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,6],5
输出: 2

示例 2:

输入: [1,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",[".","8","6",["8","3"],["4","1"],["7","2","6"],"4","5"],"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);
        return;
    }
    boolean dfs(char[][] board,int j){
        if(i == 9) return true;
        //到最后一列,换下一行
        if(j == 9) return dfs(board,i + 1,0); 
        if(board[i][j] != '.') return dfs(board,j + 1);
        //选择列表1 - 9
        for(char c = '1'; c <= '9'; c ++){ 
            if (!isValid(board,j,c)) continue; 
            //选择
            board[i][j] = c; 
            //下一层
            if (dfs(board,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,7],target = 7,所求解集为:
[
  [7],[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,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,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,5],target = 8,所求解集为:
[
  [1,[1,6]
]
    public List<List<Integer>> combinationSum2(int[] arr,t);
        return res;
    }
	// s 可以看成层数, i可以看成这一层从第几个开始
    void dfs(List<List<Integer>> res,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,i+1,t-arr[i]); 
            path.remove(path.size()-1);
        }  
    }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


输出自然数 1 到 n所有不重复的排列,即 n的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
编写一个能够输出“`Hello,World!`”的程序,这个程序常常作为一个初学者接触一门新的编程语言所写的第一个程序,也经常用来测试开发、编译环境是否能够正常工作。&#xA;&#xA;提示:“`Hello,Worl
题目相关 【题目描述】 求10000以内n的阶乘。 【输入】 只有一行输入,整数n(0≤n≤10000)。 【输出】 一行,即n!的值。 【输入样例】 4 【输出样例】 24 分析 首先n的阶乘是从1
题目相关 原题链接:P5461 赦免战俘 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目背景 借助反作弊系统,一些在月赛有抄袭作弊行为的选手被抓出来了! 题目描述 现有 $2n
水仙花数 题目描述 输出所有的&amp;quot;水仙花数&amp;quot;.所谓&amp;quot;水仙花数&amp;quot;是指这样的一个三位数:其各位数字的立方和等于该数本身。例如:371是
题目描述 奖学金 某小学最近得到了一笔赞助,打算拿出其中一部分为学习成绩优秀的前5名学生发奖学金。期末,每个学生都有3门课的成绩:语文、数学、英语。先按总分从高到低排序,如果两个同学总分相同,再按语文
已知正整数k满足2≤k≤9,现给出长度最大为30位的十进制非负整数c,求所有能整除c的k。
题目相关 题目描述 我们要求找出具有下列性质数的个数(包含输入的正整数 n)。 先输入一个正整数 n(n ≤1000),然后对此正整数按照如下方法进行处理: 不作任何处理; 在它的左边加上一个正整数,
题目相关 题目描述 排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且 r ≤n),我们可以简单地将n个元素理解为自然数1,2,…,n从中任取r个数。 现要求你输出所有组合。
题目相关 题目描述 把 m个同样的苹果放在 n个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法。(5,1,1 和 1,1,5 是同一种方法) 输入格式 第一行是测试数据的数目 t,以下每行
题目相关 【题目描述】 求两个不超过200位的非负整数的和。 【输入】 有两行,每行是一个不超过200位的非负整数,可能有多余的前导0。 【输出】 一行,即相加后的结果。结果里不能有多余的前导0,即如
请你编一程序实现两种不同进制之间的数据转换。
题目相关 【题目描述】 求两个大的正整数相减的差。 【输入】 共2行,第1行是被减数a,第2行是减数b(a &amp;gt; b)。每个大整数不超过200位,不会有多余的前导零。 【输出】 一行,即所
题目描述 一个数如果恰好等于不包含它本身所有因子之和,这个数就称为&amp;quot;完数&amp;quot;。 例如,6的因子为1、2、3,而6=1ʲʳ,因此6是&amp;quot;完数&amp
题目相关 【题目描述】 任意给定一个正整数N(N≤100),计算2的n次方的值。 【输入】 输入一个正整数N。 【输出】 输出2的N次方的值。 【输入样例】 5 【输出样例】 32 分析 本题考察的是
题目相关 【题目描述】 输入三个整数,整数之间由一个空格分隔,整数是32位有符号整数。把第二个输入的整数输出。 【输入】 只有一行,共三个整数,整数之间由一个空格分隔。整数是32位有符号整数。 【输出
79. 单词搜索 给定一个二维网格和一个单词,找出该单词是否存在于网格中。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母
[toc] 707.设计双向链表 链表简介 链表(LinkedList) 是一种线性表,但不是顺序表,因为它是通过节点直接的相互引用相互联系起来的。 由于不必按顺序存储, 链表在插入和删除的时候可以达
题目描述 784. 字母大小写全排列 给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。 示例: 输入:S = &amp;quot;a1
[toc] Leetcode动态规划【简单题】 动态规划 (Dynamic programming,简称DP),是一种把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划相较于递归,拥有更