栈
通过数组的学习,我们复习了数组的一些API,这些在JS中是很基础的。我会从这一节开始,将一些函数整理到自己的github库中,以便大家查看及学习。
本次内容主要包括
- 栈数据结构
- 向栈中增加元素
- 删除栈中的元素
- 如何使用Stack类
- 十进制转二进制
1. 栈数据结构
栈是一种遵循后进先出(LIFO = last in first out)原则,是一种用来表示元素的集合,主要操作有两种,push
和 pop
push
是向栈的顶部(尾部)中增加一个元素
pop
是移除栈中最顶端(尾部)的元素
2. 创建一个基于数组的栈
根据栈遵循的LIFO的原则,我们需要对元素的插入和删除功能做一些限制,我们要为栈声明以下几种方法
push(elements)
:向栈顶添加一个或几个元素到栈顶pop()
: 移除栈顶的元素,返回值为移除的元素peek()
: 返回栈顶的元素,不对它做任何处理(只是返回这一值)isEmpty()
: 判断栈是否为空clear()
: 清空栈中的元素size()
: 返回栈中元素的个数,类似于数组的length
class Stack {
constructor(){
this.items = [] // 我们可以用数组来保存栈中的数据
}
push(elem){
this.items.push(elem) // 通过上面的图,我们可以使用数组中的push方法将元素推到栈顶
}
pop(){
return this.items.pop()
}
peek(){
return this.items[this.items.length - 1]
}
isEmpty(){
return this.items.length === 0
}
clear(){
this.items = []
}
size(){
return this.items.length
}
}
接下来我们使用一下我们封装的Stack
类
var stack = new Stack()
console.log(stack.isEmpty()) // true
stack.push(9)
stack.push(8)
console.log(stack.size()) // 2
console.log(stack) // Stack { items: [ 9, 8 ] }
stack.push(7)
stack.push(6)
console.log(stack) // Stack { items: [ 9, 8, 7, 6] }
console.log(stack.peek()) // 6
console.log(stack.pop()) // 6
console.log(stack) // Stack { items: [ 9, 8, 7 ] }
stack.clear()
console.log(stack) // Stack { items: [] }
3. 如何使用Stack类
在我们使用数组中的方法是,大部分的API的时间复杂度是O(n)
上述的代码中我们只是在栈顶去推入一个元素。我们现在需要改进一下我们的例子,在Stack
的构造函数中增加count
的属性,这样可以在栈的任何位置插入元素,下面的代码只拿出来修改的部分
constructor(){
this.items = [] // 我们可以用数组来保存栈中的数据
this.count = 0
}
push(elem){
// this.items.push(elem) // 通过上面的图,我们可以使用数组中的push方法将元素推到栈顶
this.items[this.count] = elem
this.count ++
}
我们测试之后
var stack = new Stack()
console.log(stack) // Stack { items: [], count: 0 }
stack.push(9)
stack.push(8)
console.log(stack) // Stack { items: [ 9, 8 ], count: 2 }
同样的思路,我们将其他方法也修改一下,升级后的Stack
类如下
class Stack {
constructor(){
this.items = [] // 我们可以用数组来保存栈中的数据
this.count = 0
}
push(elem){
this.items[this.count] = elem
this.count++
// this.items.push(elem)
}
pop(){
if (this.isEmpty()) {
return undefined
}
const item = this.items[this.count-1]
this.count--
return item
// return this.items.pop()
}
peek(){
if (this.isEmpty()) {
return undefined
}
return this.items[this.count - 1]
}
isEmpty(){
return this.count === 0
}
clear(){
this.items = []
this.count = 0
}
size(){
return this.count
}
}
接下来,我们在Stack
类中增加一个toString()
的方法,将数组结构转化为字符串
toString(){
if (this.isEmpty()) {
return ''
}
let objString = `${this.items[0]}`
for (let i = 1; i < this.items.length; i++) {
objString = `${objString},${this.items[i]}`
}
return objString
}
var stack = new Stack()
stack.push(9)
stack.push(8)
stack.push(7)
stack.push(6)
console.log(stack.toString()) // 9,8,7,6
4. 用栈解决问题
我们在回溯算法问题中,栈可以用来存储访问过的任务或者路径、撤销操作。又或者在前端访问页面时的历史记录就是一个栈,我们这里介绍的是十进制转化为二进制,以及任意进制转换的算法
通过上述的图我们可以看到十进制转化为二进制的过程
// 进制转换函数
function decimalToBinary(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)
}
console.log(remStack,'remStack',remStack.isEmpty())
while (!remStack.isEmpty()) {
binaryString += remStack.pop().toString()
}
return binaryString
}
console.log(decimalToBinary(10)) // 1010
接下来我们可以对这个函数进行改造一下,让它可以实现任意进制转换
function baseConvert(decNumber,base) {
const remStack = new Stack()
const digits = '0123456789ABCDEFGHIGKLMNOPQRSTUVWXYZ'
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
}
console.log(baseConvert(100345,3)) // 12002122111
下一篇文章是关于队列的,队列则和我们生活中的排队是一样的,遵循先进先出,后进后出的原则。这也是和栈最重要的区别