zoukankan      html  css  js  c++  java
  • 刷题篇--热题HOT 21-30

    46.全排列

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

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

     

     



     

     

     

     

     

     

     

     

     1 class Solution {
     2 
     3     List<List<Integer>> res = new ArrayList();
     4 
     5     public List<List<Integer>> permute(int[] nums) {
     6         if(nums == null || nums.length == 0) return res;
     7         permutation(nums, 0);
     8         return res;
     9     }
    10     public void permutation(int[] nums, int i){
    11         if(i == nums.length-1){
    12             ArrayList<Integer> list = new ArrayList();
    13             for(int num : nums){
    14                 list.add(num);
    15             }
    16             res.add(list);
    17         }else{
    18             for(int j = i; j < nums.length; j++){
    19                 swap(nums, i, j);
    20                 permutation(nums, i+1);
    21                 swap(nums, i, j);
    22             }
    23         }
    24     }
    25     public void swap(int[] nums, int i, int j){
    26         int temp = nums[i];
    27         nums[i] = nums[j];
    28         nums[j] = temp;
    29     }
    30 }

     

    48.旋转图像

    给定一个 n × n 的二维矩阵表示一个图像。将图像顺时针旋转 90 度。说明:你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

    分析:转置+翻转

    class Solution {
        public void rotate(int[][] matrix) {
            int n = matrix.length;
            //转置
            for(int i=0; i < n; i++){
                for(int j=i; j < n; j++){
                    int temp = matrix[i][j];
                    matrix[i][j] = matrix[j][i];
                    matrix[j][i] = temp;
                }
            }
            //翻转
            for(int i=0; i<n; i++){
                for(int j=0; j < n/2; j++){
                    int temp = matrix[i][j];
                    matrix[i][j] = matrix[i][n-1-j];
                    matrix[i][n-1-j] = temp;
                }
            }
        }
    }

     

     

    49.字母异位词分组

    给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

    示例:输入: ["eat", "tea", "ate", "nat", "bat"],输出:[ ["ate","eat","tea"], ["nat","tan"], ["bat"]]
    说明:所有输入均为小写字母。 不考虑答案输出的顺序。
    分析:字母异位词字母相同,若将字符转换成数组进行排序,则他们的排序结果相同。使用一个HashMap保存数据,key是排序字符串,value是一个列表,存储字母异位词。

     1 class Solution {
     2     public List<List<String>> groupAnagrams(String[] strs) {
     3         if(strs == null || strs.length == 0) return new ArrayList();
     4         Map<String,List> map = new HashMap();
     5         for(String str : strs){
     6             char[] ch = str.toCharArray();
     7             Arrays.sort(ch);
     8             String key = String.valueOf(ch);//排序后的字符串
     9             if(!map.containsKey(key)){
    10                 map.put(key,new ArrayList());
    11             }
    12             map.get(key).add(str);
    13         }
    14         return new ArrayList(map.values());
    15     }
    16 }

     

     

    53.最大子序和

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和

    示例:输入: [-2,1,-3,4,-1,2,1,-5,4],输出:解释:  连续子数组 [4,-1,2,1] 的和最大,为 6。

    分析:贪心:每一步做到最好,然后找出整个数组中最优值。先维护一个当前位置和最大值curMax,和一个当前位置和当前位置之前的和最大值allMax。curMax的取值通过当前位置加上或者不加上前面取值(取较大值,且当前位置的值必包含在和内)来得到。时间复杂度O(N) 空间复杂度 O(1)

     1 class Solution {
     2     public int maxSubArray(int[] nums) {
     3         int curMax = nums[0], allMax = nums[0];
     4         for(int i=1; i < nums.length; i++){
     5             curMax = Math.max(nums[i], curMax + nums[i]);
     6             allMax = Math.max(curMax, allMax);
     7         }
     8         return allMax;
     9     }
    10 }

     

     

     

    55.跳跃游戏

    给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。

    示例 1:输入: [2,3,1,1,4]输出: true
    解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
    示例 2:输入: [3,2,1,0,4]输出: false
    解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

    方法一:递归——所有可能  (超时了……),一个位置判断为不行之后,之前的位置在遍历时还会到这个位置,重复判断。时间复杂度O(2^n)

     1 class Solution {
     2     public boolean canJump(int[] nums) {
     3         return testJump(nums, 0);
     4     }
     5 
     6     public boolean testJump(int[] nums, int index){
     7         if(index > nums.length) return false;
     8         if(index == nums.length-1) return true;
     9         for(int i = 1; i <= nums[index]; i++){//可能的跳数
    10             if(testJump(nums, index + i)){
    11                 return true;
    12             }
    13         }
    14         return false;
    15     }
    16 }

     

    方法二:设置一个数组,有三个值-1,0,1。‘-1’表示该位置不能到达终点,‘0’表示初始未知,‘1’表示从该位置出发可以到达终点。为了重复判断,采取从右到左遍历方式。时间复杂度O(N^2) 空间复杂度 O(N)

     1 class Solution {
     2     public boolean canJump(int[] nums) {
     3         int len = nums.length;
     4         int[] standard = new int[len];//默认是0
     5         //最后一个位置肯定是1
     6         standard[len-1] = 1;
     7         //从右往左遍历
     8         for(int i = len-2; i>=0; i--){
     9             //当前位置有nums[i]种跳法
    10             for(int jump = 1; jump <= nums[i] && i + jump < len; jump++){
    11                 if(standard[i+jump] == 1){
    12                     standard[i] = 1;
    13                     break;
    14                 }
    15             }
    16         }
    17         return standard[0] == 1;
    18     }
    19 }

    方法三:贪心,位置 i 能够跳到另一个位置 j 则需要条件:i + nums[i] >= j。根据这一条件我们可以从右往左不断寻找满足这样条件的位置,例如

    [9,4,2,1,0,2,0],最后一个位置nums[6]=0,目的索引为i=6,从右往左寻找,发现 5 + nums[5] = 7 > 6,所以能够从i=5位置跳到i=6,此时将目的位置设置为i=5。再次寻找,发现 0 + nums[0] = 9 > 7,即能够从i=0位置跳到i=5,此时将目的位置设置为i=0,结束!虽然从i=0也能直接跳到i=6,但是只要有一种跳法就可以,如果一个点能够忽略中间“跳板”跳到终点,那么这个点肯定也能够通过“跳板”跳到终点,本方法采用的是贪心算法,尽可能多的跳。时间复杂度O(N) 空间复杂度 O(1)

     1 class Solution {
     2     public boolean canJump(int[] nums) {
     3         int len = nums.length;
     4         int destination = len - 1;
     5         for(int i = len - 2; i >= 0; i--){
     6             destination  = (i + nums[i]) >= destination ? i : destination;
     7         }
     8         return destination == 0;
     9     }
    10 }

     

     56.合并区间

    给出一个区间的集合,请合并所有重叠的区间。(二维数组是n行2列)
    示例 1:输入: [[1,3],[2,6],[8,10],[15,18]],输出: [[1,6],[8,10],[15,18]],解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
    示例 2:输入: [[1,4],[4,5]],输出: [[1,5]],解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。

    分析:什么样的区间需要合并?例如[a,b]、[c,d],如果 c<=b<=d则两个区间有重合部分,需要合并。那合并后的区间应该是什么样的呢? 合并后的区间应该是 [ min(a,c) , max(b,d) ]。此题应该先将二维数组进行排序,避免合并区间进行多次循环,那么此时合并的条件就变成 !(c>b)。合并后的区间为[a , max(b,d)]。因为合并区间需要存入存出并且不知道合并后的区间个数,因此采用一个LinkedList临时存储合并后的区间,最后使用toArray返回结果。

     1 class Solution {
     2     public int[][] merge(int[][] intervals) {
     3         LinkedList<int[]> list = new LinkedList();
     4         if(intervals == null || intervals.length == 0) return list.toArray(new int[0][]);
     5         //根据数组首位元素进行排序
     6         Arrays.sort(intervals, new Comparator<int[]>(){
     7             public int compare(int[] o1, int[] o2){
     8                 return o1[0] - o2[0];//根据数组区间左位置排序
     9             }
    10         });
    11         list.add(intervals[0]);
    12         for(int i = 1; i < intervals.length; i++){
    13             if(intervals[i][0] > list.getLast()[1]){//c>b 直接在list填入
    14                 list.add(intervals[i]);
    15             }else{//需要合并区间,只要将list最后一个元素的右位置取两个中的最大值
    16                 list.getLast()[1] = Math.max(intervals[i][1], list.getLast()[1]);
    17             }
    18         }
    19         return list.toArray(new int[0][0]);
    20 
    21     }
    22 }

     

     

    62. 不同路径

    一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。问总共有多少条不同的路径?

     

     

     说明:m 和 n 的值均不超过 100。
    示例 1:输入: m = 3, n = 2,输出: 3
    解释:从左上角开始,总共有 3 条路径可以到达右下角。1. 向右 -> 向右 -> 向下2. 向右 -> 向下 -> 向右3. 向下 -> 向右 -> 向右
    示例 2:输入: m = 7, n = 3,输出: 28

    分析:如果机器人位于(i,j)位置,则下一步可能要走的位置( i+1 , j ) , ( i , j+1 )。感觉和全排列相似,使用递归回溯方法

    法一:递归  超时

     1 class Solution {
     2     int res = 0;
     3     public int uniquePaths(int m, int n) {
     4         path(0,0,m,n);
     5         return res;
     6     }
     7     public void path(int i, int j, int m, int n){
     8         if(i == m-1 && j == n-1){
     9             res++;
    10         }
    11         if(i>=m||j>=n){
    12             return;
    13         }
    14         path(i+1,j,m,n);
    15         path(i,j+1,m,n); 
    16         return;
    17     }
    18 }

     

    法二:动态规划

    定义一个二维数组dp[][],表示到从出发点到当前位置有多少种走法,因为机器人只能向右或者向下移动,所以d[0][],d[][0]都为1(一直向下,或者一直向右)。当i!=0,j!=0时,到达(i,j)位置只能通过(i-1,j)和(i,j-1)两个位置到达,所以dp[i][j] = dp[i-1][j] + dp[i][j-1].

     1 class Solution {
     2     public int uniquePaths(int m, int n) {
     3         int[][] dp = new int[m][n];
     4         for(int i=0;i<m;i++) dp[i][0] = 1;
     5         for(int i=0;i<n;i++) dp[0][i] = 1;
     6         for(int i=1;i<m;i++){
     7             for(int j=1;j<n;j++){
     8                 dp[i][j] = dp[i-1][j]+dp[i][j-1];
     9             }
    10         }
    11         return dp[m-1][n-1];
    12     }
    13 }

     

     

     

    64.最小路径和

    给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

    说明:每次只能向下或者向右移动一步。

    输入:
    [
      [1,3,1],
      [1,5,1],
      [4,2,1]
    ]        输出: 7解释: 因为路径 1→3→1→1→1 的总和最小。

    分析:根据上题可以轻松想到动态规划(可以新建一个dp二维数组,也可以直接在原来数组上修改,看实际应用是是否允许修改原数组)

     1 class Solution {
     2     public int minPathSum(int[][] grid) {
     3         int m = grid.length;
     4         int n = grid[0].length;
     5         int[][] dp = new int[m][n];
     6         dp[0][0] = grid[0][0];
     7         for(int i=1;i<m;i++) dp[i][0] = dp[i-1][0] + grid[i][0];
     8         for(int j=1;j<n;j++) dp[0][j] = dp[0][j-1] + grid[0][j];
     9         for(int i=1;i<m;i++){
    10             for(int j=1;j<n;j++){
    11                 dp[i][j] = grid[i][j] + Math.min(dp[i-1][j], dp[i][j-1]);
    12             }
    13         }
    14         return dp[m-1][n-1];
    15     }
    16 }

     

     

    70.爬楼梯

    假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?注意:给定 n 是一个正整数。法一:递归 超时(这玩意能少就少用)

    1 class Solution {
    2     public int climbStairs(int n) {
    3         if(n<=3){
    4             return n;
    5         }else{
    6             return climbStairs(n-1) + climbStairs(n-2);
    7         }
    8     }
    9 }

     

    法二:动态规划,使用数组

     1 class Solution {
     2     public int climbStairs(int n) {
     3         if(n<=3){
     4             return n;
     5         }
     6         int[] dp = new int[n];
     7         dp[0] = 1;
     8         dp[1] = 2;
     9         for(int i=2;i<n;i++){
    10             dp[i] = dp[i-1] + dp [i-2];
    11         }
    12         return dp[n-1];
    13     }
    14 }

     

    法三:斐波那契数列,不需要数组

     1 class Solution {
     2     public int climbStairs(int n) {
     3         if(n<=3){
     4             return n;
     5         }
     6         int first = 1;
     7         int second = 2;
     8         for(int i=3;i<=n;i++){
     9             int third = first + second;
    10             first = second;
    11             second = third;
    12         }
    13         return second;
    14     }
    15 }

     

     

    72.编辑距离

    给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
    你可以对一个单词进行如下三种操作: 插入一个字符、删除一个字符、替换一个字符
    示例 1:输入: word1 = "horse", word2 = "ros",输出: 3
    解释: horse -> rorse (将 'h' 替换为 'r',rorse -> rose (删除 'r'),rose -> ros (删除 'e')
    示例 2:输入: word1 = "intention", word2 = "execution",输出: 5
    解释: intention -> inention (删除 't'),inention -> enention (将 'i' 替换为 'e'),enention -> exention (将 'n' 替换为 'x'),exention -> exection (将 'n' 替换为 'c'),exection -> execution (插入 'u')

    分析:?这题不会分析,直接题解了。。。官方使用动态规划算法,递归也能解决,不过重复率过高。

      首先建立一个二维数组dp[i][j]表示word1的前i个字母和word2的前j个字母之间的最短编辑距离。按照以往经验,从前往后推dp数组元素,根据前面的数组元素确定当前最短编辑距离。那么怎么确定当前元素呢?已知对一个单词可以有三种操作:删除、插入、替换。

      如果当前i位置和j位置的字符相同,即word1[i] = word2[j],那么这两个字符不需要任何操作,dp[i][j] = dp[i-1][j-1]。

      如果当前i位置和j位置的字符相同,即word1[i] != word2[j],那么当前字符应该如何操作呢?我们来看dp[i][j]之前的操作。

        ① dp[i-1][j-1]:如果使得dp[i][j] = dp[i-1][j-1]+1,那么就是当前字符进行替换

     

     

     

        ② dp[i][j-1]:如果使得dp[i][j] = dp[i][j-1]+1,那么就是在word1[i]字符进行插入操作,这样word1[i]就和word2[j]匹配了,前移j一个,继续和i比较。

    ===》》》》》

        ③ dp[i-1][j-1]:如果使得dp[i][j] = dp[i-1][j]+1,那么就是在word1[i]字符进行删除操作,保持j位置不动,i往前移动一个。

      已经确定了之前的三个操作,那么如何选择呢,当然是选择最小的了!

     1 class Solution {
     2     public int minDistance(String word1, String word2) {
     3         int len1 = word1.length();
     4         int len2 = word2.length();
     5         int[][] dp = new int[len1+1][len2+1];
     6         //开始填入0行0列初始数据
     7         for(int i=0;i<=len1;i++) dp[i][0] = i;//只进行删除操作
     8         for(int i=0;i<=len2;i++) dp[0][i] = i;
     9         //计算dp[][]
    10         for(int i=1;i<=len1;i++){
    11             for(int j=1;j<=len2;j++){
    12                 if(word1.charAt(i-1)==word2.charAt(j-1)){
    13                     dp[i][j] = dp[i-1][j-1];//当前相等,就直接往前移动一个
    14                 }else{
    15                     dp[i][j] = Math.min(dp[i-1][j-1],Math.min(dp[i][j-1],dp[i-1][j]))+1;//替换、插入、删除
    16                 }
    17             }
    18         }
    19         return dp[len1][len2];
    20 
    21     }
    22 }

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    sqlserver2005新特性介绍
    Sql 数据库 用户密码MD5加密
    easyui datagrid
    JQ js 对数组的操作
    c#2.0锐利体验《泛型编程》读书笔记
    jQuery EasyUI DataGrid Checkbox 数据设定与取值
    数据库的常用命令
    关于background-image设置背景图片
    Css背景设置 、
    实时监听输入框值变化的完美方案:oninput & onpropertychange
  • 原文地址:https://www.cnblogs.com/qmillet/p/12119837.html
Copyright © 2011-2022 走看看