【LeetCode】 61 - 70题解

61. 旋转链表

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL

示例 2:

输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
    public ListNode rotateRight(ListNode head, int k) {
        if(head == null) return null;
        // 计算链表的节点数
        int n = 0;
        for(ListNode p = head; p != null; p = p.next) n++;
        // 取余
        k %= n;
        // 快指针前进k步
        ListNode slow = head, fast = head;
        while(k -- > 0) fast = fast.next;
        while(fast.next!= null){
            fast = fast.next;
            slow = slow.next;
        }
        // 节点交换 画图查看
        fast.next = head;
        head = slow.next;
        slow.next = null;
        return head;
    }

62. 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?

例如,上图是一个7 x 3 的网格。有多少可能的路径?

示例 1:

输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右

示例 2:

输入: m = 7, n = 3
输出: 28

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 10 ^ 9

动态规划【未优化】

    public int uniquePaths(int m, int n) {
        //f[m,n]表示走到m,n的路径 res = f[m-1][n-1]
        int[][] f = new int[m][n];

        for(int i = 0; i < m; i ++){
            for(int j = 0; j < n; j++){
                // 第一行 和 第一列 只能从同一个方向过来
                if(i == 0 || j == 0) f[i][j] = 1;
                // 其他的位置 都有两个方向的来源
                else f[i][j] = f[i - 1][j] + f[i][j - 1];
            }
        }
        return f[m - 1][n - 1];
    }

空间优化

    public int uniquePaths(int m, int n) {
        int[] f = new int[n];
        Arrays.fill(f, 1);
        for(int i = 1; i < m; i ++){
            for(int j = 1; j < n; j ++){
                f[j] += f[j - 1]; 
            }
        }
        return f[n - 1];
    }

63. 不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 10 来表示。

示例 1:

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

示例 2:

输入:obstacleGrid = [[0,1],[0,0]]
输出:1

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j]01

动态规划

第一行第一列的情况: 一旦遇到障碍物, 后续的位置便都是0。

其他情况:从上或左方向来,障碍物的位置f = 0。

class Solution {
    public int uniquePathsWithObstacles(int[][] g) {
        int n = g.length, m = g[0].length;
        //f[m,n]表示走到m,n的路径 res = f[m-1][n-1]
        int[][] f = new int[n][m];
        // 初始化第一列
        for(int i = 0; i < n && g[i][0] == 0; i ++) f[i][0] = 1;
        // 初始化第一行
        for(int i = 0; i < m && g[0][i] == 0; i ++) f[0][i] = 1;
		// 其他位置
        for(int i = 1; i < n; i ++){
            for(int j = 1; j < m; j ++){
                if(g[i][j] == 1) continue;
                f[i][j] = f[i - 1][j] + f[i][j - 1];
            }
        }

        return f[n - 1][m - 1];
    }
}

空间优化

class Solution {
    public int uniquePathsWithObstacles(int[][] g) {
        int n = g.length, m = g[0].length;
        int[] f = new int[m];
        f[0] = g[0][0] == 0 ? 1 : 0;
        
        for(int i = 0; i < n; i ++){
            for(int j = 0; j < m; j ++){
                if(g[i][j] == 1) {
                    f[j] = 0;
                    continue;
                }
                if(j - 1 >= 0 && g[i][j - 1] == 0){
                    f[j] += f[j - 1];
                }
            }
        }

        return f[m - 1];
    }
}

64. 最小路径和

难度中等793收藏分享切换为英文接收动态反馈

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

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

示例 1:

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

示例 2:

输入:grid = [[1,2,3],[4,5,6]]
输出:12

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 100

动态规划

class Solution {
    // 在grid 上原地操作
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 && j == 0) {
                    continue;
                // 第一行 只能从左边来
                } else if (i == 0) {
                    grid[i][j] += grid[i][j - 1];
                // 第一列 只能从上面来
                } else if (j == 0) {
                    grid[i][j] += grid[i - 1][j];
                // 取相对小的一个
                } else {
                    grid[i][j] += Math.min(grid[i][j - 1],grid[i - 1][j]);
                }
            }
        }
        return grid[m - 1][n - 1];
    }
}

65. 有效数字

有效数字(按顺序)可以分成以下几个部分:

  1. 一个 小数 或者 整数
  2. (可选)一个 'e''E' ,后面跟着一个 整数

小数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+''-'
  2. 下述格式之一:
    1. 至少一位数字,后面跟着一个点 '.'
    2. 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
    3. 一个点 '.' ,后面跟着至少一位数字

整数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+''-'
  2. 至少一位数字

部分有效数字列举如下:

  • ["2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1", "53.5e93", "-123.456e789"]

部分无效数字列举如下:

  • ["abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"]

给你一个字符串 s ,如果 s 是一个 有效数字 ,请返回 true

示例 1:

输入:s = "0"
输出:true

示例 2:

输入:s = "e"
输出:false

示例 3:

输入:s = "."
输出:false

示例 4:

输入:s = ".1"
输出:true

提示:

  • 1 <= s.length <= 20
  • s 仅含英文字母(大写和小写),数字(0-9),加号 '+' ,减号 '-' ,或者点 '.'
class Solution {
    // https://leetcode.com/problems/valid-number/discuss/1066333/Java-Easy-Solution-(with-comments)
    public boolean isNumber(String s) {
        boolean sign = false;
        boolean digit = false;
        boolean e = false;
        boolean dots = false;
        for(int i = 0; i < s.length(); i ++){
            // ±号的情况,满足条件: 只能出现一次符号且该位置是第一位或者前面是e
            if(s.charAt(i) == '+' || s.charAt(i) == '-') {
                if((i == 0 || s.charAt(i - 1) == 'e') && !sign){
                    sign = true;
                    continue;
                }else return false;
            }
            // 数字的限制较少, 直接标记一下并跳过
            if(s.charAt(i) >= '0' && s.charAt(i) <= '9'){
                digit = true;
                continue;
            }
            // 不能遇到点 或者 e
            if(s.charAt(i) == '.'){
                if(e || dots) return false;
                dots = true;
                continue;
            }
            // 如果是e,前面要出现过数字,且没有出现e
            if((s.charAt(i) == 'e' || s.charAt(i) == 'E') && digit && !e){
                digit = false;
                sign = false;
                e = true;
                continue;
            }else return false;
 
        }
        if(digit) return true;
        else return false;
    }
}

66. 加一

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。

示例 2:

输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
    public int[] plusOne(int[] digits) {
        for(int i = digits.length - 1; i >= 0; i --){
            if(digits[i] != 9){
                digits[i] ++;
                return digits;
            }
            digits[i] = 0;
        }
        // 每个都是0  1000
        int[] res = new int[digits.length + 1];
        res[0] = 1;
        return res;
    }

67. 二进制求和

给你两个二进制字符串,返回它们的和(用二进制表示)。

输入为 非空 字符串且只包含数字 10

示例 1:

输入: a = "11", b = "1"
输出: "100"

示例 2:

输入: a = "1010", b = "1011"
输出: "10101"

提示:

  • 每个字符串仅由字符 '0''1' 组成。
  • 1 <= a.length, b.length <= 10^4
  • 字符串如果不是 "0" ,就都不含前导零。

模拟进位

class Solution {
    public String addBinary(String a, String b) {
        int c = 0; // 进位
        int i = a.length() - 1, j = b.length() - 1;
        StringBuilder ans = new StringBuilder();
        int aa = 0, bb = 0;
        while(i >= 0 || j >= 0){
            aa = i < 0 ? 0 : a.charAt(i --) - '0';
            bb = j < 0 ? 0 : b.charAt(j --) - '0';
            c += aa + bb;
            ans.append(c % 2);
            c >>= 1;
        }
        if(c > 0) ans.append(c);
        return ans.reverse().toString();
    }
}

68. 文本左右对齐

给定一个单词数组和一个长度 maxWidth,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。

你应该使用“贪心算法”来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ' ' 填充,使得每行恰好有 maxWidth 个字符。

要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。

文本的最后一行应为左对齐,且单词之间不插入额外的空格。

说明:

  • 单词是指由非空格字符组成的字符序列。
  • 每个单词的长度大于 0,小于等于 maxWidth
  • 输入单词数组 words 至少包含一个单词。

示例:

输入:
words = ["What","must","be","acknowledgment","shall","be"]
maxWidth = 16
输出:
[
  "What   must   be",
  "acknowledgment  ",
  "shall be        "
]
解释: 注意最后一行的格式应为 "shall be    " 而不是 "shall     be",
     因为最后一行应为左对齐,而不是左右两端对齐。       
     第二行同样为左对齐,这是因为这行只包含一个单词。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/text-justification
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

https://www.youtube.com/watch?v=GqXlEbFVTXY

class Solution {
    public List<String> fullJustify(String[] words, int maxWidth) {
        List<String> res = new ArrayList<>();
        int i = 0, n = words.length;
        // 双指针,划定每一行的单词范围
        while(i < n){
            int j = i + 1;
            int lineLength = words[i].length();
            while(j < n && 
                  (lineLength + words[j].length() + (j - i - 1) < maxWidth)){
                lineLength += words[j].length();
                ++ j;
            }
            int diff = maxWidth - lineLength;
            // 单词数量
            int numberOfWords = j - i;
            if(numberOfWords == 1 || j >= n) res.add(leftJustify(words, diff, i, j));
            else res.add(middleJustify(words, diff, i, j));

            i = j;
        }
        return res;
    }
	// 一行的单词数量只有一个 或者最后一行 左对齐
    String leftJustify(String[] words, int diff, int i, int j){
        int spaceOnRight = diff - (j - i - 1);// 右边需要补充的空格数
        StringBuilder res = new StringBuilder(words[i]);
        for(int k = i + 1; k < j; k ++){
            res.append(" " + words[k]);
        }
        res.append(" ".repeat(spaceOnRight));
        return res.toString();
    }
	// 左右两端对齐
    String middleJustify(String[] words, int diff, int i, int j){
        int spacesNeeded = j - i - 1;
        int spaces = diff / spacesNeeded;
        int extraSpaces = diff % spacesNeeded;

        StringBuilder res = new StringBuilder(words[i]);
        for(int k = i + 1; k < j; k ++){
            int spacesToApply = spaces + (extraSpaces-- > 0 ? 1 : 0);
            res.append(" ".repeat(spacesToApply) + words[k]);
        }
        return res.toString();
    }
}

69. x 的平方根

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4
输出: 2

示例 2:

输入: 8
输出: 2
说明: 8 的平方根是 2.82842..., 
     由于返回类型是整数,小数部分将被舍去。

二分查找

class Solution {
    public int mySqrt(int x) {
        if(x == 1) return 1;
        int l = 0, r = x / 2;
        while( l < r ){
            int mid = l + r + 1 >> 1;
            if (mid <= x / mid) l = mid;
            else r = mid - 1;
        }
        return r;
    }
}

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 阶

动态规划

    public int climbStairs(int n) {
        if (n <= 2) {
            return n;
        }

        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];
    }

动态优化 空间优化

class Solution {
    public int climbStairs(int n) {
        int a = 0, b = 0, c = 1;
        for (int i = 1; i <= n; ++i) {
            a = b; 
            b = c; 
            c = a + b;
        }
        return c;
    }
}

时间复杂度 O(N)

数学解法

\[f(n) = f(n - 1) + f(n - 2)$$的特征方程: \]

x^2 = x + 1

\[求得$$ x_1 = {1 + \sqrt{5} \over 2}, x_2 = {1 - \sqrt{5} \over 2}$$, 设通解$$f(n) = c_1 x_1^n + c_2x_2^n$$,代入初始条件$$f(1) = 1, f(2) = 1$$, 得$c_1 = {1 \over \sqrt5}, c_2 = -{1 \over \sqrt5}$,得到递推数列的通项公式 : \]

Fn = {1 \over \sqrt{5}} \left[ \left( {1 + \sqrt{5} \over 2} \right)^2- \left( {1 - \sqrt{5} \over 2}\right)^2\right]

\[ ```java public int climbStairs(int n) { double sqrt5 = Math.sqrt(5); double fibn = Math.pow((1 + sqrt5) / 2, n + 1) - Math.pow((1 - sqrt5) / 2, n + 1); return (int) Math.round(fibn / sqrt5); } ``` \]

posted @ 2021-02-25 20:24  天乔巴夏丶  阅读(346)  评论(0编辑  收藏  举报