数据结构与算法-栈
《学习JavaScript数据结构与算法(第3版)》
栈是一种遵从后进先出(LIFO)原则的有序集合。新添加或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。
栈也被用在编程语言的编译器和内存中保存变量、方法调用等,也被用于浏览器历史记录(浏览器的返回按钮)。
数据结构
创建一个Stack类最简单的方式是使用一个数组来存储其元素。在使用数组时,大部分方法的时间复杂度是O(n)。如果数组有更多元素的话,所需的时间会更长。另外,数组是元素的一个有序集合,为了保证元素排列有序,它会占用更多的内存空间。我们也可以使用一个JavaScript对象来存储所有的栈元素,保证它们的顺序并且遵循LIFO原则。
使用对象实现栈
export default class Stack {
constructor() {
this.count = 0;
this.items = {};
}
// 添加一个(或几个)新元素到栈顶
push(element) {
this.items[this.count] = element;
this.count++;
}
// 移除栈顶的元素,同时返回被移除的元素
pop() {
if (this.isEmpty()) {
return undefined;
}
this.count--;
const result = this.items[this.count];
delete this.items[this.count];
return result;
}
// 返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶的元素,仅仅返回它)
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.count - 1];
}
// 如果栈里没有任何元素就返回true,否则返回false
isEmpty() {
return this.count === 0;
}
// 返回栈里的元素个数
size() {
return this.count;
}
// 移除栈里的所有元素
clear() {
/* while (!this.isEmpty()) {
this.pop();
} */
this.items = {};
this.count = 0;
}
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;
}
}
为了保护私有变量可以使用
weakMap
类型,但是可读性不强,无法继承私有变量。
使用双向链表实现栈
双向链表数据结构请看链表系列
import DoublyLinkedList from './doubly-linked-list';
export default class StackLinkedList {
constructor() {
this.items = new DoublyLinkedList();
}
push(element) {
this.items.push(element);
}
pop() {
if (this.isEmpty()) {
return undefined;
}
const result = this.items.removeAt(this.size() - 1);
return result;
}
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items.getElementAt(this.size() - 1).element;
}
isEmpty() {
return this.items.isEmpty();
}
size() {
return this.items.size();
}
clear() {
this.items.clear();
}
toString() {
return this.items.toString();
}
}
用栈解决问题
十进制转换为二进制
function decimalToBinary(decNumber) {
const remStack = new Stack();
let number = decNumber;
let rem;
let binaryString = '';
while (number > 0) { // 当除数不为0时
rem = Math.floor(number % 2); // 获得余数
remStack.push(rem); // 将余数放到栈中
number = Math.floor(number / 2); // 再继续除2直到为0
}
while (!resStack.isEmpty()) { // 输出转换成的二进制
binaryString += remStack.pop().toString();
}
return binaryString;
}
进制转换算法
可以转换基数为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;
}
平衡圆括号
import Stack from '../stack';
export function parenthesesChecker(symbols) {
const stack = new Stack();
const opens = '([{';
const closers = ')]}';
let balanced = true;
let index = 0;
let symbol;
let top;
while (index < symbols.length && balanced) {
symbol = symbols[index];
if (opens.indexOf(symbol) >= 0) {
stack.push(symbol);
} else if (stack.isEmpty()) {
balanced = false;
} else {
top = stack.pop();
if (!(opens.indexOf(top) === closers.indexOf(symbol))) {
balanced = false;
}
}
index++;
}
return balanced && stack.isEmpty();
}
汉诺塔
三根针,多个圆片,从小到大叠放在一根针上,一次只移动一片,不管在哪根针上,小片必须在大片上面,最后全部移动到另一个针上。
import Stack from '../data-structures/stack';
function towerOfHanoi(plates, source, helper, dest, sourceName, helperName, destName, moves = []) {
if (plates <= 0) {
return moves;
}
if (plates === 1) {
dest.push(source.pop());
const move = {};
move[sourceName] = source.toString();
move[helperName] = helper.toString();
move[destName] = dest.toString();
moves.push(move);
} else {
towerOfHanoi(plates - 1, source, dest, helper, sourceName, destName, helperName, moves);
dest.push(source.pop());
const move = {};
move[sourceName] = source.toString();
move[helperName] = helper.toString();
move[destName] = dest.toString();
moves.push(move);
towerOfHanoi(plates - 1, helper, source, dest, helperName, sourceName, destName, moves);
}
return moves;
}
export function hanoiStack(plates) {
const source = new Stack();
const dest = new Stack();
const helper = new Stack();
for (let i = plates; i > 0; i--) {
source.push(i);
}
return towerOfHanoi(plates, source, helper, dest, 'source', 'helper', 'dest');
}
export function hanoi(plates, source, helper, dest, moves = []) {
if (plates <= 0) {
return moves;
}
if (plates === 1) {
moves.push([source, dest]);
} else {
hanoi(plates - 1, source, dest, helper, moves);
moves.push([source, dest]);
hanoi(plates - 1, helper, source, dest, moves);
}
return moves;
}