「leetcode」66. 加一;119. 杨辉三角 II;256. 粉刷房子;415. 字符串相加;581. 最短无序连续子数组;

379 阅读7分钟

前言

一些水题, gaga~~

66. 加一

原题

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

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

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

示例 1:

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

示例 2:

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

思路

思路很简单,人肉模拟计算器。记得在最后判断是否仍有进位。如果有,在数组的顶部添加数字1。

代码

/**
 * @param {number[]} digits
 * @return {number[]}
 */
var plusOne = function(digits) {
    const len = digits.length
    let isCarry = false
    for (let i = len - 1; i >= 0; i--) {
        let num = digits[i]
        if (num + 1 === 10) {
            digits[i] = 0
            isCarry = true
        } else {
            digits[i] += 1
            isCarry = false
            break
        }
    }
    if (isCarry) {
        digits.unshift(1)
    }
    return digits
};

119. 杨辉三角 II

原题

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。

PascalTriangleAnimated2.gif

在杨辉三角中,每个数是它左上方和右上方的数的和。

示例:

输入: 3
输出: [1,3,3,1]
进阶:你可以优化你的算法到 O(k) 空间复杂度吗?

思路

两种思路。第一种思路,根据yangHuiTriangle[i][j] = yangHuiTriangle[i-1][j-1] + yangHuiTriangle[i-1][j] i为行 j为列,生成一个完整的杨辉三角,然后返回对应的行即可,但是会额外生成一个二维数组,不满足O(k)空间复杂度的要求。

第二种思路,我们只需要生成杨辉三角的某一行,思路比较抽象,所以我们举一个例子,逐步说明,当k等于3时。


           1
     1           1
 1         1         1
k = 2 时,创建一个长度为3的数组,数组的初始内容等于`[1,1, 1]`,从尾巴开始遍历。
1. `yangHuiTriangle[1] = yangHuiTriangle[1] + yangHuiTriangle[0] = 1 + 1 = 2`,等于21
     1           1
 1         2         1
杨辉三角的第二行,等于`[1,2,1]`


              1
        1           1
   1          2           1
1       2           1          1
k = 3 时,创建一个长度为4的数组,数组的初始内容等于`[1,2,1,1]`,从尾巴开始遍历。
1. `yangHuiTriangle[2] = yangHuiTriangle[2] + yangHuiTriangle[1] = 1 + 2 = 3`
              1
        1           1
   1          2           1
1       2           3          1
2. `yangHuiTriangle[1] = yangHuiTriangle[1] + yangHuiTriangle[0] = 2 + 1 = 3`
              1
        1           1
   1          2           1
1       3           3          1
杨辉三角的第二行,等于`[1,3,3,1]`

代码


/**
 * @param {number} rowIndex
 * @return {number[]}
 */
var getRow = function(rowIndex) {
    const k = rowIndex
    const yangHuiTriangle = [
        [1],
        [1,1]
    ]
    
    if (k === 0 || k === 1) {
        return yangHuiTriangle[k]
    }
    
    for (let i = 2; i <= k; i++) {
        for (let j = 0; j < i + 1; j++) {
            if (!yangHuiTriangle[i]) {
                // 初始化数组
                yangHuiTriangle[i] = []
            }
            let m = yangHuiTriangle[i-1][j-1] || 0
            let n = yangHuiTriangle[i-1][j] || 0
            yangHuiTriangle[i][j] = m + n
        }
    }
    
    return yangHuiTriangle[k]
};

使用o(k)的空间复杂度的算法

不需要生成完整的杨辉三角


/**
 * @param {number} rowIndex
 * @return {number[]}
 */
var getRow = function(rowIndex) {
    const k = rowIndex
    const yangHuiTriangle = [1, 1]
    
    if (k === 0) {
        return [1]
    }
    
    if (k === 1) {
        return [1, 1]
    }
    
    
    for (let i = 2; i <= k; i++) {
        // 每一行的首尾为1
        yangHuiTriangle[0] = yangHuiTriangle[i] = 1
        for (let j = i - 1; j > 0; j--) {
             yangHuiTriangle[j] = yangHuiTriangle[j-1] + yangHuiTriangle[j]
        }
    }
    
    return yangHuiTriangle
};

256. 粉刷房子

原题

假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。

当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的矩阵来表示的。

例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。请你计算出粉刷完所有房子最少的花费成本。

注意:

所有花费均为正整数。

示例:

输入: [[17,2,17],[16,16,5],[14,3,19]]
输出: 10
解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色。
     最少花费: 2 + 5 + 3 = 10。

思路

i个房子,如果刷成红色,最小成本等于,当前房子红色的成本 + 前一个房子如果刷成蓝色或者绿色时的状态时的总成本

i个房子,如果刷成蓝色,最小成本等于,当前房子蓝色的成本 + 前一个房子如果刷成红色或者绿色时的状态时的总成本

i个房子,如果刷成绿色,最小成本等于,当前房子绿色的成本 + 前一个房子如果刷成红色或者蓝色时的状态时的总成本

可得如下状态转移方程

QQ20191017-000941@2x.png

代码


/**
 * @param {number[][]} costs
 * @return {number}
 */
var minCost = function(costs) {
    if (costs.length === 0) {
        return 0   
    } 
    const redDp = []
    const blueDp = []
    const greenDp = []
    let state = 0
    redDp[0] = costs[0][0]
    blueDp[0] = costs[0][1]
    greenDp[0] = costs[0][2]
    state = Math.min(redDp[0], blueDp[0], greenDp[0])
    for (let i = 1; i < costs.length; i++) {
        redDp[i] = Math.min(blueDp[i - 1], greenDp[i - 1]) + costs[i][0]
        blueDp[i] = Math.min(redDp[i - 1], greenDp[i - 1]) + costs[i][1]
        greenDp[i] = Math.min(blueDp[i - 1], redDp[i - 1]) + costs[i][2]
        state = Math.min(redDp[i], blueDp[i], greenDp[i])
    }
    return state
};

581. 最短无序连续子数组

原题

给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

你找到的子数组应是最短的,请输出它的长度。

示例 1:

输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。

说明 :

  • 输入的数组长度范围在 [1, 10,000]。
  • 输入的数组可能包含重复元素 ,所以升序的意思是<=。

思路

本题的解答思路在于如何判断无序数组的起始位置和结束位置。

何为无序?

当从头开始遍历数组,首次出现num[i] > num[j] && i < j时,既可认为i是无序数组的起始位置。

当从尾开始遍历数组,首次出现num[i] < num[j] && i > j时,即可认为i是无序数组的结束位置。

代码


/**
 * @param {number[]} nums
 * @return {number}
 */
var findUnsortedSubarray = function(nums) {
    const getStartIndex = () => {
      let start = 0
      let end = 0
      while (nums[start] <= nums[end] && start <= nums.length - 1) {
        end += 1
        if (end >= nums.length) {
          start += 1
          end = start
        }
      }
      return start
    }
    const getEndIndex = () => {
      let start = nums.length - 1
      let end = nums.length - 1
      while (nums[start] >= nums[end] && start >= 0) {
        end -= 1
        if (end < 0) {
          start -= 1
          end = start
        }
      }
      return start
    }
    let start = getStartIndex()
    let end = getEndIndex()
    if (end < 0) return 0
    return end - start + 1
};

415. 字符串相加

原题

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。

注意:

  • num1 和num2 的长度都小于 5100.
  • num1 和num2 都只包含数字 0-9.
  • num1 和num2 都不包含任何前导零。
  • 你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式。

思路

思路同66题类似,遍历字符串,人肉模拟加法的运算过程。在遍历结束后,判断是否仍有进位,如果有需要在字符串的顶部添加1。

代码


/**
 * @param {string} num1
 * @param {string} num2
 * @return {string}
 */
var addStrings = function(num1, num2) {
    let sum = ''

    const getSum = (...args) => {
      const sum = [...args].reduce((prev, curr) => Number(prev) + Number(curr))
      return {
        result: sum >= 10 ? sum - 10 + '' : sum + '',
        carry: sum >= 10 ? '1' : '0'
      }
    }
    
    const addZero = (num, len) => {
      for (let i = 0; i < len; i++) {
        num = '0' + num
      }
      return num
    }

    if (num1.length > num2.length) {
      num2 = addZero(num2, num1.length - num2.length)
    } else if (num1.length < num2.length) {
      num1 = addZero(num1, num2.length - num1.length)
    }

    const len = num1.length

    let carry = 0

    for (let i = len - 1; i >= 0; i--) {
      const m = num1[i]
      const n = num2[i]
      const { result, carry: _carry } = getSum(m, n, carry)
      carry = _carry
      sum = result + sum
    }

    if (carry !== '0') {
      sum = '1' + sum
    } 

    return sum
};