剑指offer 解题报告
前言
¶这两天奶奶做手术,我在医院里陪护,奶奶不会说普通话所以只好一直陪在旁边。实在有些闲得慌,正好在刷知乎的时候看到了《剑指offer》的推荐,搜了下发现有 OJ 收录了原书中的题目,于是开始尝试手机写代码23333(没有带电脑)。 强迫症晚期,一上手就非要做完不可,还好题目量少。使用低效率的方式做一件并不紧要的事情实在是太浪费生命了。(不过也有人说,生命就是用来浪费的2333
为了偷懒节约篇幅,将过于基础的题放在了 附录 中。
01 二维数组的查找
¶题意简述
¶有一个
题目简析
¶注意到矩阵中任意一个子矩阵仍然满足此特性,则可以二分子矩阵,直到子矩阵的大小为
- 若目标值恰好等于此位置上的值,则直接返回
true
; - 否则,若目标值小于此位置上的值,则其只可能存在于子矩阵
中; - 否则,其只可能存在于子矩阵
或子矩阵 中
程序实现
¶- 时间复杂度:
- 额外空间复杂度:
12345678910111213141516171819202122export function Find(target: number, array: number[][]): boolean { if (array.length <= 0) return false if (array[0].length <= 0) return false
const R = array.length const C = array[0].length return search(0, R, 0, C)
function search(lft1: number, rht1: number, lft2: number, rht2: number): boolean { if (lft1 >= R || lft2 >= C) return false if (lft1 > rht1 || lft2 > rht2) return false if (lft1 === rht1 && lft2 === rht2) return false
const x = (lft1 + rht1) >> 1 const y = (lft2 + rht2) >> 1 const z = array[x][y]
if (target === z) return true if (target < z) return search(lft1, x, lft2, y) return search(x + 1, rht1, lft2, rht2) || search(lft1, rht1, y + 1, rht2) }}
04 重建二叉树
¶题目简述
¶给定一棵
题目简析
¶先序遍历的第一个节点为根节点,不妨记为
之前所有的节点都为 的左子树中的节点;- 而
之后的所有节点都为 的右子树的节点;
又因为无论先序遍历还是后序遍历都是先访问完左子树再访问右子树的,所以先序遍历序列中前
程序实现
¶- 时间复杂度:
- 额外空间复杂度:
1234567891011121314151617181920212223242526interface TreeNode { val: number left: TreeNode | null right: TreeNode | null}
export function reConstructBinaryTree(pre: number[], vin: number[]): TreeNode { if (pre.length === 0 || pre.length !== vin.length) return null const tree = build(0, pre.length - 1, 0, vin.length - 1) return tree
function build(lft1: number, rht1: number, lft2: number, rht2: number): TreeNode { const x = pre[lft1]
// 找到 let p = lft2 for (; p <= rht2; ++p) if (vin[p] === x) break
const o: TreeNode = { val: x, left: lft2 < p ? build(lft1 + 1, lft1 + p - lft2, lft2, p - 1) : null, right: rht2 > p ? build(lft1 + p - lft2 + 1, rht1, p + 1, rht2) : null, } return o }}
06 旋转数组的最小数字
¶题目简述
¶有一个长度为
求
题目简析
¶先考虑简化版问题。若原数列严格递增,则存在两种情况:
,即 单调递增,此时 即为所求答案 ,即 先上升后突降再上升,取- 若
,则答案落在 中 - 否则,即
,则答案落在 中
- 若
原问题在简化版问题基础上的第二种情况种多了一种可能:
程序实现
¶- 时间复杂度:
(极端情况下可退化为 ) - 额外空间复杂度:
123456789101112131415161718192021222324export function minNumberInRotateArray(rotateArray: number[]): number { const A: number[] = rotateArray if (A.length <= 0) return 0
let lft = 0 let rht = A.length while (lft < rht) { const last: number = A[rht - 1] if (lft + 1 === rht || last > A[lft]) return A[lft]
const mid: number = (lft + rht) >> 1 const x: number = A[mid] if (mid + 1 === rht || x < last) { // 因为前面已经判断过区间首元素和尾元素,所以此时 // mid 不可能和 lft 相等 if (x < A[mid - 1]) return x
rht = mid } else if (x === last) rht -= 1 // 复杂度退化的情况 else lft = mid + 1 } return A[rht - 1]}
23 二叉搜索树的后序遍历序列
¶题目描述
¶判断给定的数组
题目简析
¶后续遍历中,最后一个元素为根节点,这启发我们逆序遍历
算法的核心思想是沿着树中最右侧的链遍历(访问过的节点可标记为删除)的方式去遍历整棵树,则每次要进入一个左子树时,里面元素的值不得超过当前遍最右侧链的最大值,否则其不为一个有效的二叉搜索树后序遍历结果。树上的最右侧链可以使用一个栈进行维护,不难发现其是一个严格递增的单调栈。每当树上某个节点的右子树访问完后,将其在栈中对应的元素从栈中移除,不难发现,移除操作总是弹出栈顶元素。
程序实现
¶- 时间复杂度:
- 额外空间复杂度:
123456789101112131415161718192021export function VerifySquenceOfBST(sequence: number[]): boolean { if (sequence.length <= 0) return false
const stack: number[] = [] let maxOfTree: number = Number.MAX_SAFE_INTEGER for (let i = sequence.length - 1; i > -1; --i) { const x = sequence[i] if (x >= maxOfTree) return false
// 栈顶元素为树中最大的节点,当遇到一个比栈顶元素小的节点时, // 说明此后不应该再遇到比栈顶元素大的元素(因为是后续遍历的逆序, // 所以我们会先访问完右子树再访问左子树),同时可以将栈中所以大于 // 它的元素一并删除(所以这还是个单调栈)。 while (stack.length > 0 && x < stack[stack.length - 1]) { maxOfTree = stack.pop() }
stack.push(x) } return true}
附录
¶#02 替换空格: 字符串替换
02.ts | 3 lines.123export function replaceSpace(text: string): string {return text.replace(/[ ]/, '%20')}#03 从尾到头打印链表: 栈的应用
03.ts | 15 lines.123456789101112131415interface ListNode {val: numbernext: ListNode | null}const stack: number[] = []export function printListFromTailToHead(head: ListNode): number[] {let tot = 0for (let o: ListNode = head; o; o = o.next) stack[tot++] = o.valconst result: number[] = new Array(tot)for (let i = 0; tot--; ++i) result[i] = stack[tot]return result}#05 用两个栈实现队列: 栈和队列的应用
05.ts | 16 lines.12345678910111213141516const s1: number[] = []const s2: number[] = []export function push(node: number): void {s1.push(node)}export function pop(): number | undefined {// 如果 s2 为空,则将栈 s1 中的内容弹出并压入栈 s2 中if (s2.length <= 0) {while (s1.length > 0) s2.push(s1.pop()!)}// 从 s2 中弹栈的值遵循原序列的先入先出规则return s2.pop()}#07 斐波那契数列: 递推
07.ts | 13 lines.12345678910111213export function Fibonacci(n: number): number {if (n <= 1) return nlet a = 0let b = 1let c = 1for (let i = 3; i <= n; ++i) {a = bb = cc = a + b}return c}#08 跳台阶: 递推
08.ts | 14 lines.1234567891011121314export function jumpFloor(n: number): number {if (n <= 0) return 0if (n <= 2) return nlet a = 1let b = 2let c = 3for (let i = 3; i < n; ++i) {a = bb = cc = a + b}return c}#09 跳台阶扩展问题: 递推
09.ts | 4 lines.1234export function jumpFloorII(n: number): number {if (n <= 0) return 0return 1 << (n - 1)}#10 跳台阶扩展问题: 递推
10.ts | 14 lines.1234567891011121314export function rectCover(n: number): number {if (n <= 0) return 0if (n <= 2) return nlet a = 1let b = 2let c = 3for (let i = 3; i < n; ++i) {a = bb = cc = a + b}return c}#11 二进制中 1 的个数: 数学,二进制
11.ts | 25 lines.12345678910111213141516171819202122232425// 循环判断export function NumberOf1(n: number): number {let result = 0for (n; n; n >>>= 1) {if (n & 1) result += 1}return result}// 使用 lowbitexport function NumberOf1_2(n: number): number {let result = 0for (n; n; n ^= n & -n) result += 1return result}// 使用偏移累加export function NumberOf1_3(n: number): number {let x = (n & 0x55555555) + ((n >> 1) & 0x55555555)x = (x & 0x33333333) + ((x >> 2) & 0x33333333)x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f)x = (x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff)x = (x & 0x0000ffff) + ((x >> 16) & 0x0000ffff)return x}#12 数值的整数次方: 数学,快速幂
12.ts | 10 lines.12345678910export function Power(base: number, exponent: number): number {if (base === 0) return 0let n = Math.abs(exponent)let result = 1for (let x = base; n > 0; n >>= 1, x = x * x) {if (n & 1) result *= x}return exponent < 0 ? 1 / result : result}#13 调整数组顺序使奇数位于偶数前面: 数学,二进制
13.ts | 9 lines.123456789export function reOrderArray(nums: number[]): number[] {const A: number[] = []const B: number[] = []for (const x of nums) {if (x & 1) A.push(x)else B.push(x)}return A.concat(B)}#14 链表中倒数最后K个节点: 链表,双指针
14.ts | 22 lines.12345678910111213141516171819202122interface ListNode {val: numbernext: ListNode | null}export function FindKthToTail(pHead: ListNode, k: number): ListNode | null {let faster: ListNode | null = pHeadlet slower: ListNode = pHead// 快指针先走 k 步for (let step = 0; step < k; ++step) {if (faster == null) return nullfaster = faster.next}// 两个指针一起走while (faster != null) {faster = faster.nextslower = slower.next!}return slower}#15 反转链表: 链表
15.ts | 19 lines.12345678910111213141516171819interface ListNode {val: numbernext: ListNode | null}export function ReverseList(pHead: ListNode): ListNode {if (pHead == null) return nulllet x: ListNode | null = pHeadlet y: ListNode | null = pHead.nextwhile (y != null) {const z = y.nexty.next = xx = yy = z}pHead.next = nullreturn x}#16 合并两个排序的链表: 归并排序
16.ts | 28 lines.12345678910111213141516171819202122232425262728interface ListNode {val: numbernext: ListNode | null}export function Merge(pHead1: ListNode, pHead2: ListNode): ListNode {const fakeRoot: ListNode = {val: -1,next: null,}let x: ListNode = pHead1let y: ListNode = pHead2let z: ListNode = fakeRootfor (; x != null && y != null; z = z.next) {if (x.val < y.val) {z.next = xx = x.next} else {z.next = yy = y.next}}if (x != null) z.next = xif (y != null) z.next = yreturn fakeRoot.next}#17 树的子结构: 二叉树,递归
17.ts | 25 lines.12345678910111213141516171819202122232425interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}export function HasSubtree(pRoot1: TreeNode, pRoot2: TreeNode): boolean {if (pRoot1 == null || pRoot2 == null) return falseif (isSubTree(pRoot1, pRoot2)) return trueif (pRoot1.left != null && HasSubtree(pRoot1.left, pRoot2)) return trueif (pRoot1.right != null && HasSubtree(pRoot1.right, pRoot2)) return truereturn false}// 检查 v 是否为 u 的子树,且它们根节点相同function isSubTree(u: TreeNode, v: TreeNode): boolean {if (u.val !== v.val) return falseif (v.left != null) {if (u.left == null || !isSubTree(u.left, v.left)) return false}if (v.right != null) {if (u.right == null || !isSubTree(u.right, v.right)) return false}return true}#18 二叉树的镜像: 二叉树,递归
18.ts | 15 lines.123456789101112131415interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}export function Mirror(pRoot: TreeNode): TreeNode {if (pRoot == null) return nullconst x = pRoot.leftpRoot.left = pRoot.rightpRoot.right = xMirror(pRoot.left)Mirror(pRoot.right)return pRoot}#19 顺时针打印矩阵: 模拟
19.ts | 49 lines.12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849export function printMatrix(matrix: number[][]): number[] {if (matrix.length <= 0) return []if (matrix[0].length <= 0) return []const R = matrix.lengthconst C = matrix[0].lengthconst result: number[] = []const totalRound = Math.min(R, C) >> 1for (let round = 0; round < totalRound; ++round) {right(round, round, C - round)down(C - round - 1, round + 1, R - round)left(R - round - 1, C - round - 2, round - 1)up(round, R - round - 2, round)}if (Math.min(R, C) & 1) {if (C > R) {right(totalRound, totalRound, C - totalRound)} else {down(totalRound, totalRound, R - totalRound)}}return resultfunction right(row: number, x: number, y: number): void {for (let i = x; i < y; ++i) {result.push(matrix[row][i])}}function down(col: number, x: number, y: number): void {for (let i = x; i < y; ++i) {result.push(matrix[i][col])}}function left(row: number, x: number, y: number): void {for (let i = x; i > y; --i) {result.push(matrix[row][i])}}function up(col: number, x: number, y: number): void {for (let i = x; i > y; --i) {result.push(matrix[i][col])}}}#20 包含min函数的栈: 单调栈
20.ts | 27 lines.123456789101112131415161718192021222324252627const nums: number[] = [] // 普通栈const stack: number[] = [] // 单调栈export function push(node: number): void {nums.push(node)// 允许放入和栈顶元素相等的元素是为了避免在弹出元素时做特判if (stack.length <= 0 || stack[stack.length - 1] >= node) {stack.push(node)}}export function pop(): void {const x = nums.pop()// 单调栈中栈顶元素最小,如果 nums 弹出的元素和单调栈中的栈顶元素相同,// 则单调栈中也同时弹出元素if (x != null && x === min()) stack.pop()}export function top(): number | undefined {return nums.length > 0 ? nums[nums.length - 1] : undefined}export function min(): number {return stack[stack.length - 1]}#21 栈的压入、弹出序列: 栈,模拟
根据
push
序列按顺序进行压栈,每次压完栈后根据pop
序列检查是否要弹栈21.ts | 17 lines.1234567891011121314151617export function IsPopOrder(pushV: number[], popV: number[]): boolean {const stack: number[] = []let i = 0let j = 0for (const x of pushV) {const y = popV[j]if (x === y) j += 1else stack[i++] = xwhile (i > 0) {if (stack[i - 1] !== popV[j]) breaki -= 1j += 1}}return i === 0}#22 从上往下打印二叉树: 二叉树,BFS
22.ts | 19 lines.12345678910111213141516171819interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}export function PrintFromTopToBottom(root: TreeNode): number[] {if (root == null) return []const Q: TreeNode[] = []const result: number[] = []Q.push(root)while (Q.length > 0) {const x = Q.shift()result.push(x.val)if (x.left) Q.push(x.left)if (x.right) Q.push(x.right)}return result}#24 二叉树中和为某一值的路径: 二叉树,DFS
24.ts | 28 lines.12345678910111213141516171819202122232425262728interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}export function FindPath(root: TreeNode, target: number): number[][] {if (root == null) return []const results: number[][] = []const tmp: number[] = []let total = 0f(root, 0)return resultsfunction f(o: TreeNode, cur: number): void {tmp[cur] = o.valtotal += o.valif (o.left == null && o.right == null) {if (total === target) {results.push(tmp.slice(0, cur + 1))}} else {if (o.left != null) f(o.left, cur + 1)if (o.right != null) f(o.right, cur + 1)}total -= o.val}}#25 复杂链表的复制: 链表,哈希表、Map
25.ts | 30 lines.123456789101112131415161718192021222324252627282930interface RandomListNode {label: numbernext: RandomListNode | nullrandom: RandomListNode | null}export function Clone(o: RandomListNode): RandomListNode {const map = new Map()const root = { ...o }for (let u: RandomListNode = root, v: RandomListNode = root;v != null;u = u.next!, v = v.next!) {map.set(v, u)if (v.next != null) {const w = map.get(v.next)if (w != null) {u.next = wbreak}u.next = { ...v.next }}}for (let u = root; u != null; u = u.next!) {const r = map.get(u.random)u.random = r === undefined ? null : r}return root}#26 二叉搜索树与双向链表: 二叉树,中序遍历
26.ts | 25 lines.12345678910111213141516171819202122232425interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}export function Convert(pRootOfTree: TreeNode): TreeNode {if (pRootOfTree == null) return nullf(null, pRootOfTree)let root = pRootOfTreewhile (root.left != null) root = root.leftreturn root}function f(p: TreeNode, o: TreeNode): TreeNode {if (o.left) p = f(p, o.left)if (p != null) p.right = oo.left = pp = oif (o.right) p = f(p, o.right)return p}#27 字符串的排列: 全排列
27.ts | 35 lines.1234567891011121314151617181920212223242526272829303132333435export function Permutation(str: string): string[] {if (str == null || str.length <= 0) return []const letters: string[] = str.split('').sort()const answers: string[] = []const _end = letters.length - 1while (true) {answers.push(letters.join(''))// 从右往左扫描找到第一个相邻且逆序的元素对 (i - 1, i)// 将第 i-1 个元素和最后一个元素交换,然后将 [i, N] 中的元素反转顺序let i = _endfor (; i > 0; --i) {if (letters[i] > letters[i - 1]) break}// 若不存在这样的 i,则结束循环if (i <= 0) breaklet x = letters[i - 1]let k = _endfor (; k >= i; --k) if (letters[k] > x) breakletters[i - 1] = letters[k]letters[k] = xconst L = i + _endconst mid = L >> 1for (let j = i; j <= mid; ++j) {x = letters[j]letters[j] = letters[L - j]letters[L - j] = x}}return answers}#28 数组中出现次数超过一半的数字: 中位数,随机测试,找第
大数排序
28.ts | 10 lines.12345678910/*** 排序并返回中位数** 时间复杂度: O(N \log N)* 额外空间复杂度: O(N)*/export function MoreThanHalfNum_Solution(nums: number[]): number {nums.sort((x, y) => x - y)return nums[nums.length >> 1]}随机测试
28_2.ts | 20 lines.1234567891011121314151617181920/*** 随机测试** 时间复杂度: O(N \log N)* 额外空间复杂度: O(1)*/export function MoreThanHalfNum_Solution_2(nums: number[]): number {let last: number = Number.NaNwhile (true) {const current = nums[Math.floor(Math.random() * nums.length)]if (last === current) continuelast = currentlet cnt = 0for (const x of nums) if (x === current) cnt += 1if (cnt * 2 >= nums.length) return current}}找第
大的数28_3.ts | 46 lines.12345678910111213141516171819202122232425262728293031323334353637383940414243444546/*** 快排思想找数组中第 k 大元素** 时间复杂度: O(N)* 额外空间复杂度: O(log N)*/export function MoreThanHalfNum_Solution_3(nums: number[]): number {return kth(0, nums.length - 1, (nums.length + 1) >> 1)function kth(lft: number, rht: number, k: number): number {if (lft === rht) return nums[lft]// 选取一个随机值防止极端数据下的复杂度退化const x = lft + 7 <= rht ? lft + Math.floor((rht - lft) * Math.random()) : (lft + rht) >> 1const pivot = nums[x]nums[x] = nums[lft]let i = lftlet j = rht + 1while (i < j) {for (j -= 1; i < j && nums[j] >= pivot; ) --jif (i === j) breaknums[i] = nums[j]for (i += 1; i < j && nums[i] <= pivot; ) ++iif (i === j) breaknums[j] = nums[i]}nums[i] = pivot// 优化,中间元素的边界尽可能延申,否则复杂度可能退化为 O(N^2)for (i -= 1; i >= lft; --i) if (nums[i] !== pivot) breakfor (j += 1; j <= rht; ++j) if (nums[j] !== pivot) break// 先检查左边的元素个数是否大于或等于 k 个let k2 = k - (i + 1 - lft)if (k2 <= 0) return kth(lft, i, k)// 再判断左边的元素个数+中间元素个数是否大于等于 k 个k2 -= j - i - 1if (k2 <= 0) return pivot// 否则,目标值再右边的元素列表中return kth(j, rht, k2)}}
#29 最小的K个数: 排序,小根堆,找第
的数小根堆
29.ts | 98 lines.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798/*** 大根堆** 时间复杂度: O(N \log N)* 额外空间复杂度: O(N)*/export function GetLeastNumbers_Solution(nums: number[], _k: number): number[] {if (_k <= 0) return []if (_k >= nums.length) return numsconst Q: PriorityQueue<number> = createPriorityQueue<number>((x, y) => y - x)for (let i = 0; i < _k; ++i) Q.enqueue(nums[i])for (let i = _k; i < nums.length; ++i) {const x = nums[i]if (Q.top() <= x) continueQ.dequeue()Q.enqueue(x)}return Q.collect()}interface PriorityQueue<T> {enqueue: (val: T) => voiddequeue: () => T | undefinedcollect: () => T[]top: () => T | undefinedsize: () => numberisEmpty: () => boolean}// 优先队列function createPriorityQueue<T>(cmp: (x: T, y: T) => -1 | 0 | 1 | number): PriorityQueue<T> {const _tree: T[] = [null]let _size: number = 0function enqueue(val: T): void {_tree[++_size] = val_up(_size)}function dequeue(): T | undefined {if (_size < 1) return undefinedconst target = _tree[1]_tree[1] = _tree[_size--]_down(1)return target}function top(): T | undefined {return _size > 0 ? _tree[1] : undefined}function collect(): T[] {return _tree.slice(1)}function _down(index: number): void {for (let i = index; i <= _size; ) {const lft = i << 1const rht = lft | 1if (lft > _size) breaklet q = lftif (rht <= _size && cmp(_tree[rht], _tree[q]) < 0) q += 1const x = _tree[q]if (cmp(_tree[i], x) <= 0) break_tree[q] = _tree[i]_tree[i] = xi = q}}function _up(index: number): void {for (let i = index; i > 1; ) {const q = i >> 1const x = _tree[q]if (cmp(x, _tree[i]) <= 0) break_tree[q] = _tree[i]_tree[i] = xi = q}}return {enqueue,dequeue,top,collect,size: () => _size,isEmpty: () => _size < 1,}}找第
大的数29_1.ts | 50 lines.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950/*** 快排思想找数组中第 k 大元素** 时间复杂度: O(N)* 额外空间复杂度: O(log N)*/export function GetLeastNumbers_Solution(nums: number[], _k: number): number[] {if (_k <= 0) return []if (_k >= nums.length) return numskth(0, nums.length - 1, _k)return nums.slice(0, _k)function kth(lft: number, rht: number, k: number): number {if (lft === rht) return nums[lft]// 选取一个随机值防止极端数据下的复杂度退化const x = lft + 7 <= rht ? lft + Math.floor((rht - lft) * Math.random()) : (lft + rht) >> 1const pivot = nums[x]nums[x] = nums[lft]let i = lftlet j = rht + 1while (i < j) {for (j -= 1; i < j && nums[j] >= pivot; ) --jif (i === j) breaknums[i] = nums[j]for (i += 1; i < j && nums[i] <= pivot; ) ++iif (i === j) breaknums[j] = nums[i]}nums[i] = pivot// 优化,中间元素的边界尽可能延申,否则复杂度可能退化为 O(N^2)for (i -= 1; i >= lft; --i) if (nums[i] !== pivot) breakfor (j += 1; j <= rht; ++j) if (nums[j] !== pivot) break// 先检查左边的元素个数是否大于或等于 k 个let k2 = k - (i + 1 - lft)if (k2 <= 0) return kth(lft, i, k)// 再判断左边的元素个数+中间元素个数是否大于等于 k 个k2 -= j - i - 1if (k2 <= 0) return pivot// 否则,目标值再右边的元素列表中return kth(j, rht, k2)}}
#30 连续子数组的最大和: 前缀和
30.ts | 13 lines.12345678910111213export function FindGreatestSumOfSubArray(nums: number[]): number {if (nums.length <= 0) return 0let answer = nums[0]let x = 0let y = nums[0]for (let i = 1; i < nums.length; ++i) {x = Math.min(x, y)y += nums[i]if (y - x > answer) answer = y - x}return answer}#31 整数中
出现的次数(从 到 整数中1出现的次数): 数学,排列组合因为十进制数位中每个位置上
的贡献度是彼此独立的,所以依次枚举每个位置,计算该位置为 所有可能情况数,累计求和即可。31.ts | 28 lines.12345678910111213141516171819202122232425262728const tenth: number[] = []for (let i = 0, t = 1; i < 18; ++i, t *= 10) {tenth.push(t)}export function NumberOf1Between1AndN_Solution(n: number): number {const nums: number[] = String(n).split('').map(x => Number(x))let result = 0for (let i = 0; i < nums.length; ++i) {result += toNum(0, i) * tenth[nums.length - i - 1]const x = nums[i]if (x < 1) continueelse if (x > 1) result += tenth[nums.length - i - 1]else if (x === 1) result += toNum(i + 1, nums.length) + 1}return resultfunction toNum(lft: number, rht: number): number {let result = 0for (let i = lft; i < rht; ++i) {result = result * 10 + nums[i]}return result}}#32 把数组排成最小的数: 排序,贪心
方便起见,将原数组中所有的数字转为字符串。考虑每个顺序下两个相邻的数字
和 ,若组成的字符串 字典序大于字符串 ,则交换它们的位置可以得到一个更优的结果,且不会影响完整字符串中其它部分的大小。32.ts | 9 lines.123456789export function PrintMinNumber(A: number[]): string {const result = A.map(x => String(x)).sort((x, y) => {if (x === y) return 0return x + y < y + x ? -1 : 1}).join('')return result}#33 丑数: 素因子,小根堆
每次取出小根堆中的根元素,然后放入其
的数值,直到取出了 个数为止。33.ts | 96 lines.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596export function GetUglyNumber_Solution(N: number): number {for (let i = answers.length; i <= N; ++i) {const c = answers[i - 1]while (!Q.isEmpty()) {const x = Q.dequeue()if (x <= c) continueanswers.push(x)Q.enqueue(x * 2)Q.enqueue(x * 3)Q.enqueue(x * 5)break}}return answers[N]}const answers: number[] = [0]const Q: PriorityQueue<number> = createPriorityQueue<number>((x, y) => x - y)Q.enqueue(1)interface PriorityQueue<T> {enqueue: (val: T) => voiddequeue: () => T | undefinedcollect: () => T[]top: () => T | undefinedsize: () => numberisEmpty: () => boolean}// 优先队列function createPriorityQueue<T>(cmp: (x: T, y: T) => -1 | 0 | 1 | number): PriorityQueue<T> {const _tree: T[] = [null]let _size: number = 0function enqueue(val: T): void {_tree[++_size] = val_up(_size)}function dequeue(): T | undefined {if (_size < 1) return undefinedconst target = _tree[1]_tree[1] = _tree[_size--]_down(1)return target}function top(): T | undefined {return _size > 0 ? _tree[1] : undefined}function collect(): T[] {return _tree.slice(1)}function _down(index: number): void {for (let i = index; i <= _size; ) {const lft = i << 1const rht = lft | 1if (lft > _size) breaklet q = lftif (rht <= _size && cmp(_tree[rht], _tree[q]) < 0) q += 1const x = _tree[q]if (cmp(_tree[i], x) <= 0) break_tree[q] = _tree[i]_tree[i] = xi = q}}function _up(index: number): void {for (let i = index; i > 1; ) {const q = i >> 1const x = _tree[q]if (cmp(x, _tree[i]) <= 0) break_tree[q] = _tree[i]_tree[i] = xi = q}}return {enqueue,dequeue,top,collect,size: () => _size,isEmpty: () => _size < 1,}}#34 第一个只出现一次的字符: 哈希表,Map
34.ts | 13 lines.12345678910111213export function FirstNotRepeatingChar(str: string): number {const duplicated: Record<string, boolean> = {}for (let i = 0; i < str.length; ++i) {const c = str[i]duplicated[c] = duplicated[c] === undefined ? false : true}for (let i = 0; i < str.length; ++i) {if (duplicated[str[i]]) continuereturn i}return -1}#35 数组中的逆序对: 逆序对,树状数组,映射
35.ts | 42 lines.123456789101112131415161718192021222324252627282930313233343536373839404142interface IBinaryIndexTree {add(x: number, v: number): voidsum(x: number): number}export function InversePairs(data: number[]): number {const _size = data.lengthconst idx: number[] = new Array(_size)for (let i = 0; i < _size; ++i) idx[i] = iidx.sort((i, j) => data[i] - data[j])// 映射const rnk: number[] = []for (let i = 0; i < _size; ++i) rnk[idx[i]] = i + 1let result = 0const bit = createBIT(_size)for (const x of rnk) {result += bit.sum(_size) - bit.sum(x)bit.add(x, 1)}return result % (1e9 + 7)}function createBIT(n: number): IBinaryIndexTree {const lowbit = (x: number): number => x & -xconst sumv: number[] = new Array(n + 1).fill(0)const add = (x: number, v: number): void => {for (let i = x; i <= n; i += lowbit(i)) sumv[i] += v}const sum = (x: number): number => {let answer = 0for (let i = x; i > 0; i -= lowbit(i)) answer += sumv[i]return answer}return { add, sum }}#36 两个链表的第一个公共节点: 双指针
先分别从两个链表的表达走到末尾,得到所需步数分别为
和 ,不妨设 ,即第一个链表比第二个长,先让快指针从第一个链表出发先走 步,再让慢指针从第二个链表出发和快指针一起前进,当慢指针和快指针相等时即到达第一个公共节点36.ts | 23 lines.1234567891011121314151617181920212223interface ListNode {val: numbernext: ListNode | null}export function FindFirstCommonNode(pHead1: ListNode, pHead2: ListNode): ListNode {let k1 = 0let k2 = 0for (let o = pHead1; o != null; o = o.next) k1 += 1for (let o = pHead2; o != null; o = o.next) k2 += 1const delta: number = Math.abs(k1 - k2)let faster: ListNode | null = k1 > k2 ? pHead1 : pHead2let slower: ListNode | null = k1 < k2 ? pHead1 : pHead2for (let i = 0; i < delta; ++i) faster = faster.nextwhile (faster !== slower) {faster = faster.nextslower = slower.next}return faster}- 37.ts | 14 lines.1234567891011121314export function GetNumberOfK(nums: number[], k: number): number {return lowerBound(k + 1) - lowerBound(k)function lowerBound(x: number): number {let lft = 0let rht = nums.lengthwhile (lft < rht) {const mid = (lft + rht) >> 1if (nums[mid] < x) lft = mid + 1else rht = mid}return lft}}
#38 二叉树的深度: 二叉树,DFS
38.ts | 10 lines.12345678910interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}export function TreeDepth(o: TreeNode | null): number {if (o == null) return 0return Math.max(TreeDepth(o.left), TreeDepth(o.right)) + 1}#39 平衡二叉树: 二叉树,DFS
39.ts | 22 lines.12345678910111213141516171819202122interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}export function IsBalanced_Solution(pRoot: TreeNode): boolean {return dfs(pRoot) !== -1function dfs(o: TreeNode): 0 | -1 | number {if (o == null) return 0const h1 = dfs(o.left)if (h1 === -1) return -1const h2 = dfs(o.right)if (h2 === -1) return -1const delta = Math.abs(h1 - h2)return delta <= 1 ? Math.max(h1, h2) + 1 : -1}}#40 数组中只出现一次的两个数字: 异或,分类
不妨记只出现一次的两个数字分别为
和 。先求出所有数字的异或结果 ,不难发现 。又因为 ,所以 。取出 二进制位中任意一位不为 的位 ,即 ,将原数组中所有的数字划分为第 为 和第 位为 两大类,不难发现,- 相同的数字会被分配到同一类中
和 会被分配到不同的类别中
则问题转换为【一个数组中,除一个元素只出现一次外其它元素均恰好出现两次,求这个只出现一次的元素】,这个问题直接异或就可以了。
40.ts | 14 lines.1234567891011121314export function FindNumsAppearOnce(nums: number[]): [number, number] {let s = 0for (const x of nums) s ^= xconst b = s & -slet x1 = 0let x2 = 0for (const x of nums) {if (x & b) x1 ^= xelse x2 ^= x}return [Math.min(x1, x2), Math.max(x1, x2)]}#41 和为
的连续正数序列: 前缀和41.ts | 17 lines.1234567891011121314151617export function FindContinuousSequence(sum: number): number[][] {if (sum <= 0) return []const results: number[][] = []let lft = 1let total = 1for (let i = 2, _end = ((sum + 1) >> 1) + 1; i < _end; ++i) {total += ifor (; total > sum; ++lft) total -= lftif (total === sum) {const answer: number[] = []for (let x = lft; x <= i; ++x) answer.push(x)results.push(answer)}}return results}#42 和为
的两个数字: 二分42.ts | 24 lines.123456789101112131415161718192021222324export function FindNumbersWithSum(nums: number[], sum: number): number[] {const _size = nums.lengthif (_size <= 1) return []let i = lowerBound(1, _size, sum - nums[0])if (i === _size) return []for (; i > 0; --i) {const x = nums[i]const y = sum - xconst j = lowerBound(0, i, y)if (j < i && nums[j] === y) return [y, x]}return []function lowerBound(lft: number, rht: number, x: number): number {while (lft < rht) {const mid = (lft + rht) >> 1if (nums[mid] < x) lft = mid + 1else rht = mid}return lft}}#43 左旋转字符串: 数组切片
43.ts | 7 lines.1234567export function LeftRotateString(str: string, n: number): string {if (str == null || str.length <= 0) return ''const _size = str.lengthconst k = ((n % _size) + _size) % _sizeif (k === 0) return strreturn str.slice(k) + str.slice(0, k)}#44 翻转单词序列: 字符串
44.ts | 3 lines.123export function ReverseSentence(s: string): string {return s.split(/\s+/g).reverse().join(' ')}#45 扑克牌顺子: 模拟,枚举
45.ts | 16 lines.12345678910111213141516export function IsContinuous(numbers: number[]): boolean {const nums = numbers.slice().sort((x, y) => x - y)let i = 0for (; i < nums.length; ++i) if (nums[i]) breakif (i + 1 >= nums.length) return truelet x = nums[i]for (let j = i + 1; j < nums.length; ++j) {const y = nums[j]if (x === y) return falsei -= y - x - 1if (i < 0) return falsex = y}return true}#46 孩子们的游戏(圆圈中最后剩下的数): 数学,约瑟夫环
46.ts | 8 lines.12345678export function LastRemaining_Solution(n: number, m: number): number {if (n === 0) return -1if (n === 1) return 0const x = LastRemaining_Solution(n - 1, m)// 以上一个位置为零号位置重新编号return (m + x) % n}#47 求
: 逻辑运算符,递归47.ts | 3 lines.123export function Sum_Solution(n: number): number {return n > 0 && Sum_Solution(n - 1) + n}#48 不用加减乘除做加法: 位运算,模拟
48.ts | 25 lines.12345678910111213141516171819202122232425export function Add(num1: number, num2: number): number {let result = 0for (; num1 > 0 && num2 > 0; ) {let a = num1 & -num1let b = num2 & -num2if (a <= b) {num1 ^= afor (; a & result; a <<= 1) result ^= aresult ^= a} else {num2 ^= bfor (; b & result; b <<= 1) result ^= bresult ^= b}}if (num2 > 0) num1 = num2for (; num1 > 0; ) {let a = num1 & -num1num1 ^= afor (; a & result; a <<= 1) result ^= aresult ^= a}return result}#49 把字符串转换成整数: 十进制,模拟
49.ts | 22 lines.12345678910111213141516171819202122export function StrToInt(str: string): number {if (str == null || str.length <= 0) return 0let flag: boolean = falselet result = 0let i = 0if (str[0] === '+') i += 1else if (str[0] === '-') {flag = truei += 1}if (i >= str.length) return 0const zero = '0'.codePointAt(0)for (; i < str.length; ++i) {const x = str.codePointAt(i) - zeroif (x < 0 || x > 9) return 0result = result * 10 + x}return flag && result ? -result : result}#50 数组中重复的数字: 判重
50.ts | 15 lines.123456789101112131415export function duplicate(nums: number[]): number {const _size: number = nums.lengthconst set: Set<number> = new Set()// 校验输入数据for (const x of nums) {if (x < 0 || x >= _size) return -1}for (const x of nums) {if (set.has(x)) return xset.add(x)}return -1}#51 构建乘积数组: 前缀积、后缀积
51.ts | 14 lines.1234567891011121314export function multiply(A: number[]): number[] {const _size = A.lengthconst s1: number[] = [A[0]]const s2: number[] = []s2[_size - 1] = A[_size - 1]for (let i = 1; i < _size; ++i) s1[i] = s1[i - 1] * A[i]for (let i = _size - 2; i >= 0; --i) s2[i] = s2[i + 1] * A[i]const B: number[] = [s2[1]]for (let i = 1; i + 1 < _size; ++i) B[i] = s1[i - 1] * s2[i + 1]B[_size - 1] = s1[_size - 2]return B}#52 正则表达式匹配: 字符串,匹配,递归
52.ts | 38 lines.1234567891011121314151617181920212223242526272829303132333435363738export function match(str: string, pattern: string): boolean {return f(0, 0)function f(i: number, j: number): boolean {if (i === str.length) {if ((pattern.length - j) & 1) return falsefor (j += 1; j < pattern.length; j += 2) {if (pattern[j] !== '*') return false}return true}if (j === pattern.length) return falseconst x = pattern[j]switch (x) {case '*': {return f(i, j + 1)}case '.': {if (j + 1 < pattern.length && pattern[j + 1] === '*') {for (; i <= str.length; ++i) {if (f(i, j + 2)) return true}return false}return f(i + 1, j + 1)}default: {if (j + 1 < pattern.length && pattern[j + 1] === '*') {if (f(i, j + 2)) return truefor (; i < str.length && str[i] === x; ++i) if (f(i + 1, j + 2)) return truereturn false}return str[i] === x && f(i + 1, j + 1)}}}}#53 表示数值的字符串: 字符串,匹配,正则表达式
53.ts | 3 lines.123export function isNumeric(str: string): boolean {return /^[+-]?(\.\d+|\d+(\.\d+)?)([eE][+-]?\d+)?$/.test(str)}#54 字符流中第一个不重复的字符: Set
54.ts | 24 lines.123456789101112131415161718192021222324const queue: string[] = []const set1: Set<string> = new Set<string>()const set2: Set<string> = new Set<string>()export function Init(): void {queue.length = 0set1.clear()set2.clear()}export function Insert(ch: string): void {if (set1.has(ch)) return void set2.add(ch)set1.add(ch)queue.push(ch)}export function FirstAppearingOnce(): string {while (queue.length > 0) {const x = queue[0]if (set2.has(x)) queue.shift()else return x}return '#'}#55 链表中环的入口结点: 链表,双指针
55.ts | 27 lines.123456789101112131415161718192021222324252627interface ListNode {val: numbernext: ListNode | null}export function EntryNodeOfLoop(pHead: ListNode): ListNode | null {if (pHead == null) return nulllet slower: ListNode | null = pHeadlet faster: ListNode | null = pHeaddo {faster = faster.nextif (faster == null) return nullfaster = faster.nextif (faster == null) return nullslower = slower.next} while (slower !== faster)slower = pHeadwhile (slower !== faster) {faster = faster.nextslower = slower.next}return slower}#56 删除链表中重复的节点: 链表,递归
56.ts | 19 lines.12345678910111213141516171819interface ListNode {val: numbernext: ListNode | null}export function deleteDuplication(o: ListNode | null): ListNode | null {if (o == null) return nulllet v = o.nextif (v == null) return oo.next = nullif (o.val === v.val) {for (v = v.next; v != null; v = v.next) if (o.val !== v.val) breakreturn deleteDuplication(v)}o.next = deleteDuplication(v)return o}#57 二叉树的下一个结点: 二叉树,中序遍历
中序遍历的下一个节点要么是第一个还没有遍历右子节点的祖先节点,要么是右子树中的最左叶子节点。
57.ts | 21 lines.123456789101112131415161718192021interface TreeLinkNode {val: numberleft: TreeLinkNode | nullright: TreeLinkNode | nullnext: TreeLinkNode | null}export function GetNext(o: TreeLinkNode): TreeLinkNode | null {if (o.right) {let v = o.rightwhile (v.left) v = v.leftreturn v}let v = o.nextwhile (v && v.right === o) {o = vv = v.next}return v}#58 对称的二叉树: 二叉树,BFS
58.ts | 43 lines.12345678910111213141516171819202122232425262728293031323334353637383940414243interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}interface Item {node: TreeNodestep: number}export function isSymmetrical(pRoot: TreeNode): boolean {if (pRoot == null) return trueconst Q: Item[] = []const nums: number[] = []let tot = 0let currentStep = 0Q.push({ node: pRoot, step: 0 })while (Q.length > 0) {const { node, step } = Q.shift()if (currentStep < step) {currentStep = stepfor (let i = 0, j = tot - 1; i < j; ++i, --j) {if (nums[i] !== nums[j]) return false}tot = 0}if (node == null) nums[tot++] = -Number.MAX_SAFE_INTEGERelse {nums[tot++] = node.valQ.push({ node: node.left, step: step + 1 })Q.push({ node: node.right, step: step + 1 })}}for (let i = 0, j = tot - 1; i < j; ++i, --j) {if (nums[i] !== nums[j]) return false}return true}#59 按之字形顺序打印二叉树: 二叉树,BFS
59.ts | 38 lines.1234567891011121314151617181920212223242526272829303132333435363738interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}interface Item {node: TreeNodestep: number}export function Print(o: TreeNode): number[][] {if (o == null) return []const Q: Item[] = []const results: number[][] = []const tmp: number[] = []let currentStep = 1let tot = 0Q.push({ node: o, step: 1 })while (Q.length > 0) {const { node, step } = Q.shift()if (currentStep < step) {const result: number[] = tmp.slice(0, tot)if (step & 1) result.reverse()results.push(result)tot = 0currentStep = step}tmp[tot++] = node.valif (node.left) Q.push({ node: node.left, step: step + 1 })if (node.right) Q.push({ node: node.right, step: step + 1 })}const result: number[] = tmp.slice(0, tot)if ((currentStep + 1) & 1) result.reverse()results.push(result)return results}#60 把二叉树打印成多行: 二叉树,BFS
60.ts | 38 lines.1234567891011121314151617181920212223242526272829303132333435363738interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}interface Item {node: TreeNodestep: number}export function Print(o: TreeNode): number[][] {if (o == null) return []const Q: Item[] = []const results: number[][] = []const tmp: number[] = []let currentStep = 1let tot = 0Q.push({ node: o, step: 1 })while (Q.length > 0) {const { node, step } = Q.shift()if (currentStep < step) {const result = tmp.slice(0, tot)results.push(result)tot = 0currentStep = step}tmp[tot++] = node.valif (node.left) Q.push({ node: node.left, step: step + 1 })if (node.right) Q.push({ node: node.right, step: step + 1 })}const result = tmp.slice(0, tot)results.push(result)return results}#61 序列化二叉树: 二叉树,重建二叉树
- 序列化:求出先序遍历和后序遍历,并将其转为字符串
- 反序列化:通过先序遍历和后序遍历结果重建树
61.ts | 55 lines.12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}export function Serialize(pRoot: TreeNode): string {const pre: number[] = []const vin: number[] = []if (pRoot != null) {preTraverse(pRoot)vinTraverse(pRoot)}return pre.join(',') + '#' + vin.join(',')function preTraverse(o: TreeNode | null): void {if (o == null) returnpre.push(o.val)preTraverse(o.left)preTraverse(o.right)}function vinTraverse(o): void {if (o == null) returnvinTraverse(o.left)vin.push(o.val)vinTraverse(o.right)}}export function Deserialize(s: string): TreeNode | null {if (s === '#') return nullconst [x, y] = s.split('#')const pre: number[] = x.split(',').map(n => Number(n))const vin: number[] = y.split(',').map(n => Number(n))if (pre.length <= 0 || pre.length !== vin.length) return nullconst tree = f(0, pre.length - 1, 0, vin.length - 1)return treefunction f(lft1: number, rht1: number, lft2: number, rht2: number): TreeNode {const t = pre[lft1]let x = lft2for (; x <= rht2; ++x) if (vin[x] === t) breakconst o = {val: t,left: lft2 < x ? f(lft1 + 1, lft1 + x - lft2, lft2, x - 1) : null,right: rht2 > x ? f(lft1 + x - lft2 + 1, rht1, x + 1, rht2) : null,}return o}}#62 二叉搜索树的第k个结点: 二叉树,二叉搜索树,中序遍历
62.ts | 28 lines.12345678910111213141516171819202122232425262728interface TreeNode {val: numberleft: TreeNode | nullright: TreeNode | null}export function KthNode(pRoot: TreeNode, k: number): TreeNode | null {let result = nullif (k <= 0) return resultf(pRoot)return resultfunction f(o: TreeNode): void {if (o == null) returnf(o.left)if (result != null) returnk -= 1if (k <= 0) {result = oreturn}f(o.right)}}#63 数据流中的中位数: 堆,中位数
维护一个大根堆和小根堆:
- 大根堆中存放当前数列中前
小的元素; - 小根堆中存放当前数列中后
大的元素;
则每次查询时,若元素个数为奇数,直接返回大根堆的根顶元素;否则,即元素个数为偶数,返回小根堆和大根堆的堆顶元素的平均值。
63.ts | 18 lines.123456789101112131415161718import { PriorityQueue } from '@algorithm.ts/queue'const lowerQ = new PriorityQueue<number>({ compare: (x, y) => y - x })const upperQ = new PriorityQueue<number>({ compare: (x, y) => x - y })export function Insert(num: number): void {if (lowerQ.size === upperQ.size) {upperQ.enqueue(num)lowerQ.enqueue(upperQ.dequeue()!)} else {lowerQ.enqueue(num)upperQ.enqueue(lowerQ.dequeue()!)}}export function GetMedian(): number {return lowerQ.size === upperQ.size ? (lowerQ.front()! + upperQ.front()!) / 2 : lowerQ.front()!}- 大根堆中存放当前数列中前
#64 滑动窗口的最大值: 滑动窗口,单调栈
64.ts | 19 lines.12345678910111213141516171819export function maxInWindows(nums: number[], size: number): number[] {if (nums.length < size || size <= 0) return []const stack: number[] = []for (let i = 0, _end = size - 1; i < _end; ++i) {const x = nums[i]while (stack.length > 0 && x >= nums[stack[stack.length - 1]]) stack.pop()stack.push(i)}const results: number[] = []for (let i = size - 1; i < nums.length; ++i) {const x = nums[i]if (stack.length > 0 && stack[0] + size === i) stack.shift()while (stack.length > 0 && x >= nums[stack[stack.length - 1]]) stack.pop()stack.push(i)results.push(nums[stack[0]])}return results}#65 矩阵中的路径: 模拟,矩阵
65.ts | 32 lines.1234567891011121314151617181920212223242526272829303132export function hasPath(matrix: string[][], word: string): boolean {if (word == null || word.length <= 0) return trueconst R = matrix.lengthif (R <= 0) return falseconst C = matrix[0].lengthif (C <= 0) return falseconst vis: boolean[][] = []for (let x = 0; x < R; ++x) vis[x] = new Array(C).fill(false)for (let x = 0; x < R; ++x) {for (let y = 0; y < C; ++y) {if (f(x, y, 0)) return true}}return falsefunction f(x: number, y: number, cur: number): boolean {if (x < 0 || x >= R || y < 0 || y >= C) return falseif (vis[x][y] || matrix[x][y] !== word[cur]) return falsecur += 1if (cur === word.length) return truevis[x][y] = trueconst flag = f(x - 1, y, cur) || f(x + 1, y, cur) || f(x, y - 1, cur) || f(x, y + 1, cur)vis[x][y] = falsereturn flag}}#66 机器人的运动范围: BFS
66.ts | 30 lines.123456789101112131415161718192021222324252627282930export function movingCount(threshold: number, rows: number, cols: number): number {const vis: boolean[][] = []for (let i = 0; i < rows; ++i) {vis[i] = new Array(cols).fill(false)}let result = 0f(0, 0)return resultfunction f(x: number, y: number): void {if (x < 0 || x >= rows || y < 0 || y >= cols) returnif (vis[x][y]) returnvis[x][y] = trueconst total = weight(x) + weight(y)if (total > threshold) returnresult += 1f(x - 1, y)f(x + 1, y)f(x, y - 1)f(x, y + 1)}function weight(x: number): number {let result = 0for (let i = x; i > 0; i = Math.round((i - (i % 10)) / 10)) result += i % 10return result}}#67 剪绳子: 数学
考虑剪下来的某段长为
的绳子,继续减它不会对其它段造成影响。- 若
,则必然有 ; - 当
时,有
这启发我们尽可能将绳子剪成长度为
的段,而当某一段长度为 时,则不再剪它。67.ts | 17 lines.1234567891011121314151617export function cutRope(n: number): number {if (n <= 0) return 0if (n <= 4) return nlet result = 1if (n % 3 === 1) {result = 4n -= 4} else if (n % 3 === 2) {result = 2n -= 2}for (; n > 0; n -= 3) {result *= 3}return result}- 若
Related
¶