刷通剑指offer~
1.剑指 Offer 03. 数组中重复的数字
输入:
[2, 3, 1, 0, 2, 5, 3]
输出: 2 或 3
首先思考解题思路,可以暴力,两层for循环,也可以用js的api:nums.indexOf(),但时间复杂度都太高了
思考优化,想到数组中用空间换时间的方法set和map,最后使用set做,时间复杂度O(n)
2. 剑指 Offer 04. 二维数组中的查找
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
首先思考暴力,二维数组,两个for循环可做,思考优化,因为是有序的,而且是查找,所以想到二分查找,时间复杂度O(log2n)。二分需要找到一个中间值,然后判断这个中间值是大还是小,改变中间值,于是将右上角的值作为中间值,如果target比他大就向左,小就向下。
var findNumberIn2DArray = function(matrix, target) {
if(!matrix ||!matrix.length || !matrix[0].length) return false
let m = matrix.length
let n = matrix[0].length
let row = 0, col = n-1
while(row<m && col>=0) {
let midVal = matrix[row][col]
if(midVal === target) return true
if(midVal < target) row++
else if(midVal > target) col--
}
return false
};
3.剑指 Offer 05. 替换空格
输入: s = "We are happy."
输出: "We%20are%20happy."
这题看上去感觉可以模拟,脑子里是怎么替换空格的:找到空格,把%20插进去。开始动手,如果可以开辟一个额外的空间的话,先拿一个指针指向原数组,然后在新数组里同步添加原数组字符,当遇到空格的时候,就把%20添加进去。
var replaceSpace = function(s) {
let temp = []
for(let i=0;i<s.length;i++) {
if(s[i]===' ') {
temp.push('%')
temp.push('2')
temp.push('0')
//也可以直接temp.push('%20')
} else {
temp.push(s[i])
}
}
return temp.join("")
};
4.剑指 Offer 06. 从尾到头打印链表
输入: head = [1,3,2]
输出: [2,3,1]
顺便复习一下怎么用js创建一个链表:
function ListNode(val) {
this.val = val
this.next = null
}
let head = new ListNode(1)
head.next = new ListNode(3)
head.next.next = new ListNode(2)
//目前掌握的方法,以后学到了的话继续补充
考虑用数组记录下每个节点的值,然后逆序返回,时间复杂度O(n)
var reversePrint = function(head) {
let temp = []
while(head) {
temp.push(head.val)
head = head.next
}
return temp.reverse()
}
5.剑指 Offer 07. 重建二叉树(重要,对下标思考很麻烦,得重复做)
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
Input: preorder = [-1], inorder = [-1]
Output: [-1]
题目为由前序和中序遍历重新构建二叉树,返回的是一棵二叉树。思路是前序遍历,第一个一定是根节点,然后在中序遍历中找,根节点左边的是左子树,右边的是右子树,分治之后递归的去找。注意最终要构建一棵树,而不是返回一个数组,所以我们在寻找的同时还要把树给构建出来。为了便于查找,先用一个字典把中序遍历的值的下标给记录下来,之后再进行递归。
var buildTree = function(preorder, inorder) {
const dic = new Map()
for(let i=0; i<inorder.length;i++) {
dic.set(inorder[i],i)
}
return recur(0,0,inorder.length-1)
function recur (root, left, right) {
if(left > right) return null
const node = new TreeNode(preorder[root])
let i = dic.get(preorder[root])
node.left = recur(root+1, left, i-1)
node.right = recur(root + i - left + 1, i+1,right)
return node
}
};
6.剑指 Offer 09. 用两个栈实现队列
输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
栈是给定的,不需要自己去设计,这道题的思路是用两个先进后出的栈实现一个先进先出的队列。想象面前有两个桶,家长为了让你学习,把游戏机放在最底下,然后把语文书,数学书,英语书依次往上放,然后告诉你只允许看最上面的书,要是回来发现里面的东西顺序变了,就说明你玩了游戏,你就完蛋了。那我们该如何玩到游戏机呢?我们从上到下一本一本书的顺序放到另一个桶中,这样另一个桶中的书就是倒序的,直到把游戏机拿出来,然后再一本书一本书的放回第一个桶中,哎,这下我们既玩到了游戏,又没有破坏顺序(然后发现你妈在窗户那看完了全过程,边夸你聪明边拿出了鸡毛掸子)。
var CQueue = function() {
this.inStack = []
this.outStack = []
};
/**
* @param {number} value
* @return {void}
*/
CQueue.prototype.appendTail = function(value) {
this.inStack.push(value)
};
/**
* @return {number}
*/
CQueue.prototype.deleteHead = function() {
if(!this.inStack.length && !this.outStack.length) return -1
if(this.outStack.length) return this.outStack.pop()
while(this.inStack.length) {
this.outStack.push(this.inStack.pop())
}
return this.outStack.pop()
};
7.剑指 Offer 10- I. 斐波那契数列(重复做,还有矩阵快速幂,打表等多种解法待补充)
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
输入: n = 2
输出: 1
输入: n = 5
输出: 5
假设没有学过动态规划,第一次看见这个题第一反应应该是递归,因为从小到大学这个都是递归思想,但是会超时,时间复杂度是o(f(n)),会随着规模增大而增大,指数级。
var fib = function(n) {
if(n === 0) return 0
if(n === 1) return 1
return (fib(n-1) + fib(n-2))
}
于是考虑记忆化搜索,用空间来换时间,把每次得到的结果用数组记录下来。
var fib = function(n) {
const dp = []
dp[0] = 0
dp[1] = 1
for(let i=2; i<=n;i++) {
dp[i] = (dp[i-1] + dp[i-2])%1000000007
}
return (dp[n])
}
不超时了,但空间复杂度太高了,有没有什么优化空间的方法呢?
想到其实没有必要每次都用数组记录下来,以前的值是可以不要的,只要保留前两个值就可以
var fib = function(n) {
if(n === 0) return 0
let a = 1
let b = 0
for(let i=1;i<n;i++) {
a = a+b
b = a-b
a %= 1000000007
}
return a
}
8.剑指 Offer 10- II. 青蛙跳台阶问题
输入: n = 2
输出: 2
输入: n = 7
输出: 21
和上一题思路一模一样,每次考虑上两次跳台阶方法之和
var numWays = function(n) {
if(n === 0 || n === 1) return 1
let a = 2
let b = 1
for(let i=2;i<n;i++) {
a = a+b
b = a-b
a %= 1000000007
}
return a
};