问:
- 剑指 Offer 35. 复杂链表的复制
- 剑指 Offer 36. 二叉搜索树与双向链表
- 剑指 Offer 38. 字符串的排列
- 剑指 Offer 40. 最小的k个数
- 剑指 Offer 41. 数据流中的中位数 解:
- 遍历链表,解决next指向问题:复制当前节点,将当前节点指向复制节点,复制节点指向原先节点的next。这样就形成了A->A'->B->B'->C->C'。的链表;然后再重头遍历一遍,解决random指针的问题:假设A的random指向C。那A'的random就应该指向C',由第一步形成的链表结构可以看出A'的random应该指向A的random的next;最后一步,分离链表,将链表中每个节点指向下下个节点即可。
const copyRandomList = function(head) {
let curNode = head
while (curNode) {
const temp = curNode.next
const copyNode = new Node(curNode.val)
curNode.next = copyNode
copyNode.next = temp
curNode = temp
}
curNode = head
while (curNode) {
curNode.next.random = curNode.random ? curNode.random.next : null
curNode = curNode.next.next
}
curNode = head
let res = null
while (curNode) {
const temp = curNode.next
if (!res) res = temp
curNode.next = curNode.next ? curNode.next.next : null
curNode = temp
}
return res
};
- 二叉搜索树形成有序双向需要两个信息,左子树的最大节点和右子树的最小节点。后序遍历,得到左树信息和右树信息后加工出自己的信息返回。
const treeToDoublyList = function(root) {
if (!root) return null
function getRes(node) {
if (!node) return {
min: null,
max: null
}
const leftInfo = getRes(node.left)
const rightInfo = getRes(node.right)
if (leftInfo.max) {
leftInfo.max.right = node
node.left = leftInfo.max
}
if (rightInfo.min) {
node.right = rightInfo.min
rightInfo.min.left = node
}
return {
min: leftInfo.min ?? node,
max: rightInfo.max ?? node
}
}
const { min, max } = getRes(root)
max.right = min
min.left = max
return min
};
- 假设来到了i位置,前面的字符已经固定了,i ~ n上每一个字符是可以互换位置的,所以遍历i ~ n上每一个字符,尝试把每一个字符都和i位置字符交换,然后递归看i+1的情况。尝试完毕将位置换回来。
const permutation = function(str) {
let res = []
function getRes(idx, preStr) {
if (idx === str.length) {
res.push(preStr)
return
}
for (let i = idx; i < preStr.length; i++) {
str = swap(str, i, idx)
getRes(idx + 1, str);
str = swap(str, i, idx)
}
}
getRes(0, str)
function swap(str, i, j) {
str = str.split('')
const temp = str[j]
str[j] = str[i]
str[i] = temp
return str.join('')
}
res = [...new Set(res)]
return res
};
- 基于快排
const getLeastNumbers = function(arr, k) {
function getRes(left, right) {
if (left >= right) return
const target = arr[Math.floor((Math.random() * (right - left) + left))]
const { smallIdx, bigIdx } = partition(left, right, target)
if (k > smallIdx && k <= bigIdx) {
return arr.slice(0, k)
}
if (k <= smallIdx) {
return getRes(left, smallIdx)
} else {
return getRes(bigIdx, right)
}
}
return getRes(0, arr.length - 1)
function partition(left, right, target) {
let smallIdx = left - 1
let bigIdx = right + 1
let pos = left
while (pos < bigIdx) {
if (arr[pos] < target) {
[arr[smallIdx + 1], arr[pos]] = [arr[pos], arr[smallIdx + 1]]
smallIdx++
pos++
} else if (arr[pos] === target) {
pos++
} else {
[arr[bigIdx - 1], arr[pos]] = [arr[pos], arr[bigIdx - 1]]
bigIdx--
}
}
return {
smallIdx,
bigIdx
}
}
};
- 创建一个大根堆和一个小根堆。当放入的数大于大根堆堆顶时,就把这个数放入小根堆。反之放入大根堆。当放入一个数后,两个堆的长度差达到了2。就把更多的堆的堆顶匀给另一个堆,保持数量相对平衡。这样,在任何时候,大根堆的堆顶和小根堆的堆顶将决定中位数是多少。
class MedianFinder {
public bigHeap: number[]
public smallHeap: number[]
constructor() {
this.bigHeap = []
this.smallHeap = []
}
addNum(num: number): void {
if (!this.bigHeap.length || num < this.bigHeap[0]) {
this.heapInset(num, this.bigHeap, true)
if (this.bigHeap.length - this.smallHeap.length > 1) {
[this.bigHeap[this.bigHeap.length - 1], this.bigHeap[0]] = [this.bigHeap[0], this.bigHeap[this.bigHeap.length - 1]]
this.heapInset(this.bigHeap.pop(), this.smallHeap, false)
this.heapFy(0, this.bigHeap, true)
}
} else {
this.heapInset(num, this.smallHeap, false)
if (this.smallHeap.length - this.bigHeap.length > 1) {
[this.smallHeap[this.smallHeap.length - 1], this.smallHeap[0]] = [this.smallHeap[0], this.smallHeap[this.smallHeap.length - 1]]
this.heapInset(this.smallHeap.pop(), this.bigHeap, true)
this.heapFy(0, this.smallHeap, false)
}
}
}
findMedian(): number {
if (this.bigHeap.length === this.smallHeap.length) return (this.bigHeap[0] + this.smallHeap[0]) / 2
if (this.bigHeap.length > this.smallHeap.length) return this.bigHeap[0]
return this.smallHeap[0]
}
heapInset(num: number, heap: number[], type: boolean) : void {
heap.push(num)
let curIdx = heap.length - 1
while(type ? (heap[curIdx] > heap[Math.floor((curIdx - 1) / 2)]) : heap[curIdx] < heap[Math.floor((curIdx - 1) / 2)]) {
[heap[curIdx], heap[Math.floor((curIdx - 1) / 2)]] = [heap[Math.floor((curIdx - 1) / 2)], heap[curIdx]];
curIdx = Math.floor((curIdx - 1) / 2)
}
}
heapFy(idx: number, heap: number[], type: boolean) : void {
let leftIdx = 2 * idx + 1
let rightIdx = leftIdx + 1
let resIdx = idx
if (leftIdx < heap.length && (type ? heap[leftIdx] > heap[resIdx] : heap[leftIdx] < heap[resIdx])) resIdx = leftIdx
if (rightIdx < heap.length && (type ? heap[rightIdx] > heap[resIdx] : heap[rightIdx] < heap[resIdx])) resIdx = rightIdx;
if (resIdx !== idx) {
[heap[idx], heap[resIdx]] = [heap[resIdx], heap[idx]]
this.heapFy(resIdx, heap, type)
}
}
}