JS 算法与数据结构之栈

列表是一种最自然的数据组织方式,如果数据存储的顺序不重要,且无需对数据进行查找,那么列表是一种再好不过的数据结构,但对于其它一些应用,列表就显得太过简陋,我们需要一种更复杂的数据结构——栈

一、 什么是栈

栈是一种后入先出(LIFO,Last-in-first-out) 的数据结构,其数据只能在栈顶添加或删除,因此操作高效快速。

栈顶:栈内的元素只能通过列表的一端访问,这一端称为栈顶。

由于栈后入先出的特点,所以任何不在栈顶的元素都无法访问,要得到栈底的元素,需要先拿掉上面的元素。

二、栈的操作

1、入栈

使用 push() 方法,将一个元素压入栈。

2、出栈

使用 pop() 方法,将一个元素弹出栈。

3、预览栈顶元素

pop() 方法虽然可以访问栈顶元素,但调用后栈顶元素即被删除了,
而 peek() 方法则只返回栈顶元素,不删除它,用来预览栈顶元素。

4、记录栈顶元素位置

我们用变量 top 来记录栈顶元素的位置,标记哪里可以加入新元素;
当向栈内压入元素时,top 值增大,当从栈内弹出元素时,top 值减小。

5、清除栈内所有元素

用 clear() 方法来清除栈内所有元素

6、记录栈内元素个数

用变量 length 来记录栈内元素的个数

7、表示栈内是否含有元素

用 empty 属性来表示栈内是否含有元素,用 length 也可达到同样的目的

三、栈的实现

1、定义 Stack 类

function Stack() {
  this.dataStore = []
  this.top = 0
  this.push = push
  this.pop = pop
  this.peek = peek
}

这里用数组 dataStore 保存栈内元素,构造函数将其初始化为一个空数组。
变量 top 记录栈顶位置,被构造函数初始化为 0,表示栈顶对应数组的起始位置 0。

2、实现 push 方法

当向栈中压入一个新元素时,需要将其保存在数组中变量 top 所对应的位置,然后将 top 值加 1,让其指向数组中下一个空位置。

function push(element) {
  this.dataStore[this.top++] = element
}

3、实现 pop 方法

pop 和 push 相反——返回栈顶元素,同时将变量 top 的值减 1。

function pop() {
  return this.dataStore[--this.top]
}

关于 i++ 和 ++i
使用 i++ 时,i 先将自身的值赋值给变量 a,然后再自增 1
使用 ++i 时,i 先将自身的值自增 1,再将自增后的值赋值给变量 a

4、实现 peek 方法

peek 方法返回数组的第 top-1 个位置的元素,即栈顶元素。

function peek() {
  return this.dataStore[this.top - 1]
}

如果对一个空栈调用 peek 方法,结果为 undefined(因为栈是空的,栈顶没有任何元素)。

5、实现 length 方法

length 方法通过返回变量 top 值的方法返回栈内的元素个数,可用来了解栈内存储了多少个元素。

function length() {
  return this.top
}

6、实现 clear 方法

clear 方法将变量 top 的值设为 0,用来清空一个栈。

function clear() {
  this.top = 0
}

7、测试 Stack 类的实现

var s = new Stack()

s.push('a')
s.push('b')
s.push('c')

console.log(s.length()) // 3
console.log(s.peek())   // "c"

var poped = s.pop()

console.log(poped)      // "c"
console.log(s.peek())   // "b"

s.push("d")

console.log(s.peek())   // "d"

s.clear()

console.log(s.length()) // 0
console.log(s.peek())   // undefined

四、栈的实例

1、回文判断

回文:指的是一个单词、短语或数字,从前往后写和从后往前写是一样的,比如:“dad”、“racecar”。

使用栈可以轻松判断一个字符串是否是回文:
将字符串的每个字符按从左到右的顺序压入栈,栈内就保存了一个反转后的字符串,尾字符在栈顶,而首字符在栈底;
通过持续弹出栈内的每个元素就可以得到一个新的字符串,这个字符串与原字符串的顺序相反;
只需比较新字符串和原字符串是否相等即可。

function isPalindrome(word) {
  var s = new Stack()
  for (var i = 0; i < word.length; ++i) {
    s.push(word[i])
  }
  var rword = ''
  while (s.length() > 0) {
    rword += s.pop()
  }
  if (word == rword) {
    return true
  } else {
    return false
  }
}

var word1 = 'hello'
var word2 = 'racecar'

console.log(isPalindrome(word1))  // false
console.log(isPalindrome(word2))  // true

2、阶乘计算

递归可以用来实现阶乘运算

① 普通函数递归实现阶乘

function factorial(n) {
  if (n === 0) {
    return 1
  } else {
    return n * factorial(n - 1)
  }
}

console.log(factorial(5)) // 120

② 栈模拟递归实现阶乘

使用栈来模拟递归实现阶乘,先将数字从 5 到 1 压入栈,然后使用一个循环,将数字挨个弹出连乘即可。

function factorial(n) {
  var s = new Stack()
  while (n > 1) {
    s.push(n--)
  }
  var product = 1
  while (s.length() > 0) {
    product *= s.pop()
  }
  return product
}

console.log(factorial(5)) // 120
posted @ 2021-03-18 21:28  Leophen  阅读(23)  评论(0编辑  收藏  举报