1.JavaScript数据结构与算法(栈)

栈是一种遵从后进先出(LIFO)原则的有序集合,新添加或待删除的元素都保存在栈的同一端,称为栈顶,另一端就叫栈底
栈也被用在编程语言的编译器和内存中保存变量、方法调用等,也被用于浏览器历史记录(浏览器的返回按钮)

1.创建一个基于数组的栈

class Stack {
  constructor() {
    this.items = []
  }
}

·push(element(s)) 添加一个(或几个)元素到栈顶
·pop() 移除栈顶的元素,同时返回被移除的元素
·peek() 返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶的元素,仅仅返回它)
·isEmpty() 如果栈里没有任何元素就返回true,否则返回false
·clear() 移除栈里所有元素
·size() 返回栈里元素的个数。

1.向栈添加元素

  push(element) {
    this.items.push(element)
  }

2.从栈移除元素

  pop() {
    return this.items.pop()
  }

3.查看栈顶元素

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

4.检查栈是否为空

 isEmpty() {
    return this.items.length === 0
  }

5.清空栈元素

  clear() {
    this.items = []
  }

6.使用Stack类

const stack = new Stack()
console.log(stack.isEmpty(), 'isEmpty')
stack.push(1)
stack.push(2)
stack.push(3)
console.log(stack.peek(), 'peek')
console.log(stack.size(), 'size')
console.log(stack.pop(), 'pop')

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

在使用数组时,大部分的方法的时间复杂度是O(n)。O(n)的意思是,需要迭代震哥哥数组直到找到要找到的那个元素,在最坏的情况下需要迭代数组的所有位置。另外,数组是元素的一个有序集合,为了保证元素排列有序,它会占用更多的内存空间。

class Stack {
  constructor() {
    this.count = 0;
    this.items = {};
  }
}

在这个版本的Stack类中,将使用一个count属性来帮助记录栈的大小(也能帮助从数据结构中添加和删除元素)

1.向栈中插入元素

  push(element) {
    this.items[this.count] = element
    this.count ++
  }

2.验证一个栈是否为空和它的大小

  size() {
    return this.count
  }
  isEmpty() {
    return this.count === 0
  }

3.从栈中弹出元素

  pop() {
    if(this.isEmpty()) {
      return undefined;
    }
    this.count --;
    const result = this.items[this.count];
    delete this.items[this.count];
    return result;
  }

4.查看栈顶的值并将栈清空

  peek() {
    if(this.isEmpty()) {
      return undefined;
    }
    return this.items[this.count - 1]
  }
 clear() {
    this.count = 0;
    this.items = {}
  }

5.创建toString方法

  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
  }

除了toString方法,其他方法的时间复杂度均为O(1),代表可以直接找到目标元素并对其进行操作

3.保护数据结构内部元素

ES2015类是基于原型的,尽管基于原型的类能节省内存空间并在扩展方面优于函数的类,但这种方式不能声明私有属性(变量)或方法。下面看看其他使用JavaScript来实现私有属性的方法

1.下划线命名约定

class Stack {
  constructor() {
    this._count = 0;
    this._items = {};
  }
}

下划线命名约定就是在树形名称之前加上一个下划线,不过这种方式只是一种约定,并不能保护数据。

2.用ES2015的限定作用域Symbol实现类

ES2015新增了一种叫做Symbol的基本类型,它是不可变的,可以用作对象的属性

const _items = Symbol('stackItems');
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
  }
  cleart() {
    this[_items] = []
  }
}

这种方法创建了一个加的私有属性,因为ES2015新增的Object.getOwnPropertySymbols方法能够获取到类里卖弄生命的所有Symbols属性

const stack = new Stack();
stack.push(2);
stack.push(3);
stack.push(4);
let objectSymbols = Object.getOwnPropertySymbols(stack);
console.log(objectSymbols.length); // 1
console.log(objectSymbols); // [ Symbol(stackItems) ]
console.log(objectSymbols[0]); // Symbol(stackItems) 
stack[objectSymbols[0]].push(5); 
stack.print(); // 2,3,4,5

从以上代码可以看到,访问stack[objectSymbols[0]]是可以得到_items的,并且,_items是一个数组,可以进行任意的数组操作。

3.用ES2015的WeakMap实现类

有一种数据类型可以确保属性是私有的,就是WeakMap。WeapMap可以存储键值对,其中键是对象,值可以是任意数据类型。

const items = new WeakMap();
class Stack {
  constructor() {
    items.set(this, [])
  }
  push(element) {
    const s = items.get(this);
    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() {
    const s = items.get(this);
    while(s.length) {
      s.pop()
    }
  }
}
const stack = new Stack()
console.log(stack.peek(), 'peek')
stack.push(2);
stack.push(4);
console.log(stack.isEmpty(), 'isEmpty');
console.log(stack.size(), 'size');
stack.clear();
console.log(stack.size())

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

4.用栈解决问题

栈的实际应用非常广泛。在回溯问题中,它可以存储访问过的任务或路径、撤销的操作。Java和C#用栈来存储变量和方法调用,特别是处理递归算法时,有可能抛出一个栈溢出异常

从十进制到二进制

function decimalToBinary(decNumber) {
  const remStack = new Stack();
  let str = '';
  let rem;
  let number = decNumber;
  while(number > 0) {
    rem = number % 2;
    remStack.push(rem)
    number = Math.floor(number / 2);
  }
  while(!remStack.isEmpty()) {
    str = `${str}${remStack.pop()}`
  }
  return str
}
console.log(decimalToBinary(8));
console.log(decimalToBinary(7));
console.log(decimalToBinary(1023));

进制转换算法

function baseConverter(decNumber, base) {
  const remStack = new Stack();
  const diggits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  let number = decNumber;
  let rem;
  let str = '';
  if(base < 2 || base > 36) {
    return ''
  }
  while(number > 0) {
    rem = number % base;
    remStack.push(diggits[rem]);
    number = Math.floor(number / base)
  }
  while(!remStack.isEmpty()) {
    str = `${str}${remStack.pop()}`
  }
  return str;
}
console.log(baseConverter(100, 32));
console.log(baseConverter(10231, 32));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值