栈(Stack)
栈是一种遵从后进先出(LIFO)原则的有序集合。新添加的或待删除的元素都保存在栈的 同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。
实现栈结构
const Stack = (() => {
const wm = new WeakMap()
class Stack {
constructor() {
wm.set(this, [])
this.top = 0
}
push(...nums) {
let list = wm.get(this)
nums.forEach(item => {
list[this.top++] = item
})
}
pop() {
let list = wm.get(this)
let last = list[--this.top]
list.length = this.top
return last
}
peek() {
let list = wm.get(this)
return list[this.top - 1]
}
clear() {
let list = wm.get(this)
list.length = 0
}
size() {
return this.top
}
output() {
return wm.get(this)
}
isEmpty() {
return wm.get(this).length === 0
}
}
return Stack
})()
let s = new Stack()
s.push(1, 2, 3, 4, 5)
console.log(s.output()) // [ 1, 2, 3, 4, 5 ]
利用栈十进制转化为其他进制
function divideBy2(decNumber, base = 2) {
let remStack = new Stack()
let rem
let binaryString = ''
let digits = '0123456789ABCDEF'
while (decNumber > 0) {
rem = Math.floor(decNumber % base)
remStack.push(rem)
decNumber = Math.floor(decNumber / base)
}
while (!remStack.isEmpty()) {
binaryString += digits[remStack.pop()].toString()
}
return binaryString
}
// 将十进制转换成其他进制
let num = 100345
num.toString(2) // "11000011111111001"
num.toString(8) // "303771"
console.log(divideBy2(num, 2)) // "11000011111111001"
console.log(divideBy2(num, 8)) // "303771"
队列
class Queue {
constructor(max) {
this.max = max
this.q = 0
this.p = 0
this.size = 0
this.data = Array(max)
}
enqueue(item) {
if (this.size === this.max) {
throw 'Queue Overflow'
}
this.data[this.p++] = item
this.size++
if (this.p === this.max) {
this.p = 0
}
}
dequeue() {
if (this.size === 0) {
throw 'Queue Underflow'
}
const item = this.data[this.q++]
this.size--
if (this.q === this.max) {
this.q = 0
}
return item
}
}
let queue = new Queue(3)
queue.enqueue(1)
queue.enqueue(2)
queue.enqueue(3)
console.log(queue.dequeue())
console.log(queue.dequeue())
console.log(queue.dequeue())
二叉树
二叉搜索树
class Node {
constructor(key) {
this.key = key
this.right = this.left = null
}
}
class BinarySearchTree {
constructor(list = []) {
this.root = null
if (list.length) {
this.root = new Node(list.shift())
list.forEach(item => this.insert(item))
}
}
insert(item) {
let newNode = new Node(item)
if (this.root == null) {
this.root = newNode
} else {
this._insertNode(this.root, newNode)
}
}
_insertNode(node, newNode) {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode
} else {
this._insertNode(node.left, newNode)
}
} else {
if (node.right === null) {
node.right = newNode
} else {
this._insertNode(node.right, newNode)
}
}
}
// 先序遍历 根 左 右
preOrderTraverseNode(node, callback) {
if (node instanceof Node) {
callback(node.key)
this.preOrderTraverseNode(node.left, callback)
this.preOrderTraverseNode(node.right, callback)
}
}
// 中序遍历 左 根 右
inOrderTraverseNode(node, callback) {
if (node instanceof Node) {
this.inOrderTraverseNode(node.left, callback)
callback(node.key)
this.inOrderTraverseNode(node.right, callback)
}
}
// 后序遍历 左 右 根
postOrderTraverseNode(node, callback) {
if (node instanceof Node) {
this.postOrderTraverseNode(node.left, callback)
this.postOrderTraverseNode(node.right, callback)
callback(node.key)
}
}
// 搜索最小值
min() {
return this._minNode(this.root)
}
_minNode(node) {
while (node instanceof Node && node.left) {
node = node.left
}
return node ? node.key : null
}
// 搜索最大值
max() {
return this._maxNode(this.root)
}
_maxNode(node) {
while (node instanceof Node && node.right) {
node = node.right
}
return node ? node.key : null
}
// 搜索特定值
search(key) {
return this._searchNode(this.root, key)
}
_searchNode(node, key) {
if (node instanceof Node) {
if (node.key === key) {
return true
}
if (key < node.key) {
return this._searchNode(node.left, key)
} else {
return this._searchNode(node.right, key)
}
}
return false
}
findMinNode(node) {
while (node && node.left !== null) {
node = node.left
}
return node
}
// 删除指定值
remove(key) {
return this._removeNode(this.root, key)
}
_removeNode(node, key) {
if (node === null) {
return null
}
if (key < node.key) {
// 根据当前node 去它的左子节点继续查找
node.left = this._removeNode(node.left, key)
return node
} else if (key > node.key) {
// 根据当前node 去它的右子节点继续查找
node.right = this._removeNode(node.right, key)
return node
} else {
// 找到了 删除节点
// 情况1:删除节点 没有子节点的情况下 直接删除
if (node.left === null && node.right === null) {
node = null
return node
}
// 情况2:删除节点 只有一个子节点
if (node.left === null) {
node = node.right
return node
} else if (node.right === null) {
node = node.left
return node
}
// 情况3:删除节点 有两个节点
// console.log(node)
// 查找右边最小节点
let min = this.findMinNode(node.right)
node.key = min.key
node.right = this._removeNode(node.right, min.key)
return node
}
}
}
const list = [10, 6, 20, 8, 12, 5, 22]
const tree = new BinarySearchTree(list)
tree.insert(17)
// console.log(tree)
tree.remove(20)
// console.log(tree.min())
// console.log(tree.max())
// 先序遍历
// tree.preOrderTraverseNode(tree.root, key => {
// console.log(key) // 10 6 5 8 20 12 17 22
// })
// 中序遍历
// tree.inOrderTraverseNode(tree.root, key => {
// console.log(key) // 5 6 10 12 17 20 22
// })
// 后序遍历
// tree.postOrderTraverseNode(tree.root, key => {
// console.log(key) // 5 8 6 17 12 22 20 10
// })
链表
class LinkNode {
constructor(key) {
this.key = key
this.next = null
}
}
class LinkedList {
constructor(list, seq = true) {
this.head = null
this.head = new LinkNode(list.shift())
if (seq) {
list.forEach(this.insert.bind(this))
} else {
list.forEach(this.insertReverse.bind(this))
}
}
// 正序插入
insert(key) {
if (!this.head) {
this.head = new LinkNode(key)
return
}
let node = this.head
while(node.next) {
node = node.next
}
node.next = new LinkNode(key)
}
// 倒序插入
insertReverse(key) {
let node = new LinkNode(key)
if (this.head) {
node.next = this.head
}
this.head = node
}
}
let list = [1, 2, 3, 4]
let link = new LinkedList(list, false) // (list: Array, seq: Boolean)
link.insertReverse(5)
link.insert(6)
// {"key":5,"next":{"key":4,"next":{"key":3,"next":{"key":2,"next":{"key":1,"next":{"key":6,"next":null}}}}}}
console.log(JSON.stringify(link.head))
反转链表
// 链表的反转
// 输入: 1->2->3->4->5->NULL
// 输出: 5->4->3->2->1->NULL
var reverseList = function(head) {
let cur = head
let prev = null
while(cur) {
// let temp = cur.next
// cur.next = prev
// prev = cur
// cur = temp
[cur.next, prev, cur] = [prev, cur, cur.next]
}
return prev
};
// {"value":5,"next":{"value":4,"next":{"value":3,"next":{"value":2,"next":{"value":1,"next":null}}}}}
console.log(JSON.stringify(reverseList(link.head)))
双向链表
class DoubleListNode {
constructor(key) {
this.key = key
this.prev = null
this.next = null
}
}
class DoubleLinkedList {
constructor(list) {
this.head = null
this.head = new DoubleListNode(list.shift())
list.forEach(this.insert.bind(this))
}
insert(key) {
let n = new DoubleListNode(key)
if (!this.head) {
this.head = n
}
let node = this.head
while(node.next) {
node = node.next
}
node.next = n
n.prev = node
}
delete(key) {
let node = this.head
while(node && node.key !== key) {
node = node.next
}
node.prev.next = node.next
node.next.prev = node.prev
node.next = null
node.prev = null
}
}
const list = [1, 2, 3, 4, 5, 6]
let doubleLink = new DoubleLinkedList(list)
doubleLink.delete(3)
console.log(doubleLink)
深度优先遍历
// 递归版本
function deepFirstSearch(node,nodeList) {
if (node) {
nodeList.push(node);
var children = node.children;
for (var i = 0; i < children.length; i++)
//每次递归的时候将 需要遍历的节点 和 节点所存储的数组传下去
deepFirstSearch(children[i],nodeList);
}
return nodeList;
}
// 非递归版本:
function deepFirstSearch(node) {
var nodes = [];
if (node != null) {
var stack = [];
stack.push(node);
while (stack.length != 0) {
var item = stack.pop();
nodes.push(item);
var children = item.children;
for (var i = children.length - 1; i >= 0; i--)
stack.push(children[i]);
}
}
return nodes;
}
广度优先遍历
function breadthFirstSearch(node) {
var nodes = [];
if (node != null) {
var queue = [];
queue.unshift(node);
while (queue.length != 0) {
var item = queue.shift();
nodes.push(item);
var children = item.children;
for (var i = 0; i < children.length; i++)
queue.push(children[i]);
}
}
return nodes;
}
LRU缓存机制
题目描述: 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则
插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
示例:
输入:["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出:[null, null, null, 1, null, -1, null, -1, 3, 4]
解释:
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
class LNode {
public key: number
public value: number
public next: LNode | null
public prev: LNode | null
constructor(key: number, value: number) {
this.key = key
this.value = value
this.next = null
this.prev = null
}
}
class LRUCache {
readonly capacity: number
private hashTable: Map<number, LNode>
private dummyHead: LNode
private dummyTail: LNode
constructor(capacity: number) {
this.capacity = capacity
this.hashTable = new Map()
this.dummyHead = new LNode(-1, -1)
this.dummyTail = new LNode(-1, -1)
this.dummyHead.next = this.dummyTail
this.dummyTail.prev = this.dummyHead
}
get(key: number) {
if (this.hashTable.has(key)) {
const node = this.hashTable.get(key) as LNode
this.moveHead(node)
return node.value
}
return -1
}
put(key: number, value: number) {
const node = this.hashTable.get(key) as LNode
if (node == null) {
const node = new LNode(key, value)
this.hashTable.set(key, node)
this.addHead(node)
if (this.hashTable.size > this.capacity) {
this.removeLRUItem()
}
} else {
node.value = value
// 用到这个node 要把最近使用移动到头部
this.moveHead(node)
}
}
moveHead(node: LNode) {
this.removeFromList(node)
this.addHead(node)
}
removeFromList(node: LNode) {
const prevNode = node.prev as LNode
const nextNode = node.next as LNode
prevNode.next = nextNode
nextNode.prev = prevNode
}
popTail() {
const tailNode = this.dummyTail.prev as LNode
this.removeFromList(tailNode)
return tailNode
}
removeLRUItem() {
const tail = this.popTail() as LNode
this.hashTable.delete(tail.key)
}
addHead(node: LNode) {
const tempHead = this.dummyHead
node.next = tempHead.next
node.prev = tempHead
;(tempHead.next as LNode).prev = node
tempHead.next = node
}
}
const lRUCache = new LRUCache(2)
console.log(lRUCache.put(1, 1)); // 缓存是 {1=1}
console.log(lRUCache.put(2, 2)); // 缓存是 {1=1, 2=2}
console.log(lRUCache.get(1)); // 返回 1
console.log(lRUCache.put(3, 3)); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
console.log(lRUCache.get(2)); // 返回 -1 (未找到)
console.log(lRUCache.put(4, 4)); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
console.log(lRUCache.get(1)); // 返回 -1 (未找到)
console.log(lRUCache.get(3)); // 返回 3
console.log(lRUCache.get(4)); // 返回 4
堆
大顶堆
class MaxHeap {
constructor(private numbers: number[] = []) {
const len = this.numbers.length
for (let i = Math.floor(len / 2); i >= 0; --i) {
this.heapifyDown(i)
}
}
public get size(): number {
return this.numbers.length
}
public peek(): number {
return this.numbers[0]
}
heapifyDown(index: number) {
const len = this.numbers.length
while (true) {
if (index > this.numbers.length) return
const leftIndex = this.leftIndex(index)
const rightIndex = this.rightIndex(index)
const rootVal = this.numbers[index]
let maxIndex = index
if (leftIndex < len && rootVal < this.numbers[leftIndex]) {
maxIndex = leftIndex
}
if (rightIndex < len && this.numbers[maxIndex] < this.numbers[rightIndex]) {
maxIndex = rightIndex
}
if (maxIndex !== index) {
this.swap(index, maxIndex)
index = maxIndex
} else {
break
}
}
}
insert(num: number): void {
this.numbers.push(num)
this.heapifyUp(this.numbers.length - 1)
}
heapifyUp(index: number): void {
while (index > 0) {
let parentIndex = this.parentIndex(index)
if (this.numbers[index] > this.numbers[parentIndex]) {
this.swap(index, parentIndex)
index = parentIndex
} else {
break
}
}
}
popMax(): number {
const numbers = this.numbers
const len = numbers.length
if (len === 0) {
return -1
}
const max = numbers[0]
numbers[0] = numbers.pop() as number
this.heapifyDown(0)
return max
}
valueOf() {
return this.numbers.slice(0)
}
leftIndex (index: number) {
return index * 2 + 1
}
rightIndex (index: number) {
return index * 2 + 2
}
parentIndex(index: number) {
return Math.floor((index - 1) / 2)
}
swap(from: number, to: number):void {
const tmp = this.numbers[from]
this.numbers[from] = this.numbers[to]
this.numbers[to] = tmp
}
}
export default MaxHeap
小顶堆
class MinHeap {
private nums: number[] = []
constructor (nums: number[] = []) {
this.nums = nums
this.heapify()
}
compareFn(a: number, b: number) {
return this.nums[a] - this.nums[b]
}
heapify () {
if (this.size < 2) return
for (let i = 1; i < this.size; i++) {
this.bubbleUp(i)
}
}
bubbleUp (index: number) {
while (index > 0) {
const parentIndex = (index - 1) >> 1
if (this.nums[index] < this.nums[parentIndex]) {
this.swap(index, parentIndex)
index = parentIndex
} else {
break
}
}
}
swap (indexA: number, indexB: number): void {
const nums = this.nums
;[nums[indexA], nums[indexB]] = [nums[indexB], nums[indexA]]
}
valueOf () {
return this.nums
}
peek (): number {
return this.nums[0]
}
poll (): number {
if (this.size === 0) return -1
const max = this.nums[0] as number
const last = this.nums.pop() as number
if (this.size !== 0) {
this.nums[0] = last
this.bubbleDown(0)
}
return max
}
bubbleDown (index: number): void {
const len = this.nums.length
while (true) {
const leftIndex = index * 2 + 1
const rightIndex = index * 2 + 2
let minIndex = index
if (leftIndex < len && this.compareFn(leftIndex, minIndex) < 0) {
minIndex = leftIndex
}
if (rightIndex < len && this.compareFn(rightIndex, minIndex) < 0) {
minIndex = rightIndex
}
if (minIndex !== index) {
this.swap(index, minIndex)
index = minIndex
} else {
break
}
}
}
offer (num: number) {
this.nums.push(num)
this.bubbleUp(this.size - 1)
}
get size(): number {
return this.nums.length
}
}
export default MinHeap