LeetCode Top100之70,72,75题

70. 爬楼梯

① 题目描述
  • 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
  • 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
  • 注意:给定 n 是一个正整数。
  • 示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶
  • 示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶
② 递归求解(Time Limit Exceeded
  • 用递归的思路想一下,要求 n 层的台阶的走法,由于一次走 1 或 2 个台阶,所以上到第 n 个台阶之前,一定是停留在第 n - 1 个台阶上,或者n - 2个台阶上。所以如果用 f ( n ) 代表 n 个台阶的走法。那么,
    f ( n ) = f ( n - 1) + f ( n - 2 )
    f ( 1 ) = 1,f ( 2 ) = 2
  • 发现个神奇的事情,这就是斐波那契数列(Fibonacci sequence)。
  • 直接暴力一点,利用递归写出来。不过,竟然到37时,Time Limit Exceeded
  • 时间复杂度:是一个树状图, O ( 2 n ) O(2^n) O(2n)
  • 代码如下:
public int climbStairs(int n) {
    
    
    if (n == 1) {
    
    
        return 1;
    }
    if (n == 2) {
    
    
        return 2;
    }
    return climbStairs(n - 2) + climbStairs(n - 1);
}
③ 递归的优化
  • f(4)为例,我们需要分别求解f(3)f(2);求解f(3),需要分谢求解f(2)f(1),其实f(2)早就已经求解过了。
    在这里插入图片描述
  • 优化方法就是把求出的解都存起来,后边求的时候直接使用,不用再进入递归了,叫做 memoization 技术。
  • 这种技术,之前的递归方法的优化中,也使用了!
  • 代码如下:
public int climbStairs(int n) {
    
    
    HashMap<Integer, Integer> map = new HashMap<>();
    return climbStairsN(n, map);
}

public int climbStairsN(int n, HashMap<Integer, Integer> map) {
    
    
    if (n == 1) {
    
    
        return 1;
    }
    if (n == 2) {
    
    
        return 2;
    }
    int f1 = 0;
    if (map.containsKey(n - 1)) {
    
    
        f1 = map.get(n - 1);
    } else {
    
    
        f1 = climbStairsN(n - 1, map);
        map.put(n - 1, f1);
    }
    int f2 = 0;
    if (map.containsKey(n - 2)) {
    
    
        f2 = map.get(n - 2);
    } else {
    
    
        f2 = climbStairsN(n - 2, map);
        map.put(n - 2, f2);
    }
    map.put(n, f1 + f2);
    return f1 + f2;
}
④ 使用数组
  • 新建数组f[],用于存储斐波那契数列,初始化f[1] = 1, f[2] = 2;,那么f[i] = f[i - 1] + f[i - 2]
  • 虽然空间复杂度 O ( n ) O(n) O(n),但是时间复杂度也变为 O ( n ) O(n) O(n),所以运行时间为0ms.
  • 代码如下:
public int climbStairs(int n) {
    
    
    if (n == 1) {
    
    
        return 1;
    }
    if (n == 2) {
    
    
        return 2;
    }
    int[] f = new int[n + 1];
    f[1] = 1;
    f[2] = 2;
    for (int i = 3; i <= n; i++) {
    
    
        f[i] = f[i - 1] + f[i - 2];
    }
    return f[n];
}

72. 编辑距离

② 题目描述
  • 给定两个单词 word1word2,计算出将 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[ 0, i )(word1 的第 0 到 第 i - 1个字符)和 word2[ 0, j - 1) 的最小编辑距离。
  • 状态转移方程:
    ① 若word1[i -1] = word2[j - 1]dp[i][j] = dp[i - 1][j - 1],因为字符没有发生改变。
    ② 若word1[i -1] != word2[j - 1]dp[i][j] = Math.min(dp[i][j - 1], Math.min(dp[i - 1][j], dp[i - 1][j - 1])) + 1,分别表示word1增加、删除、替换一个字符变成word2。
  • 初始化:
    dp[0][0]=0,表示二者长度都为0,最小编辑距离为0;
    ② 从i = 1开始,dp[i][0] = i,表示word1删除字符变成空的word2所需要的步骤;
    ③ 从j = 1开始,dp[0][j] = j,表示word1从空增加字符变成word2所需要的步骤。
public int minDistance(String word1, String word2) {
    
    
    int m = word1.length();
    int n = word2.length();
    int[][] dp = new int[m + 1][n + 1];
    // word1有非空变为空所需要的删除步骤
    for (int i = 1; i <= m; i++) {
    
    
        dp[i][0] = i;
    }
    // word1由空变成 word2所需要的添加的步骤
    for (int j = 1; j <= n; j++) {
    
    
        dp[0][j] = j;
    }
    for (int i = 1; i <= m; i++) {
    
    
        for (int j = 1; j <= n; j++) {
    
    
            // word1的下标i-1.其实表示第i个字符,也就是我们当前要求解的字符
            if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
    
    
                dp[i][j] = dp[i - 1][j - 1];
            } else {
    
    
                // min( add, delete, change)+1
                dp[i][j] = Math.min(dp[i][j - 1], Math.min(dp[i - 1][j], dp[i - 1][j - 1])) + 1;
            }
        }
    }
    return dp[m][n];
}

75. 颜色分类

① 题目描述
  • 给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
  • 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
  • 注意: 不能使用代码库中的排序函数来解决这道题。
  • 示例:

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

  • 进阶:

一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?

② 双指针(自己的想法)
  • 数组排序后一定是0,...,0 1,...,1 2,...,2的情况,我们可以将其看做三部分,每一部分分别都是数字0,1,2。
  • 因此外层循环共3趟,每一趟别将所有的0放在第一段,所有的1放在第二段,所有的放在第三段。
  • 内层循环使用双指针,start指针用于表示待插入数字的位置,cur指针不断向后移动,寻找可以插入的数字。
  • 每趟的内存循环,如果nums[start]刚好为i,需要更新start的指向,并且start不能越界;确定待插入数字的位置后,cur从start+1开始向后遍历数组,查找可以插入的数字。
  • 没有找到可以插入的数字,cur指向下一个数字;否则start和cur同时指向下一个数字。
  • 代码如下:
public void sortColors(int[] nums) {
    
    
    if (nums.length == 0 || nums.length == 1) {
    
    
        return;
    }
    int start = 0;
    for (int i = 0; i < 3; i++) {
    
    
        while (start < nums.length && nums[start] == i) {
    
    
            start++;
        }
        int cur = start + 1;
        while (cur < nums.length) {
    
    // cur向后遍历,直到遍历完数组
            if (nums[cur] == i) {
    
    // 找到了待插入的数字,交换cur和start指向数字
                int temp = nums[start];
                nums[start] = nums[cur];
                nums[cur] = temp;
                start++;  // start+1指向下一个待插入数字的位置
            }
            cur++;
        }
    }
}
③ 统计0和1的个数,为数组添加对应数目的0,1,2
  • 代码如下:
public void sortColors(int[] nums) {
    
    
    if (nums.length == 0 || nums.length == 1) {
    
    
        return;
    }
    int zero_count = 0;
    int one_count = 0;
    for (int i = 0; i < nums.length; i++) {
    
    
        if (nums[i] == 0) {
    
    
            zero_count++;
        }
        if (nums[i] == 1) {
    
    
            one_count++;
        }
    }
    // 将对应位置的数字改为对应数目的0,1,2
    for (int i = 0; i < nums.length; i++) {
    
    
        if (zero_count > 0) {
    
    
            nums[i] = 0;
            zero_count--;
        } else if (one_count > 0) {
    
    // 只有当0的个数加满后,才能添加1
            nums[i] = 1;
            one_count--;
        } else {
    
    
            nums[i] = 2;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/u014454538/article/details/90665738