JavaScript数据结构:栈

目录

1.前言

2.顺序栈

3.链式栈

4.总结


1.前言

栈是一种常见的数据结构,栈分为顺序栈和链式栈,遵循FILO(First In Last Out)原则,先入后出。栈在平常编码时也经常出现,例如递归时常出现的栈溢出报错。

栈的示意图(图片来自网络)

在JavaScript里面没有提供栈类型,这里我们使用JavaScript自己实现栈。

栈常见的方法如下:

push():入栈

pop():出栈

peek():获取栈顶元素

isEmpty():判断栈是否为空

clear():清空栈

size():获取栈的大小

toString():栈的字符串输出

2.顺序栈

顺序栈是一段顺序顺序存储结构,这里我们使用数组实现顺序栈。

function Stack(values) {
  //数据
  //初始化时可传入数组或Set对象
  this._item = values ? [...values] : []

  //如果直接写构造函数的方法,相同方法在每个实例对象上都重新定义一次,太浪费内存。
  //将方法写在prototype上,只定义一次,所有实例对象都可使用,节省内存空间。

  //入栈
  Stack.prototype.push = function (val) {
    this._item.push(val)
  }

  //出栈
  Stack.prototype.pop = function () {
    //返回出栈的元素
    return this._item.pop()
  }

  //获取栈顶元素
  Stack.prototype.peek = function () {
    //返回栈顶元素
    return this._item[this._item.length - 1]
  }

  //栈是否为空
  Stack.prototype.isEmpty = function () {
    //返回布尔值
    return this._item.length === 0
  }

  //清空栈
  Stack.prototype.clear = function () {
    this._item = []
  }

  //栈的大小
  Stack.prototype.size = function () {
    //返回栈的大小
    return this._item.length
  }

  //栈的字符串输出
  Stack.prototype.toString = function () {
    //字符串形式返回栈
    return this._item.toString()
  }
}

测试

let test = new Stack()
//入栈测试
test.push(1)
test.push(2)
test.push(3)
test.push(4)
//获取栈的大小和字符串输出
console.log(test.size()); //4
console.log(test.toString()); //1,2,3,4
//出栈测试
console.log(test.pop()); //4
console.log(test.size()); //3
console.log(test.toString()); //1,2,3
console.log(test.pop()); //3
console.log(test.size()); //2
console.log(test.toString()); //1,2
//获取栈顶测试
console.log(test.peek()); //2
//清空栈测试
test.clear()
console.log(test.size()); //0
//判断栈是否为空测试
console.log(test.isEmpty()); //true
test.push(1)
console.log(test.isEmpty()); //false

 测试通过

3.链式栈

链式栈使用的不是一段连续的内存空间,克服了顺序栈空间利用率不高的问题。这里使用单链表来实现链式栈。

function Stack() {
  //栈底
  this.head = null
  //栈顶
  this.top = null
  //栈的大小
  this.length = 0

  //节点
  function Node(value) {
    this.value = value
    this.next = null
  }

  //入栈
  Stack.prototype.push = function (val) {
    let node = new Node(val)
    //当栈为空时入栈,栈底和栈顶都指向新插入节点
    if (this.length === 0) {
      this.head = node
      this.top = node
    } else {
      //当栈不为空时插入,栈顶节点的next节点为新插入节点
      //栈顶指向至原栈顶节点的next节点
      this.top.next = node
      this.top = this.top.next
    }
    this.length++
  }

  //出栈
  Stack.prototype.pop = function () {
    //栈为空时直接返回null
    if (this.length === 0) return null
    else if (this.length === 1) {
      //栈的大小为1时
      //栈顶和栈底指向null,栈的大小减为1,返回原栈顶的value
      const value = this.top.value
      this.head = null
      this.top = null
      this.length = 0
      return value
    } else {
      //当栈的大小大于1时
      //找到栈顶的上一个节点,将栈顶指向该节点,并将栈顶的next赋值为null
      //栈的大小减1,返回原栈顶的value
      const value = this.top.value
      //当前节点,由于此时栈的至少有两个元素,所以此时curr可以直接指向栈底的下一个节点
      let curr = this.head.next
      //当前节点的上一个节点,此时为栈底
      let prev = this.head
      //当前节点不存在下一节点时则查找结束
      //查找结束时curr即为栈顶,prev为栈顶的上一个节点
      while (curr.next) {
        curr = curr.next
        prev = prev.next
      }
      //将栈顶指向prev,并将栈顶的下一节点赋值为null实现出栈
      this.top = prev
      this.top.next = null
      //出栈成功,栈的大小减1
      this.length--
      return value
    }
  }

  //获取栈顶元素
  Stack.prototype.peek = function () {
    //直接返回栈顶的value
    return this.top.value
  }

  //栈是否为空
  Stack.prototype.isEmpty = function () {
    //判断栈的大小是否为0
    return this.length === 0
  }

  //清空栈
  Stack.prototype.clear = function () {
    //和出栈时栈的大小为1的处理相同
    this.head = null
    this.top = null
    this.length = 0
  }

  //获取栈的大小
  Stack.prototype.size = function () {
    //直接返回栈的大小
    return this.length
  }

  //栈的字符串输出
  Stack.prototype.toString = function () {
    let str = ""
    //栈的大小为0时返回空字符串
    if (this.length === 0) return str
    //栈的大小为1时返回栈低的字符式value
    else if (this.length === 1) return str + this.head.value
    else {
      //当前查找到的节点
      let curr = this.head
      //从栈底遍历栈
      while (curr) {
        //字符串加逗号间隔
        str = str + curr.value + ","
        curr = curr.next
      }
      //返回结果时去除末尾多余的逗号
      return str.slice(0, str.length - 1)
    }
  }
}

采用和顺序栈一样的测试

let test = new Stack()
//入栈测试
test.push(1)
test.push(2)
test.push(3)
test.push(4)
//获取栈的大小和字符串输出
console.log(test.size()); //4
console.log(test.toString()); //1,2,3,4
//出栈测试
console.log(test.pop()); //4
console.log(test.size()); //3
console.log(test.toString()); //1,2,3
console.log(test.pop()); //3
console.log(test.size()); //2
console.log(test.toString()); //1,2
//获取栈顶测试
console.log(test.peek()); //2
//清空栈测试
test.clear()
console.log(test.size()); //0
//判断栈是否为空测试
console.log(test.isEmpty()); //true
test.push(1)
console.log(test.isEmpty()); //false

测试通过

4.总结

栈的一切操作遵循先进先出原则,元素从栈顶压入,从栈顶压出。

栈作为最常见的数据结构之一,应用场景丰富,了解栈的原理有利于对数据结构和算法的学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值