js(JavaScript)数据结构之栈(Stack)

发布时间:2024年01月10日

什么是数据结构?

下面是维基百科的解释:

数据结构是计算机存储、组织数据的方式。数据结构意味着接口或封装:一个数据结构可被视为两个函数之间的接口,或者是由数据类型联合组成的存储内容的访问方法封装。

我们每天的编码中都会用到数据结构,下面是常见的数据结构:

  • 数组(Array)
  • 栈(Stack)
  • 队列(Queue)
  • 链表(Linked List)
  • 散列表(Hash)
  • 字典
  • 树(Tree)
  • 图(Graph)
  • 堆(Heap)

栈(Stack)

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

特点: 栈是一种高效的数据结构,只能在栈顶进行添加或删除操作,操作速度较快。适用于后入先出或先进后出的数据保存需求。

实现: 通过对数据进行封装,提供常用方法如push、pop、peek、isEmpty、size和clear来操作栈。

class Stack {
  constructor() {
    this._items = []; // 储存数据
  }

  push(item) {
    this._items.push(item);
  }

  pop() {
    return this._items.pop();
  }

  peek() {
    return this._items[this._items.length - 1];
  }

  isEmpty() {
    return !this._items.length;
  }

  size() {
    return this._items.length;
  }

  clear() {
    this._items = [];
  }
}
  1. push(item): 添加一个或多个新元素到栈顶。新元素被添加到栈顶后,成为新的栈顶元素,其他元素的位置不变。

  2. pop(): 移除并返回栈顶的元素。该方法会将栈顶元素移除,并返回移除的元素。移除栈顶元素后,原本的次顶元素变为新的栈顶元素。

  3. peek(): 返回栈顶元素,不对栈做修改。该方法不会修改栈,只是将栈顶元素返回,即返回当前在栈顶位置的元素。

  4. isEmpty(): 判断栈内是否无元素,如果栈内无元素,则返回 true,否则返回 false。

  5. size(): 返回栈内元素个数。该方法返回栈中元素的数量或长度。

  6. clear(): 清空栈。该方法会删除栈中所有的元素,将栈恢复到创建时的初始状态。

栈(Stack)的实现示例,基于 JavaScript 的类(class)。下面是对每个方法的分析和代码案例:

  1. push(item): 将一个或多个新元素添加到栈顶。
    示例:
const stack = new Stack();
stack.push(1);
stack.push(2);
console.log(stack.size()); // 输出:2
  1. pop(): 移除并返回栈顶的元素。
    示例:
const stack = new Stack();
stack.push(1);
stack.push(2);
console.log(stack.pop()); // 输出:2
console.log(stack.size()); // 输出:1
  1. peek(): 返回栈顶的元素,不对栈做修改。
    示例:
const stack = new Stack();
stack.push(1);
stack.push(2);
console.log(stack.peek()); // 输出:2
console.log(stack.size()); // 输出:2
  1. isEmpty(): 判断栈内是否无元素,无元素返回 true,否则返回 false。
    示例:
const stack = new Stack();
console.log(stack.isEmpty()); // 输出:true
stack.push(1);
console.log(stack.isEmpty()); // 输出:false
  1. size(): 返回栈内元素个数。
    示例:
const stack = new Stack();
stack.push(1);
stack.push(2);
console.log(stack.size()); // 输出:2
  1. clear(): 清空栈。
    示例:
const stack = new Stack();
stack.push(1);
stack.push(2);
console.log(stack.size()); // 输出:2
stack.clear();
console.log(stack.size()); // 输出:0

你可以将这段代码放入一个 HTML 文件中,并在浏览器中打开该文件,然后打开浏览器的开发者工具控制台,查看输出结果。

栈的应用

  1. 十进制转任意进制: 通过栈记录余数的方式,实现十进制到其他进制的转换。

  2. 逆波兰表达式计算: 利用栈对逆波兰表达式进行计算,遇到运算符时弹出栈顶元素进行运算,结果入栈。

  3. 实现有min方法的栈: 使用两个栈,一个用于存储数据,另一个用于存储栈内最小的数据,保持两个栈元素个数相同。

十进制转任意进制
要求:给定一个函数,输入目标数值和进制基数,输出对应的进制数(最大为16进制)。

当要将十进制数转换为任意进制数时,可以利用栈这种数据结构的先入后出特性,将余数逐步入栈并组合起来。

以下是用 JavaScript 实现这一功能的代码:

// 定义一个栈数据结构
class Stack {
    constructor() {
        this.items = [];
    }

    // 添加元素到栈顶
    push(element) {
        this.items.push(element);
    }

    // 从栈顶移除元素
    pop() {
        if (this.items.length === 0) {
            return "Underflow";
        }
        return this.items.pop();
    }

    // 返回栈顶元素
    peek() {
        return this.items[this.items.length - 1];
    }

    // 检查栈是否为空
    isEmpty() {
        return this.items.length === 0;
    }
}

// 定义一个函数,将十进制数值转换为指定进制的数值
function baseConverter(decimalNumber, base) {
    const stack = new Stack(); // 创建一个栈来存储余数
    let remainder = null;
    let result = [];
    const digits = '0123456789ABCDEF'; // 字符表示十六进制数字

    // 将十进制数值转换为指定进制
    while (decimalNumber > 0) {
        // 计算余数并将其压入栈中
        remainder = Math.floor(decimalNumber % base);
        stack.push(remainder);
        // 更新被除数
        decimalNumber = Math.floor(decimalNumber / base);
    }

    // 从栈中取出余数并组合成指定进制的数值
    while (!stack.isEmpty()) {
        result.push(digits[stack.pop()]);
    }

    // 返回转换后的数值
    return result.join('');
}

// 测试转换函数
console.log(baseConverter(100345, 2));   // 输出 11000011111111001  (二进制)
console.log(baseConverter(100345, 8));   // 输出 303771             (八进制)
console.log(baseConverter(100345, 16));  // 输出 187F9              (十六进制)

这段代码定义了一个 Stack 类来模拟栈数据结构,并使用 baseConverter 函数将十进制数转换为任意进制。该函数将十进制数值除以目标进制的基数,依次计算余数并将余数入栈,然后逐个出栈组合成目标进制的数值。最后,将结果转换为字符串并返回。

你可以调用 baseConverter 函数并传入不同的参数进行测试,观察输出结果。

逆波兰表达式计算
要求:逆波兰表达式,也叫后缀表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式,例如 (a+b)(c+d)转换为 a b+c d+

逆波兰表达式是一种不需要括号来表示运算符优先级的表达式,其计算方式是从左到右遍历表达式,遇到操作数就入栈,遇到运算符就从栈中弹出相应数量的操作数进行计算,然后将结果入栈,最终栈中仅剩一个结果,即为表达式的计算结果。

以下是使用 JavaScript 实现逆波兰表达式计算的代码:

    // 检查字符是否为运算符
    function isOperator(str) {
      return ['+', '-', '*', '/'].includes(str);
    }

    // 根据逆波兰表达式计算结果
    function calcExp(exp) {
      const stack = []; // 使用数组模拟栈

      // 遍历表达式数组
      for (let i = 0; i < exp.length; i++) {
        const element = exp[i];
        if (!isOperator(element)) {
          // 如果是操作数,则入栈
          stack.push(element);
        } else {
          // 如果是运算符,从栈中弹出两个操作数进行运算
          const operand2 = parseFloat(stack.pop());
          const operand1 = parseFloat(stack.pop());

          // 根据运算符进行计算,并将结果压入栈中
          switch (element) {
            case '+':
              stack.push(operand1 + operand2);
              break;
            case '-':
              stack.push(operand1 - operand2);
              break;
            case '*':
              stack.push(operand1 * operand2);
              break;
            case '/':
              stack.push(operand1 / operand2);
              break;
          }
        }
      }

      // 返回计算结果(栈中剩余的唯一元素)
      return stack[0];
    }

    // 测试逆波兰表达式计算函数
    console.log(calcExp(["4", "13", "5", "/", "+"])); // 输出 6.6
    console.log(calcExp(["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]).toFixed(0)); // 输出 22

这段代码定义了一个 calcExp 函数,接受一个逆波兰表达式的数组作为输入,并通过遍历数组实现表达式的计算。在遍历过程中,如果遇到操作数,则将其入栈;如果遇到运算符,则从栈中弹出相应数量的操作数进行运算,将计算结果重新入栈。最终,栈中唯一的元素即为表达式的计算结果。

你可以调用 calcExp 函数并传入不同的逆波兰表达式数组进行测试,观察输出结果。

利用普通栈实现一个有 min方法的栈
以下是用普通栈实现具有 min 方法的栈的 JavaScript 代码:

class MinStack {
  constructor() {
    // 数据栈,用于存储栈的所有元素
    this.dataStack = [];
    // 最小值栈,用于存储栈的最小元素
    this.minStack = [];
  }

  push(value) {
    // 将数据压入数据栈
    this.dataStack.push(value);

    // 判断最小值栈是否为空或者新元素是否小于等于最小值栈的栈顶元素
    if (!this.minStack.length || value <= this.minStack[this.minStack.length - 1]) {
      // 如果是,则将新元素压入最小值栈
      this.minStack.push(value);
    } else {
      // 如果不是,则复制最小值栈的栈顶元素再次压入最小值栈
      this.minStack.push(this.minStack[this.minStack.length - 1]);
    }
  }

  pop() {
    if (!this.dataStack.length) {
      return null;
    }

    // 弹出数据栈和最小值栈的栈顶元素
    this.minStack.pop();
    return this.dataStack.pop();
  }

  min() {
    if (!this.minStack.length) {
      return null;
    }
    return this.minStack[this.minStack.length - 1];
  }
}

// 示例
const stack = new MinStack();
stack.push(3);
stack.push(5);
console.log("Min:", stack.min()); // 输出: Min: 3
stack.push(2);
console.log("Min:", stack.min()); // 输出: Min: 2
stack.pop();
console.log("Min:", stack.min()); // 输出: Min: 3

这个 JavaScript 实现的栈确保了在每次压栈操作时,最小值栈中的元素都与数据栈中的元素相对应。

总结

栈(Stack)是一种遵循后进先出(LIFO)原则的数据结构。栈只能对栈顶进行操作,不关心内部的元素状态。栈通常用于需要后进先出的数据处理场景,栈的特点是只能在栈顶进行添加和删除操作,添加的元素会放在栈顶,删除的元素也会从栈顶位置开始。例如浏览器的历史记录、括号匹配等。

持续学习总结记录中,回顾一下上面的内容:
定义: 栈是一种遵循后进先出(LIFO)原则的有序集合。新添加的或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。
特点: 栈是一种高效的数据结构,只能在栈顶进行添加或删除操作,操作速度较快。适用于后入先出或先进后出的数据保存需求。
实现: 通过对数据进行封装,提供常用方法如push、pop、peek、isEmpty、size和clear来操作栈。
push(item): 添加一个或多个新元素到栈顶。新元素被添加到栈顶后,成为新的栈顶元素,其他元素的位置不变。
pop(): 移除并返回栈顶的元素。该方法会将栈顶元素移除,并返回移除的元素。移除栈顶元素后,原本的次顶元素变为新的栈顶元素。
peek(): 返回栈顶元素,不对栈做修改。该方法不会修改栈,只是将栈顶元素返回,即返回当前在栈顶位置的元素。
isEmpty(): 判断栈内是否无元素,如果栈内无元素,则返回 true,否则返回 false。
size(): 返回栈内元素个数。该方法返回栈中元素的数量或长度。
clear(): 清空栈。该方法会删除栈中所有的元素,将栈恢复到创建时的初始状态。
栈的应用
十进制转任意进制: 通过栈记录余数的方式,实现十进制到其他进制的转换。
逆波兰表达式计算: 利用栈对逆波兰表达式进行计算,遇到运算符时弹出栈顶元素进行运算,结果入栈。
实现有min方法的栈: 使用两个栈,一个用于存储数据,另一个用于存储栈内最小的数据,保持两个栈元素个数相同。

文章来源:https://blog.csdn.net/qq_37255976/article/details/134461630
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。