1.栈数据结构
栈是一种遵从后进先出(LIFO)原则的有序集合。新添加或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都靠近栈底。
现实生活中典型的栈的例子就是一摞书或者餐厅里叠放的盘子。
1.1 创建一个基于数组的栈
创建一个类来表示栈。
class Stack {
constructor() {
this.items = [];
}
}
1.2 向栈添加元素
实现方法push,添加一个(或几个)新元素到栈顶。
push(element) {
this.items.push(element);
}
1.3 从栈移除元素
实现方法pop,移除栈顶的元素,同时返回被移除的元素。
pop() {
return this.items.pop();
}
1.4 查看栈顶元素
实现方法peek,返回栈顶的元素,不对栈做任何修改。
peek() {
return this.items[this.items.length -1];
}
1.5 检查栈是否为空
实现方法isEmpty,如果栈里没有任何元素就返回true,否则返回false。
isEmpty() {
return this.items.length === 0;
}
1.6 清空栈元素
实现方法clear,移除栈里的所有元素。
clear() {
this.items = [];
}
1.7 返回栈元素个数
实现方法size,返回栈里的元素个数。该方法和数组的length属性很类似。
size() {
return this.items.length;
}
1.8 完整的Stack类
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;
}
clear() {
this.items = [];
}
size() {
return this.items.length;
}
}
1.9 使用Stack类
首先初始化Stack类,然后验证一下栈是否为空。
const stack = new Stack();
console.log(stack.isEmpty()); // 输出为true
往栈里添加元素5,8
stack.push(5);
stack.push(8);
调用peek方法
console.log(stack.peek()); // 8
再添加一个元素
stack.push(11);
console.log(stack.size()); // 3
console.log(stack.isEmpty()); // false
最后,再添加一个元素
stack.push(15);
console.log(stack.size()); // 4
然后,调用两次pop方法从栈里移除两个元素。
stack.pop();
stack.pop();
console.log(stack.size()); // 2
2.创建一个基于JavaScript对象的Stack类
创建一个Stack类最简单的方式是使用一个数组来存储其元素。在处理大量数据的时候,我们需要评估如何操作数据最高效。在使用数组时,大部分方法的时间复杂度是O(n),另外,数组是元素的一个有序集合,为了保证元素排列有序,它会占用更多的内存空间。
为了解决上述问题,我们可以使用一个JavaScript对象来存储所有的栈元素,保证它们的顺序并且遵循LIFO原则。
首先声明一个Stack类。
class Stack {
constructor() {
this.count = 0;
this.items = {};
}
}
2.1 向栈中插入元素
push(element) {
this.items[this.count] = element;
this.count++;
}
2.2 验证一个栈是否为空和它的大小
size() {
return this.count
}
isEmpty() {
return this.count === 0;
}
2.3 从栈中弹出元素
pop() {
if(this.isEmpty()) {
return undefined;
}
this.count--;
const result = this.items[this.count];
delete this.items[this.count];
return result;
}
2.4 查看栈顶的值并将栈清空
peek() {
if(this.isEmpty()) {
return undefined;
}
return this.items[this.count -1]
}
clear() {
this.items = {};
this.count = 0;
}
2.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;
}
3.用栈解决问题
从十进制到二进制
十进制转化成二进制,可以将十进制数除以2(二进制是满二进一)并对商取整,直到结果是0为止。
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);
}
while(!remStack.isEmpty()) {
binaryString += remStack.pop().toString();
}
return binaryString;
}
console.log(decimalToBinary(233));
console.log(decimalToBinary(10));
console.log(decimalToBinary(1000));
进制转换算法
我们可以修改上述算法,使之能把十进制转换成基数为2~36的任意进制。
function baseConverter(decNumber, base) {
const remStack = new Stack();
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));
console.log(baseConverter(100345,8));
console.log(baseConverter(100345,16));
console.log(baseConverter(100345,35));
5.小结
本节学习了栈这一数据结构的相关知识。使用数组和JavaScript对象实现了栈,还涉及了如何用push和pop往栈里添加和移除元素。
6.经典题目
平衡圆括号
给定一个只包括 '(',')','{','}','[',']'的字符串,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 注意空字符串可被认为是有效字符串。
思路:当遇到右边的括号时,入栈,当遇到左边的括号时,判断栈顶是否与左边的括号匹配,如果不是的话,直接返回false,如果是进行下一次判断,循环结束,如果栈为空,则返回true,如果不为空,则返回false。
function(s) {
const item = [];
const opens = '([{';
const closers = ')]}';
let index = 0;
let symbol;
let top;
while (index < s.length) {
symbol = s[index];
if (opens.indexOf(symbol) >= 0) {
item.push(symbol);
} else {
top = item.pop();
if (!(opens.indexOf(top) === closers.indexOf(symbol))) {
return false;
}
}
index++;
}
return item.length === 0;
};