本文仅供自己复习使用
以下的题目均采用多解法方式
1、剑指 Offer 03. 数组中重复的数字
a、用集合set
用集合应该是最好想到的一个方法
/**
* @param {number[]} nums
* @return {number}
*/
var findRepeatNumber = function(nums) {
let set = new Set()
for(let i = 0; i < nums.length; i++) {
if(set.has(nums[i])) {
return nums[i]
} else {
set.add(nums[i])
}
}
};
b、原地放置
这个的话就是因为下标和值刚好能对应上,所以就需要通过交换两个值来看是否有相同的
/**
* @param {number[]} nums
* @return {number}
*/
var findRepeatNumber = function(nums) {
for(let i = 0; i < nums.length; i++) {
while(nums[i] !== i) {
if(nums[nums[i]] !== nums[i]) {
// 这里都要用temp
let temp = nums[i]
// 毕竟这一步更改了nums[i]
nums[i] = nums[temp]
// 这里就拿不到原来的nums[i]
nums[temp] = temp
} else {
return nums[i]
}
}
}
};
c、排序
/**
* @param {number[]} nums
* @return {number}
*/
var findRepeatNumber = function(nums) {
nums = nums.sort()
let pre = nums[0]
for(let i = 1; i < nums.length; i++) {
if(pre === nums[i]) {
return pre
}
pre = nums[i]
}
};
2、剑指 Offer 04. 二维数组中的查找
a、使用js的Array.prototype.flat
通过flat方法,将多维数组变成一维,然后通过indexOf找
/**
* @param {number[][]} matrix
* @param {number} target
* @return {boolean}
*/
var findNumberIn2DArray = function(matrix, target) {
return matrix.flat().indexOf(target) !== -1
};
b、通过遍历找
不多说,就是暴力
/**
* @param {number[][]} matrix
* @param {number} target
* @return {boolean}
*/
var findNumberIn2DArray = function(matrix, target) {
for(let i = 0; i < matrix.length; i++) {
if(matrix[i].indexOf(target) !== -1) {
return true
}
}
return false
};
c、找到合适的位置开始改变行和列
这道题有一个位置很重要,也就是左下角,如果目标比左下角大就可以列+1,如果目标比左下角小就是行-1
/**
* @param {number[][]} matrix
* @param {number} target
* @return {boolean}
*/
var findNumberIn2DArray = function(matrix, target) {
let row = matrix.length - 1, col = 0
while(row >= 0 && col < matrix[0].length) {
if(matrix[row][col] < target) {
col++
} else if(matrix[row][col] > target) {
row--
} else {
return true
}
}
return false
};
3、剑指 Offer 05. 替换空格
a、正则表达直接替换
/**
* @param {string} s
* @return {string}
*/
var replaceSpace = function(s) {
return s.replace(/ /g, '%20')
};
b、先切分,再合并
/**
* @param {string} s
* @return {string}
*/
var replaceSpace = function(s) {
return s.split(' ').join('%20')
};
c、暴力循环
/**
* @param {string} s
* @return {string}
*/
var replaceSpace = function(s) {
let res = ''
for(let i = 0; i < s.length; i++) {
if(s[i] === ' ') {
res += '%20'
} else {
res += s[i]
}
}
return res
};
4、剑指 Offer 06. 从尾到头打印链表
a、插队的队列
这个很好想到
/**
* @param {ListNode} head
* @return {number[]}
*/
var reversePrint = function(head) {
let res = []
while(head !== null) {
res.unshift(head.val)
head = head.next
}
return res
};
b、回溯
说白了就是递归,先弄到最里面然后再出来
这里要明确,进入到最里面后返回值是[],然后通过给这个数组逐步的加数据
/**
* @param {ListNode} head
* @return {number[]}
*/
var reversePrint = function(head) {
let find = function(head) {
if(head === null) {
return []
}
let res = find(head.next)
res.push(head.val)
return res
}
return find(head)
};
5、剑指 Offer 07. 重建二叉树
a、递归
这个很好想,其实就是之前做过的,详见算法总结
/**
* @param {number[]} preorder
* @param {number[]} inorder
* @return {TreeNode}
*/
var buildTree = function(preorder, inorder) {
let getTree = function(preorder, inorder) {
if(preorder.length === 0 || inorder.length === 0) {
return null
}
let root = preorder.shift()
let index = inorder.indexOf(root)
let tree = new TreeNode(root)
let left = getTree(preorder, inorder.slice(0, index))
let right = getTree(preorder, inorder.slice(index + 1))
tree.left = left
tree.right = right
return tree
}
return getTree(preorder, inorder)
};
6、剑指 Offer 09. 用两个栈实现队列
这道题不难,问题就在于读懂题目,因为这道题的题意是用两个后进先出的栈实现先进先出的队列
var CQueue = function() {
this.stack1 = []
this.stack2 = []
};
/**
* @param {number} value
* @return {void}
*/
CQueue.prototype.appendTail = function(value) {
this.stack1.push(value)
};
/**
* @return {number}
*/
CQueue.prototype.deleteHead = function() {
if(this.stack2.length !== 0) {
return this.stack2.pop()
}
while(this.stack1.length > 0) {
this.stack2.push(this.stack1.pop())
}
return this.stack2.length !== 0 ? this.stack2.pop() : -1
};
/**
* Your CQueue object will be instantiated and called as such:
* var obj = new CQueue()
* obj.appendTail(value)
* var param_2 = obj.deleteHead()
*/
7、剑指 Offer 10- I. 斐波那契数列
斐波拉契其实最简单能想到的就是递归,但是递归的话会超时,所以这里将前两个存起来(相当于dp了)
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
let pre = 0, after = 1
if(n < 2) {
return n
}
let res = 0
for(let i = 2; i <= n; i++) {
res = (pre + after) % (1e9 + 7)
let temp = after
after = res
pre = temp
}
return res
};
8、剑指 Offer 10- II. 青蛙跳台阶问题
动态规划,不多说
/**
* @param {number} n
* @return {number}
*/
var numWays = function(n) {
let dp = new Array(n + 1).fill(0)
dp[0] = 1, dp[1] = 1
for(let i = 2; i < n + 1; i++) {
dp[i] = (dp[i - 1] + dp[i - 2]) % (1e9 + 7)
}
return dp[n]
};
9、剑指 Offer 11. 旋转数组的最小数字
a、类似单调栈,找最突兀的
毕竟是把递增序列的前几个放最后面,那么最小的一定比前一个元素小(有点类似单调栈)
/**
* @param {number[]} numbers
* @return {number}
*/
var minArray = function(numbers) {
for(let i = numbers.length - 1; i > 0; i--) {
if(numbers[i - 1] > numbers[i]) {
return numbers[i]
}
}
return numbers[0]
};
b、二分查找
这里要明确,因为原本是单调递增的数组,所以哪怕逆转了后,也是局部递增的,也就是说结果一般都出现在最小的那个二分数组的左边,所以我们比较right,也移动right,以免漏掉第一个
/**
* @param {number[]} numbers
* @return {number}
*/
var minArray = function(numbers) {
let left = 0
let right = numbers.length - 1
// 要找某个值,二分查找就不需要等号
// 找某个范围,二分查找就需要等号
while(left < right) {
let middle = Math.floor((left + right) / 2)
// 这里其实没有拿left跟middle比较(因为要么即全拿left比较,要么全拿right比较)
if(numbers[right] > numbers[middle]) {
right = middle
} else if(numbers[right] < numbers[middle]) {
left = middle + 1
} else {
// 这里是重点,只能用right--,毕竟用的是right比较
right--
}
}
// 这里使用left或者right都行,毕竟此时left===right
return numbers[right]
};
10、剑指 Offer 12. 矩阵中的路径
/**
* @param {character[][]} board
* @param {string} word
* @return {boolean}
*/
var exist = function(board, word) {
// 表示现在从第几行,第几列,找第几个数
let dfs = function(row, col, num) {
// 如果要找的数找完了就返回true
if(num >= word.length) {
return true
}
// 如果下标越界,那也不是
if(row < 0 || row >= board.length || col < 0 || col >= board[0].length) {
return false
}
// 如果这个数不是或已经被访问过那就返回false
if(board[row][col] !== word[num] || board[row][col] === '') {
return false
}
// 把这个值标记成已被访问过
let temp = board[row][col]
board[row][col] = ''
// 这里有四种情况,上,下,左,右(任意一个成功即可)
let next = dfs(row - 1, col, num + 1) || dfs(row + 1, col, num + 1) || dfs(row, col - 1, num + 1) || dfs(row, col + 1, num + 1)
// 这里要还原(毕竟可能还需要再递归一次(当最开始传入的i和j不同时))
board[row][col] = temp
return next
}
for(let i = 0; i < board.length; i++) {
for(let j = 0; j < board[0].length; j++) {
if(dfs(i, j, 0)) {
return true
}
}
}
return false
};