javascript数据结构-栈

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

1.1 创建一个基于数组的栈

这里将创建一个类来表示栈。简单地从创建一个 stack-array.js 文件并声明StackA类开始。
我们需要一种数据结构来保存栈里的元素,可以选择数组。数组运行我们在任何位置添加或删除元素。由于栈遵循LIFO原则,需要对元素的插入和删除功能进行限制。接下来为栈声明一些方法。

  • push(element(s)): 添加一个或几个新元素到栈顶。
  • pop():移除栈顶的元素,同时返回被移除的元素。
  • peek():返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶元素,仅仅返回它)。
  • isEmpty():如果栈里没有任何元素就返回true,否则返回false。
  • clear():移除栈里的所有元素。
  • size():返回栈里的元素个数。该方法和数组的 length 属性很类似。
class StackA {
 constructor() {
   this.items = []
 }
 // 向栈添加元素:添加一个或多个元素到栈顶
 push(element) {
   this.items.push(element)
 }

 // 从栈移除元素:移除栈顶元素,同时返回被移除的元素
 pop() {
   return this.items.pop()
 }

 // 查看栈顶元素:返回栈顶元素,不对栈做任何修改
 peek() {
   return this.items[this.items.length -1]
 }

 // 检测栈是否为空:如果栈里没用任何元素就返回true,否则返回false
 isEmpty() {
   return this.items.length === 0
 }

 // 清空栈元素:移除栈里的所有元素
 clear() {
   this.items = []
 }
}

1.2 使用StackA类

简单使用一下StackA类

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
<script src="./stack-array.js"></script>
<script>
  // 初始化Stack-Array类
  const stacka = new StackA()
  console.log(stacka.isEmpty()) // 初始化后,数据里没有元素,返回true

  stacka.push(5)
  stacka.push(8)
  console.log(stacka.peek()) // 输出8
  console.log(stacka.size()) // 输出3
  console.log(stacka.isEmpty()) // 输出false

</script>
</body>
</html>

2.1 创建一个基于JavaScript对象的Stack类

创建一个Stack类最简单的方式是使用一个数组来存储其元素。在处理大量数据的时候,同样需要评估如何操作数据是最高效的。在使用数组时,大部分方法的时间复杂度的O(n)。如果数组有更多元素的话,所需的时间会更长。另外,数组是元素的一个有序集合,为了保证元素的排列有序,它会占用更多的内存空间。
可以使用一个对象来存储所有的栈元素,能直接获取元素,占用较少的内存空间,任然保证它们的顺序并且遵循LIFO原则。
先声明一个对象的Stack类, 使用count属性来记录栈的大小,也能从数据结构中添加和删除元素。

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

  // 先栈中插入元素,只允许一次插入一个元素
  push(element) {
    this.items[this.count] = element
    this.count++
  }
  // 返回它的大小
  size() {
    return this.count
  }

  // 验证一个栈是否为空
  isEmpty() {
    return this.count === 0
  }
  // pop方法同样返回了从栈中移除的元素。由于没有使用数组来存储数据,需要手动实现移除元素的逻辑。
  pop() {
    if (this.isEmpty()) {
      return undefined
    }
    this.count--
    const result = this.items[this.count]
    delete this.items[this.count]
    return result
  }
  // 返回栈顶元素,不对栈有任何修改
  peek() {
    if (this.isEmpty()) {
      return undefined
    }
    return this.items[this.count - 1]
  }
  // 清空栈
  clear() {
    this.items = {}
    this.count = 0
  }
  // toString方法来像数组一样打印出栈的内容
  toString() {
    if (this.isEmpty()) {
      return ''
    }
    let objString = `${this.items[0]}`
    for (let i = 1; i < this.count; i++) {
      objString = `${objString}, ${this.items[i]}`
    }
    return objString
  }
}

2.2 使用StackO类

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

<script src="./stack-object.js"></script>
<script>
  // 初始化Stack-Object类
  const stacko = new StackO()
  console.log(stacko.isEmpty())

  stacko.push(5)
  stacko.push(7)
  stacko.push(8)
  console.log(stacko.peek()) // 输出8
  console.log(stacko.toString()) // 5, 7, 8
</script>
</body>
</html>

3.1 用栈解决问题

3.1.1 从十进制到二进制

要把十进制转化成二进制,我们可以将该十进制数除以二(二进制是满二进一)并对商取整,直到结果是0为止。比如:

10 / 2 === 5 rem === 0  
5 / 2 === 2  rem === 1
2 / 2 === 1  rem === 0
1 / 2 === 0  rem === 1
// 先得到的余数就压入栈中。 输出就等于将余数移出栈。 所以结果就为:1010

下面用js来实现一下:

// 十进制转二进制
// remStack 是前面创建的Stack类,传给函数内部使用
function decimalToBinary(decNumber, remStack) {
  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
}

在这段代码里,当除法的结果不为0时,会得到一个余数,并放到栈里。然后让结果继续除以2。

  const remStack1 = new StackO()
  console.log(decimalToBinary(233, remStack1)) // 11101001
  console.log(decimalToBinart(10, remStack1)) // 1010

可以修改之前的算法,使之能把十进制转换成基数为2-36的任意进制。除了把十进制转成二进制,还可以传入其他任意进制的基数为参数。

// 十进制转其他进制
function baseConverter(decNumber, base, remStack) {
  const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  let number = decNumber;
  let rem;
  let baseString = '';

  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()) {
    baseString += digits[remStack.pop()]
  }

  return baseString
}

只需要改变一个地方。在将十进制转成二进制时,余数是0或1;在将十进制转成八进制时,余数是0~7;但是将十进制转成十六进制时,余数是0-9加上A、B、C、D、E、F。因此,需要对栈中的数字做个转化才行。
因此,从十一进制开始,字母表中的每个字母将表示相应的基数。字母A代表基数11,B代表基数12,以此类推。

  const remStack2 = new StackO()
  console.log(baseConverter(100345, 2, remStack2)) // 转为二进制:11000011111111001
  console.log(baseConverter(100345, 8, remStack2)) // 转为8进制:303771
  console.log(baseConverter(100345, 16, remStack2)) // 转为16进制:187F9
  console.log(baseConverter(100345, 35, remStack2)) // 转为35进制:2BW0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值