算法学习记录(六十二)

89 阅读2分钟

问:

  1. 剑指 Offer 35. 复杂链表的复制
  2. 剑指 Offer 36. 二叉搜索树与双向链表
  3. 剑指 Offer 38. 字符串的排列
  4. 剑指 Offer 40. 最小的k个数
  5. 剑指 Offer 41. 数据流中的中位数 解:
  6. 遍历链表,解决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
};
  1. 二叉搜索树形成有序双向需要两个信息,左子树的最大节点和右子树的最小节点。后序遍历,得到左树信息和右树信息后加工出自己的信息返回。
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
};
  1. 假设来到了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
};
  1. 基于快排
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
        }
    }
};
  1. 创建一个大根堆和一个小根堆。当放入的数大于大根堆堆顶时,就把这个数放入小根堆。反之放入大根堆。当放入一个数后,两个堆的长度差达到了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)
        }
    }
}