152 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

什么是栈结构?

  • 栈结构是遵循先后先出的有序集合

创建一个基于数组的栈

class Stack {
    constructore() {
        this.item = []
    }
}
  • 添加元素push,该方法只添加元素,栈顶也就是栈的末尾
push(element) {
    this.item.push(element)
}
  • 从栈移除元素pop,遵从LIFO原则,移除最后添加进去的元素
pop(){
    return this.item.pop()
}
  • 查看栈顶元素peek,栈顶就是最后添加进去的,用栈的长度减1,就可以拿到最后添加进去的位置了
peek() {
    retrun this.item[this.item.length - 1]
}
  • 检查栈是否为空isEmpty
isEmpty() {
    return this.item.length === 0
}
  • 查看栈的长度size
size() {
    return this.item.length
}
  • 清空栈clear
clear() {
    this.item = []
}

完成了,栈已经实现了

使用Stack类

const stack = new Stack()
//判断是否为空
console.log(stack.isEmpty())    //true

//添加元素
stack.push(5)
stack.push(8)
stack.push(11)

//查看栈顶
console.log(stack.peek())    //11

//移除元素
stack.pop()
stack.pop()
console.log(stack.size())    //1

创建一个属于JavaScript对象的Stack类

class Stack {
    constructor(){
        this.count = 0
        this.items = {}
    }
}

使用count属性来记录一下栈的大小

  • 插入元素
 push(element) {
        this.items[this.count] = element
        this.count++
    }

一次只能插入一个元素,count作为items对象的键名,插入的元素为值

const stack = new Stcak()
stack.push(5)
stack.push(8)

//内部
items = {
    0: 5
    1: 8
}
count = 2
  • 验证栈是否为空和它的大小
size(){
        return this.count
    }

isEmpty() {
        return this.count === 0
    }
  • 从栈里面弹出元素
  pop() {
    if (this.isEmpty()) {
      return undefined;
    }
    this.count--;
    const result = this.items[this.count];
    delete this.items[this.count];
    return result;
  }

先判断是否为空,如果不为空,就count减一,并保存栈顶的值,删除后将它返回

items = {
    0: 5
    1: 8
}
count = 2

要想访问栈顶的元素(最后添加的元素8),就需要访问键值为1的位置,所有count变量需要从2变1,这样就可以访问加删除了

  • 查看栈顶的值并清空栈
peek() {
      if(this.isEmpty()){
        return undefined
      }

      return this.items[this.count - 1]
  }

clear() {
      this.items = {}
      this.count = 0
  }
  
  //当然也可以遵守LIFO原则
  while(!this.isEmpty()) {
      this.pop()
  }
  • 创建toString方法
toSting() {
      if(this.isEmpty()) ""
    let objString = `${this.items[0]}`
    for(let i = 1; i < this.count; i++){
        objString = `${objString},${this.items[i]}`
    }
    return objString
  }

保护数据结构内部元素

const stack = new Stack()
console.log(Object.getOwnPropertyNames(stack));
console.log(Object.keys(stack));
console.log(stack.items);

image.png 表示count和items属性是公开的,我们可以直接访问,但是我们只希望用户可以访问我们在类中暴露的方法

下划线命名约定

  • 使用下划线约定来标记一个属性为私有属性
class Stack {
    constructor() {
    this._count = 0;
    this._items = {};
  }
}

ES2015限定作用域Symbol

  • Symbol是不可改变的
const _items = Symbol('stackItems')
class Stack {
    constructor () {
        this[_items] = []
    }
    
    //栈的方法
}

这种方法创建了一个假的私有属性

栈的小案例

  • 十进制到二进制
10/2 == 5 rem == 0
5/2  == 2 rem == 1
2/2  == 1 rem == 0
1/2  == 0 rem == 1
  • 动画过程 动画.gif
  • 对应算法
function de(decNumber) {
  const remStack = new Stack();
  let number = decNumber;
  let rem;
  let binaryString = "";

  while (number > 0) {
    rem = Math.floor(number % 2);
    remStack.push(rem);
    number = Math.floor(number / 2);
  }

  while (!remStack.isEmpty()) {
    binaryString += remStack.pop().toString();
  }

  return binaryString;
}

升级一下,把十进制换成2~36的任意进制

function de2(decNumber, base) {
    const remStack = new Stack();
    const digits = '0123456789ABCDEFGHJKLMNOPQRSTUVWXYZ'
    let number = decNumber;
    let rem;
    let binaryString = "";

    if(!(base >= 2 && base <= 36)) {
        return ''
    }
  
    while (number > 0) {
      rem = Math.floor(number % base);
      remStack.push(rem);
      number = Math.floor(number / base);
    }
  
    while (!remStack.isEmpty()) {
      binaryString += digits[remStack.pop()]
    }
  
    return binaryString;
  }