JS 之 常见线性结构 - 栈结构

本文介绍了线性结构中的数组和栈的概念,提供了JS和TS两种语言的栈实现,并通过示例展示了栈在进制转换和检查有效括号问题中的应用。此外,还讨论了如何使用栈实现包含min函数的栈以及用栈模拟队列的面试题解法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

1. 线性结构

2. 线性结构 之 数组结构 - Array

数组应该很熟悉了吧,这里就不多说,直接看文档 : MDN_Array

二、线性结构 之 栈结构 - Stack

1. 概念

栈(stack),它是一种受限的线性结构,后进先出(LIFO) => 也叫先进后出(FILO)
  • 其限制是仅允许在 表的一端 进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底

  • LIFO(last in first out)表示就是后进入的元素, 第一个弹出栈空间

  • 向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素

  • 从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素

栈常见的操作 :
  • push(element):添加一个新元素到栈顶位置

  • pop():移除栈顶的元素,同时返回被移除的元素

  • peek():返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)

  • isEmpty():如果栈里没有任何元素就返回true,否则返回false

  • size():返回栈里的元素个数。这个方法和数组的length属性很类似

2. 栈结构实现 - 数组

JS实现

class arrayStack {
  // 私有数据
  #dataList = [];

  push(item) {
    this.#dataList.push(item);
  }
  pop() {
    return this.#dataList.pop();
  }
  peek() {
    return this.#dataList[this.#dataList.length - 1];
  }
  size() {
    return this.#dataList.length;
  }
  isEmpty() {
    return this.#dataList.length === 0;
  }
}

// export default arrayStack
 
 
// 测试代码
const s1 = new arrayStack();
s1.push(1);
s1.push('2');
s1.push({name:123});

console.log(s1.peek()); // {name:123}
console.log(s1.pop()); // {name:123}
console.log(s1.pop()); // '2'
console.log(s1.pop()); // 1
console.log(s1.pop()); // undefined
console.log(s1.size()); // 0
console.log(s1.isEmpty()); // true

TS实现

interface starStack<T> {
  // 入栈
  push(item: T): void;
  // 出栈
  pop(): T | undefined;
  // 看栈顶元素
  peek(): T | undefined;
  // 栈个数
  size(): number;
  // 是否为空
  isEmpty(): boolean;
}

class arrayStack<T> implements starStack<T> {
  // 私有数据
  private dataList: T[] = [];

  push(item: T): void {
    this.dataList.push(item);
  }
  pop(): T | undefined {
    return this.dataList.pop();
  }
  peek(): T | undefined {
    return this.dataList[this.dataList.length - 1];
  }
  size(): number {
    return this.dataList.length;
  }
  isEmpty(): boolean {
    return this.dataList.length === 0;
  }
}

// export default arrayStack
 
 
// 测试代码
const s1 = new arrayStack<number>();
s1.push(1);
s1.push(2);
s1.push(3);

console.log(s1.peek()); // 3
console.log(s1.pop()); // 3
console.log(s1.pop()); // 2
console.log(s1.pop()); // 1
console.log(s1.pop()); // undefined
console.log(s1.size()); // 0
console.log(s1.isEmpty()); // true

3. 栈结构实现 - 链表

4. 面试题 - 十进制转其他进制

分析

以十进制 转 二进制为例
对2进行求余,最后从下往上连起来即是答案

代码

import Stack from './IStack';

/**
 * @param decimal 十进制数字
 * @param conversionType 转换进制类型
 * @returns 转换后结果
 */
const baseConversion = (decimal: number, conversionType: number): string => {
  // 1. 创建栈
  const stack = new Stack<number>();
  // 2. 判断是否求余成功
  while (decimal > 0) {
    stack.push(decimal % conversionType);
    decimal = Math.floor(decimal / conversionType);
  }
  // 3. 组装数据
  let binary = '';
  while (!stack.isEmpty()) {
    binary += stack.pop()?.toString();
  }
  // 4. 返回
  return binary;
};

// 十进制 转 二进制
console.log(baseConversion(100, 2)); // 1100100
// 十进制 转 八进制
console.log(baseConversion(100, 8)); // 144
// 十进制 转 十六进制
console.log(baseConversion(100, 16)); // 64

5. 面试题 - 有效的括号

分析

  • 解这道题目主要的问题就是一一对应,典型的用栈来解决

  • 当遇到左括号的时候,立马把右括号放进栈中

  • 当遇到右括号的时候,从栈顶与之对比,如果不一样,则不是有效的括号

栈实现

interface starStack<T> {
  // 入栈
  push(item: T): void;
  // 出栈
  pop(): T | undefined;
  // 看栈顶元素
  peek(): T | undefined;
  // 栈个数
  size(): number;
  // 是否为空
  isEmpty(): boolean;
}

class Stack<T> implements starStack<T> {
  // 私有数据
  private dataList: T[] = [];

  push(item: T): void {
    this.dataList.push(item);
  }
  pop(): T | undefined {
    return this.dataList.pop();
  }
  peek(): T | undefined {
    return this.dataList[this.dataList.length - 1];
  }
  size(): number {
    return this.dataList.length;
  }
  isEmpty(): boolean {
    return this.dataList.length === 0;
  }
}

const strMap: {
  '{': string;
  '(': string;
  '[': string;
} = {
  '{': '}',
  '(': ')',
  '[': ']'
};

const isValid = (str: string): boolean => {
  if (str.length % 2 === 1) return false;
  const stack = new Stack<string>();
  for (let i = 0, length = str.length; i < length; i++) {
    const c: string = str[i];
    switch (c) {
      case '{':
      case '(':
      case '[':
        stack.push(strMap[c]);
        break;
      default:
        if (stack.isEmpty() || c !== stack.peek()) return false;
        stack.pop();
    }
  }
  return stack.isEmpty();
};

const hasCorrespond = (c: string, str: string): boolean => {
  return str.indexOf(c) !== -1;
};

数组实现

const strMap = {
  '{': '}',
  '(': ')',
  '[': ']'
};

const isValid = (str: string): boolean => {
 // 如果是奇数,直接返回
  if (str.length % 2 === 1) return false;
  const stack = []
  for (let i = 0, length = str.length; i < length; i++) {
    const c: string = str[i];
    switch (c) {
      case '{':
      case '(':
      case '[':
      // 如果是左边的,直接推入对应的右边的字符进入栈中
        stack.push(strMap[c]);
        break;
      default:
        // 如果来到这,说明是右边字符
        // 如果此时栈内为空,说明没有匹配的左边字符,直接返回false
        // 如果和栈顶元素不一样,返回false
        if (!stack.length || c !== stack[stack.length -1]) return false;
        // 如果和栈顶元素一样,出栈,进行下一次循环
        stack.pop();
    }
  }
  // 结束循环后,如果栈内还有数据,说明没匹配完,返回false,否则返回true
  return !stack.length;
};

6. 面试题 - 包含min函数的栈

解法一

使用辅助栈 : 辅助栈和主栈时刻保持同样的个数
class MinStack {
  // 主栈
  private stack: number[] = [];
  // 最小栈
  private minStack: number[] = [];

  // 入栈
  push(x: number): void {
    // 主栈直接推入,保持正确的顺序
    this.stack.push(x);

    // 获取最小栈的栈顶元素
    const n = this.minStack[this.minStack.length - 1];
    // 当栈为空时,直接传入
    if (n === undefined) {
      this.minStack.push(x);
    } else {
      // 判断传入的值是否比之前的值小
      const res = n > x ? x : n;
      this.minStack.push(res);
    }
  }
  // 出栈,同时出栈
  pop(): void {
    this.stack.pop();
    this.minStack.pop();
  }
  // 返回主栈的栈顶元素
  top(): number {
    return this.stack[this.stack.length - 1];
  }
  // 返回最小栈的栈顶元素
  min(): number {
    return this.minStack[this.minStack.length - 1];
  }
}

解法二

使用辅助栈 : 辅助栈只保存比之前栈顶元素小的值,当主栈和辅助栈数值相等时,弹出辅助栈
class MinStack {
  // 主栈
  private stack: number[] = [];
  // 最小栈
  private minStack: number[] = [];

  // 入栈
  push(x: number): void {
    // 主栈直接推入,保持正确的顺序
    this.stack.push(x);

    // 获取最小栈的栈顶元素
    const n = this.minStack[this.minStack.length - 1];
    // 当栈为空时,直接传入
    if (n === undefined) {
      this.minStack.push(x);
    } else {
      // 判断栈顶元素大于等于传入的值,则入栈
      if (n >= x) {
        this.minStack.push(x);
      }
    }
  }
  // 出栈
  pop(): void {
    // 主栈直接出栈,
    const res = this.stack.pop();
    // 如果出栈的元素和最小栈栈顶元素相同,则最小栈出栈
    if (res === this.minStack[this.minStack.length - 1]) {
      this.minStack.pop();
    }
  }
  // 返回主栈的栈顶元素
  top(): number {
    return this.stack[this.stack.length - 1];
  }
  // 返回最小栈的栈顶元素
  min(): number {
    return this.minStack[this.minStack.length - 1];
  }
}

7. 面试题 - 用栈实现队列

class MyQueue {
  // 创建入栈
  private enStack: number[];
  // 创建出栈
  private outStack: number[];
  constructor() {
    this.enStack = [];
    this.outStack = [];
  }

  push(x: number): void {
    // 推入元素到入栈
    this.enStack.push(x);
  }

  pop(): number {
    // 如果出栈有值,直接返回
    if (this.outStack.length) return this.outStack.pop()!;
    // 把入栈的元素倒入出栈中
    while (this.enStack.length) {
      this.outStack.push(this.enStack.pop()!);
    }
    // 返回顶部元素
    return this.outStack.pop()!;
  }

  peek(): number {
    // 如果出栈有值,直接返回
    if (this.outStack.length) return this.outStack[this.outStack.length - 1];
    // 把入栈的元素倒入出栈中
    while (this.enStack.length) {
      this.outStack.push(this.enStack.pop()!);
    }
    // 返回顶部元素
    return this.outStack[this.outStack.length - 1];
  }

  empty(): boolean {
    // 当两个栈都为空时,返回true
    return this.enStack.length === 0 && this.outStack.length === 0;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值