问:
- 剑指 Offer 56 - I. 数组中数字出现的次数
- 剑指 Offer 57. 和为s的两个数字
- 剑指 Offer 57 - II. 和为s的连续正数序列
- 剑指 Offer 58 - I. 翻转单词顺序
- 剑指 Offer 59 - I. 滑动窗口的最大值
- 剑指 Offer 59 - II. 队列的最大值
- 剑指 Offer 60. n个骰子的点数 解:
- 整个数组异或一遍,可知最后剩下的是 eor = a ^ b, a,b不相等,所以eor不为0,由此可推断出eor的二进制中,必定至少存在一个1,我们假设它在X位上。也就是说 a 和 b的二进制在X位上必定不相等。那么,只要我们以X位的数作为分界,把数组分为两份,可知道a,b必定在不同区域。那么我们再设置一个变量eor2去异或其中的一份数组,可以得到eor2就是a或者b。最后用eor ^ eor2,其结果就是另外一个数
const singleNumbers = function(nums) {
let eor = 0
nums.forEach(i => eor ^= i)
const one = eor & (~eor + 1)
let eor2 = 0
nums.forEach((i) => {
if ((i & one) === 0) eor2 ^= i
})
return [eor2, eor ^ eor2]
};
- 双指针遍历
const twoSum = function(nums, target) {
let left = 0
let right = nums.length - 1
while (left < right) {
if (nums[left] + nums[right] < target) {
left++
} else if (nums[left] + nums[right] === target) {
return [nums[left], nums[right]]
} else {
right--
}
}
return []
};
- 滑动窗口,窗口中值小于target就继续扩张,大于就缩小,等于就结算一次然后继续扩张
const findContinuousSequence = function(target) {
let left = 1
let right = 1
const res = []
const sumArr = []
let sum = 0
while (right < target) {
if (sum === target) {
res.push([...sumArr])
}
if (sum <= target) {
sum += right
sumArr.push(right)
right++
} else {
sum -= left
sumArr.shift()
left++
}
}
return res
};
- 用join也可
const reverseWords = function(s) {
const strArr = s.trim().split(' ')
let res = ''
for (let i = strArr.length - 1; i >= 0; i--) {
if (strArr[i] === '') continue
res += i !== 0 ? strArr[i] + ' ' : strArr[i]
}
return res
};
- 双端队列,维持一个不递增的队列,前大后小。如果窗口左指针压中的值等于队列的头部,意味着左指针就是当前最大值,所以窗口右移时要移除队列的头部。如果窗口右移时左指针不是队列头部,意味着窗口内最大值与此时的左指针无关。
const maxSlidingWindow = function(arr, size) {
const queue = []
let leftIdx = 0
const res = []
for (let i = 0; i < arr.length; i++) {
while (queue.length && queue[queue.length -1] < arr[i]) {
queue.pop()
}
queue.push(arr[i])
// 若窗口尚未形成,跳过
if (i < size - 1) continue
// 窗口已经形成,放入结果数组
res.push(queue[0])
// 若窗口最左的值是最大的,那么移除这个值(因为滑动之后最左值不再有效)
if (arr[leftIdx] === queue[0]) queue.shift()
leftIdx++
}
return res
};
- 与上题思路差不多。一个队列正常存取值。另一个队列存值时维持单调不递增,pop时判断pop出来的值是否是队列头部,如果是的话那pop出来的是最大值,把单调队列的头部也移除。否则意味着pop出来的不是最大值,不用对单调队列进行操作。
class MaxQueue {
private queue: number[]
private maxQueue: number[]
constructor() {
this.queue = []
this.maxQueue = []
}
max_value(): number {
return this.maxQueue[0] ?? -1
}
push_back(value: number): void {
this.queue.push(value)
while (this.maxQueue.length && this.maxQueue[this.maxQueue.length - 1] < value) {
this.maxQueue.pop()
}
this.maxQueue.push(value)
}
pop_front(): number {
const res = this.queue.shift() ?? -1
if (this.maxQueue[0] === res) {
this.maxQueue.shift()
}
return res
}
}
- dp表含义为投i个骰子的时候,投出j的概率是多少。
const dicesProbability = function(n) {
const dp = []
for (let i = 1; i <= n; i++) {
dp[i] = []
}
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= 6 * i; j++) {
if (i === 1) {
dp[i][j] = j <= 6 ? 1 / 6 : 0
continue
}
// 斜率优化
dp[i][j] = (dp[i][j - 1] ?? 0) + ((dp[i - 1][j - 1] ?? 0) - (dp[i - 1][j - 7] ?? 0)) * (1 / 6)
}
}
return dp[n].slice(n)
};