【TS数据结构】栈

栈是一种先进先出(LIFO)原则的有序集合。新添加或待删除的元素都处于栈的同一端,称作栈顶,另一端叫做栈底。在栈里,新元素都靠近栈顶,旧元素都靠近栈底。

栈的一些方法:

  • push(element[s]):添加一个(或多个)新元素到栈顶
  • pop():从栈顶弹出一个元素
  • peek():看一下栈顶的元素,但是不弹出
  • isEmpty():返回栈是否为空
  • clear():清除栈中所有的元素
  • size():返回栈中的元素的个数

创建一个基于数组的栈

class ArrayStack<T> {
  private _array: T[]
  constructor() {
    this._array = []
  }

  push(...elements: T[]): void {
    this._array.push(...elements)
  }

  pop(): T {
    return this._array.pop()
  }

  peek(): T {
    let len = this._array.length
    return this._array[len - 1]
  }

  isEmpty(): boolean {
    return this._array.length <= 0
  }

  clear(): void {
    this._array.length = 0
  }

  size(): number {
    return this._array.length
  }
}

export default ArrayStack

这里我们用了一个数组来模拟栈,利用数组身上的push()pop()可以很轻易地实现栈这种数据结构的一些操作。

ArrayStack地内部有一个私有的_array属性,存放的是T类型(泛型)的数组。这是使用的是_命名规范也就是js中私有变量的命名规范,我们也可以省略前面的_因为我们用的是ts可以直接声明私有属性。


使用数组的API创建一个栈是最简单的。但是在处理大量数据的时候,我们同样需要评估如何操作数据是最搞笑的。在创建数组时,大部分方法的时间复杂度是$O(n)$。$O(n)$的意思是,我们需要迭代整个数组知道找到要找的那个元素,在最坏的情况下需要迭代数组所有的位置。

如果我们能够直接获取元素,占用较少的内存空间,并且荏苒保证所有元素按照我们的需要排列,不是更好吗?对于js语言实现栈数据结构的场景,我们也可以使用一个js对象来存储所有栈元素,保证他们的顺序并且遵守LIFO原则。

使用一个对象来实现栈

class Stack<T> {
  private items: {}
  private count: number
  constructor() {
    this.items = {}
    this.count = 0
  }

  push(element: T) {
    this.items[this.count++] = element
  }

  pop(): T {
    if (this.isEmpty()) return undefined
    this.count--
    const res = this.items[this.count]
    delete this.items[this.count]
    return res
  }

  peek(): T {
    if (this.isEmpty()) return undefined
    return this.items[this.count - 1]
  }

  isEmpty(): boolean {
    return this.count <= 0
  }

  clear(): void {
    this.items = {}
    this.count = 0
  }

  toString() {
    if (this.isEmpty()) return 'stack is empty!'
    let res = this.items[0].toString()
    for (let i = 1; i < this.count; i++) {
      res = `${res}, ${this.items[i].toString()}`
    }
    return res
  }
}

export default Stack

使用对象来模拟栈,可以解决数组中的方法都需要便利数组的情况,理论上来说是要比数组实现的栈效率更高

用栈解决问题

栈的实际应用非常广泛。在回溯问题中,他可存储访问过的任务或路径、撤销的操作。

从十进制转到二进制

要把十进制转换成二进制,我们可以将该十进制数除2并对商取整,知道结果为0为止。举个例子,把十进制数10转化成二进制的数字,大致的过程是如下这样的。

Untitled

import Stack from '../../ds/stack'
export const decimalToBinary = (decimal: number): string => {
  const stack = new Stack<string>()
  let n: number = decimal
  let binaryStr: string = ''
  while (n > 0) {
    stack.push((n % 2) + '')
    n = Math.floor(n / 2)
  }
  while (!stack.isEmpty()) {
    binaryStr += stack.pop()
  }
  return binaryStr
}

ts数据结构仓库地址: https://github.com/FarajMujey/dsa.ts

posted @ 2021-09-06 16:44  FarajMujey  阅读(588)  评论(0)    收藏  举报