数据结构

356 阅读4分钟

栈(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 存在于缓存中,则返回关键字的值,否则返回 -1void 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