JavaScript数据结构之栈的认识

栈数据结构

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

接下来,我们创建一个类来表示栈。新建一个 stack-array.js 文件

const Stack {
  constructor() {
    this.items = [];
  }
  // 下面是为栈声明一些方法
  push(element) {
    // 添加新元素到栈顶
    this.items.push(element);
  }
  pop() {
    // 移除栈顶的元素,同时返回被移除的元素
    return this.items.pop();
  }
  peek() {
    // 返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶的元素,仅仅返回它)
    return this.items[this.items.length - 1];
  }
  isEmpty() {
    // 如果栈里没有任何元素就返回true,否则返回false。
    return this.items.length === 0;
  }
  clear() {
    // 移除栈里的所有元素
    this.items = [];
  }
  size() {
    // 返回栈里的元素个数。该方法和数组的length属性很类似
    return this.items.length;
  }
}

然后通过一些简单的测试代码来使用Stack类

const Stack = new Stack();
console.log(stack.isEmpty()); // 输出为true
stack.push(5);
stack.push(8);
console.log(stack.peek()); // 输出8
stack.push(11);
console.log(stack.size()); // 输出3
console.log(stack.isEmpty()); // 输出false
stack.push(15);
stack.pop();
stack.pop();
console.log(stack.size()); // 输出2

日常开发中,我们如果要创建别的开发者也可以使用的数据结构或对象时,我们希望保护内部的元素,只有我们暴露出的方法才能修改内部结构。对于Strack类来说,要确保元素只会被添加到栈顶,而不是栈底或其他任意位置(比如栈的中间)。但是,我们刚才在Stack类中声明的items属性并没有得到保护,因为JavaScript的类就是这样工作的。

看下面的代码

const stack = new Stack();
console.log(Object.getOwnPropertyNames(stack)); // 输出["items"]
console.log(Object.keys(stack)); 输出["items"]
console.log(stack.items); // 输出[]

我们可以直接访问到items属性,也可以直接对这个属性赋新的值。我们希望Stack类中用户只能访问我们在类中暴露的方法。下面来看看使用JavaScript来实现私有属性的方法。

 使用weakMap数据结构,修改我们的stack-array.js文件

WeakMap可以存储键值对,其中键是对象,值可以是任意数据类型

const items = new WeakMap(); // 声明一个WeakMap类型的变量items
const Stack {
  constructor() {
    items.set(this, []); // 在constructor中,以this(Stack类自己的引用)为键,把代表栈的数组存入items
  }
  push(element) {
    const s = items.get(this); // 从WeakMap中取出值,即以this为键,从items中取值
    s.push(element);
  }
  pop() {
    const s = items.get(this);
    const r = s.pop();
    return r;
  }
  peek() {
    const s = items.get(this);
    return s[s.length - 1];
  }
  isEmpty() {
    const s = items.get(this);
    return s.length === 0;
  }
  size() {
    const s = items.get(this);
    return s.length;
  }
  clear() {
    items.set(this, []);
  }
  print() {
    cosnt s = items.get(this);
    console.log(s.toString());
  }
}

再来执行下面的代码

const stack = new Stack();
console.log(Object.getOwnPropertyNames(stack)); // 输出[]
console.log(Object.keys(stack)); 输出[]
console.log(stack.items); // 输出undefined
stack.push(5);
stack.push(8);
stack.print(); // 输出5,8

由此,items在Stack类里是真正的私有属性。但是采用这种方法,代码的可读性不强,而且在扩展该类时无法继承私有属性。

事实上,我们不能像在其他编程语言中一样声明私有属性和方法。虽然有很多种方法都可以达到相同的效果,但无论是在语法还是性能层面,这些方法都有各自的有点和缺点。

 下面举一个应用栈的小实例(进制转换)

从十进制到二进制

日常开发中,我们主要使用十进制。但在计算科学中,二进制非常重要,因为计算机里的所有内容都是用二进制数字表示的(0和1)。要把十进制转化成二进制,我们可以将该十进制数除以2(二进制是满二进一)并对商取整,直到结果是0为止。例如,把十进制的数10转化成二进制的数字,过程大概如下:

下面写一下实现十进制转二进制的方法

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); // 让number除以2,并向下取整以便循环运算
  }

  while(!remStack.isEmpty()) {
    binaryString += remStack.pop().toString(); // 用pop方法把栈中的元素移除,并把出栈的元素连接成字符串
  }

  return binaryString;
}

console.log(decimalToBinary(233)); // 输出11101001

 接下来我们把上面的方法改造一下,实现十进制转换成基数为 2~36的任意进制。

function baseConverter(decNumber, base) {
  const remStack = new Stack();
  // 从十一进制开始,字母表中的每个字母将表示相应的基数。字母A代表基数11,字母B代表基数12,以此类推
  const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  let number = decNumber;
  let rem;
  let baseString = '';

  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()) {
    baseString += digits[remStack.pop()]; // 对栈中的数字做转化
  }

  return baseString;
}

console.log(baseConverter(100345, 2)); // 输出 11000011111111001
console.log(baseConverter(100345, 8)); // 输出 303771
console.log(baseConverter(100345, 16)); // 输出 187F9
console.log(baseConverter(100345, 35)); // 输出 2BWO

猜你喜欢

转载自blog.csdn.net/longgege001/article/details/109099226