前言
一些水题, 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 行。

在杨辉三角中,每个数是它左上方和右上方的数的和。
示例:
输入: 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`,等于2。
1
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个房子,如果刷成绿色,最小成本等于,当前房子绿色的成本 + 前一个房子如果刷成红色或者蓝色时的状态时的总成本
可得如下状态转移方程

代码
/**
* @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
};