数据结构--栈

186 阅读3分钟

一、 什么是栈

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

1.1 特点

  1. 先入后出,后入先出(LIFO原则)
  2. 除头尾节点之外,每个元素有一个前驱,一个后继

1.2 栈常有的操作

  • push(element):添加一个新元素到栈顶位置。
  • pop() :移除栈顶的元素
  • peek( ) :返回栈顶的元素,不对栈做任何修改
  • isEmpty() :判断栈是否空,如果栈里没有任何元素,则返回true,否则返回false
  • size() :返回栈里的元素个数
  • toString() :将栈结构的内容以字符形式返回

二、js实现栈结构

2.1 创建一个基于数组的栈结构

class Stack{
    constructor(){
        this.items = []
    }
    //向栈中添加元素
    push(element){
        this.items.push(element)
    }
    // 移除栈顶元素
    pop(){
        return this.items.pop()
    }
    peek(){// 查看栈顶元素
        return this.items[this.items.length - 1]
    }
    isEmpty(){
        //判断栈是否为空
        return this.items.length == 0
    }
    size(){
        //判断栈中元素的个数
        return this.items.length
    }
    clear(){
        this.items = []
    }
    toString(){
        //以字符串形式输出栈内数据
        let resultString = ''
        for (let i of this.items){
          resultString += i + ' '
        }
        return resultString
    }
}

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

从上述创建过程可以得出,我们是基于数组来存储其元素的。但是我们认真衡量下,在处理大量数据的时候,如何操作数据才能更高效呢。

例如我们在栈中查找某个元素,我们需要迭代整个元素的数据才能够找到。如果我们能直接获取元素,占用较少的内存空间,可以使用一个JavaScript对象存储所有的栈元素,并保证它的顺序遵循LIFO原则,接下来我们来实现。

class Stack{
    constructor(){
        this.items = {};
        this.count = 0 
    }
    //向栈中添加元素
    push(element){
        this.items[this.count] = element;
        this.count ++ 
    }
    // 移除栈顶元素
    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]
    }
    isEmpty(){
        //判断栈是否为空
        return this.count === 0
    }
    size(){
        //判断栈中元素的个数
        return this.count
    }
    clear(){
        this.items = {}
        this.count = 0
    }
    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并对商取整,直到结果是0为止

function decimalToBinary(decNumber){
    const stack  = new Stack()
    let number = decNumber
    let rem = '',binaryString = ''
    while(number>0){
        rem = Math.floor(number%2)
        stack.push(rem)
        number = Math.floor(number/2)
    }
    while(!stack.isEmpty()){
        binaryString +=stack.pop().toString()
    }
    return binaryString
}

进制转换算法

我们从上述代码中可以得到一定的逻辑,把十进制转换成基数(2~36)任意进制

function baseConverter(decNumber,base){
    const stack  = new Stack()
    const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    let number = decNumber
    let rem = '',binaryString = ''

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

注意:

  • 十进制转化为二进制时,余数是0或1;
  • 十进制转化为八进制时,余数是0~7
  • 十六进制时,余数是0~9,ABCDEF
  • 因此我们在转化为进制数据时,考虑下数字对应的基数,即digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

四、leetcode上相关算法题

题目leetcode 题数难易程度
每日温度LeetCode 739中等
字符串解码LeetCode 394中等
有效的括号LeetCode 20简单
基本计算器 IILeetCode 227中等
逆波兰表达式求值LeetCode 150中等
有效的括号字符串LeetCode 678中等
移掉k位数字LeetCode 402中等