JavaScript数据结构与算法—— 栈

1次阅读

共计 1502 个字符,预计需要花费 4 分钟才能阅读完成。

我们可以在数组的任何位置上删除或者添加元素,但有时候我们还需要在元素的添加或删除时有更多控制的数据结构,有两种数据结构类似于数组,但在添加或删除元素时更为可控,它们就是栈和队列。本节主要介绍栈。
1. 栈数据结构
栈是一种遵循后进先出(LIFO)原则的有序集合。新添加的或待删除的元素都保存在栈的同一端,叫做栈顶,另一端叫栈底。在栈中,新元素靠近栈顶,旧元素接近栈底。如下图所示:
2. 创建栈
// 首先创建一类表示栈
function Stack(){
let items = []; // 选择数组来保存栈中的元素
// 各种属性和方法
}
下面要为栈声明一些方法:
push() // 添加一个或多个新元素到栈顶
pop() // 移除栈顶元素,同时返回被移除的元素
peek() // 仅仅返回栈顶元素,不对栈做任何修改
isEmpty() // 如果栈中没有元素返回 true,否则返回 false
clear() // 移除栈中所有元素
size() // 返回栈中元素的个数(和数组 length 类似)
2.1 向栈添加元素
this.push() = function(element) {
items.push(element);
}
2.2 从栈移除元素
this.pop = function() {
items.pop();
}
2.3 查看栈顶元素
this.peek = function() {
return items[items.length – 1];
}
2.4 查看栈是否为空
this.isEmpty = function(){
return items.length === 0;
}
this.size = function() {
return items.lenght;
}
2.5 清空和打印栈元素
this.clear = function() {
items = [];
}
this.print = function() {
console.log(items.toString());
}
经过上述的方法的添加,我们就完整的创建了一了个栈 Stack。
3. 栈的应用—十进制转 N 进制
首先我们写出将十进制转为二进制:
function divideBy2(decNum) {
var remStack = new Stack(),
rem,
binaryString = ”;
// 将十进制数除 2 的余数放入一个 stack 中
while(decNum > 0) {
// 取余
rem = Math.floor(decNum % 2);
// 入栈
remStack.push(rem);
decNum = Math.floor(decNum / 2);
}
// 从栈中取出转化为字符串然后连接起来构成二进制
while(!remStack.isEmpty()) {
// 出栈
binaryString += remStack.pop().toString();
}
return binaryString;
}
下面十进制转化 N 进制算法
function divideByN(decNum, n) {
var remStack = new Stack(),
rem,
binaryString = ”,
digits = ‘0123456789ABCDEF’;
// 将十进制数除 N 的余数放入一个 stack 中
while(decNum > 0) {
// 取余
rem = Math.floor(decNum % n);
// 入栈
remStack.push(rem);
decNum = Math.floor(decNum / n);
}
// 从栈中取出转化为字符串然后连接起来构成二进制
while(!remStack.isEmpty()) {
// 出栈 使用 digits 方便在 16 进制中做个对应转化
binaryString += digits[remStack.pop()];
}
return binaryString;
}

正文完
 0

[ JavaScript ] 数据结构与算法 —— 栈

1次阅读

共计 2892 个字符,预计需要花费 8 分钟才能阅读完成。

前言
JavaScript 是当下最流行的编程语言之一,它可以做很多事情:

数据可视化(D3.js,Three.js,Chart.js);
移动端应用 (React Native,Weex,AppCan,Flutter,Hybrid App, 小程序);
服务端 (Express.js,Koa2);
桌面应用 (Electron,nw.js);
游戏,VR,AR(LayaAir,Egret,Turbulenz,PlayCanvas);
等等。。。

而且目前大部分编程语言的高级应用都会用到数据结构与算法以及设计模式。
本篇主要有三部分

什么是栈
栈的实现
栈的应用

源码地址:https://github.com/yhtx1997/S…
什么是栈
较官方解释
栈是一种遵从后进先出(LIFO)原则的有序集合。新添加的或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。

个人理解
上面看不懂?没关系,接下来我用生活中比较常见的事来解释。大家应该都搬过家,旅行,或者上学时住宿,这个过程中都会用到行李箱。

栈:现在这个旅行箱就是我们的栈;
栈里边的元素:我们往旅行箱放叠好的衣服或其他的东西,这些东西就是栈里边的元素;
栈底:我们放衣服时都是是先放旅行箱的最底下,不可能说什么东西都没有就让它飘着是吧,这个旅行箱最底下衣服在的位置就是栈底
栈顶:相对应的最后放进去的一件衣服的位置就是栈顶;
压栈:把元素放进去(把衣服放进旅行箱)的动作就是压栈;
出栈:把元素拿出来(把衣服拿出来)的动作就是出栈;
堆栈溢出:一般是指栈满了放不下了(旅行箱满了怎么塞塞不下了);
后进先出:放衣服时一件一件的放,但是往外拿衣服的时候是先拿放的时候最后放的,最后拿出来的是放的时候最先放的;可能有人拿衣服直接翻到最下边然后拿出来,这个过程可以看做,先将除了栈底的元素依次出栈并保存,等拿到栈底的元素在依次压栈,把元素放回去
有序集合:额,就是一个挨着一个,有顺序的,一些有相同属性的东西

栈的实现

添加元素
删除元素
返回栈顶元素
是否为空
元素数量
清空元素
打印所有元素

class Stack {
constructor() {
this.count = 0; // 长度
this.items = {}; // 栈
}
push(element) {
// 添加元素
}
pop() {
// 删除元素
}
peek() {
// 返回栈顶元素
}
isEmpty() {
// 是否为空
}
size() {
// 元素数量
}
clear() {
// 清空元素
}
toString() {
// 打印所有元素
}
}
添加元素
push(element) {
this.items[this.count] = element;
this.count++; // 长度加 1
}
删除元素
pop() {
if (this.isEmpty()) {// 如果是空直接返回 undefined
return undefined;
}
this.count–;
let item = this.items[this.count];
delete this.items[this.count];
return item
}
返回栈顶元素
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.count – 1];
}
是否为空
isEmpty() {
return this.count === 0;
}
元素数量
size() {
return this.count;
}
清空元素
clear() {
this.count = 0;
this.items = {};
}
打印所有元素
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;
}
栈的应用

进制转换
括号匹配检验
迷宫求解

进制转换
栈因为是先进后出,所以如果将一组数据全部压栈,再出栈并保存每次出栈的元素,这样一来相当于将这一组元素的顺序进行倒序。十进制转换二进制的过程就是一个数除以 2,取余数,最后将余数结果进行倒序排列。现在栈可以进行倒序,而进制转换需要倒序,所以就可以将栈应用到进制的转换中。代码如下:
function DecimalToBinary(number) {
let stack = new Stack(); // 新建栈
let rem = 0; // 余数
let binary = ”; // 结果
while (number > 0) {
rem = Math.floor(number % 2); // 取余数
stack.push(rem); // 将余数压栈
number = Math.floor(number / 2); // 去掉余数除以二
}
while (!stack.isEmpty()) {// 不为空
binary += stack.pop(); // 将元素全部出栈
}
return binary;
}
括号匹配检验
正常括号嵌套是这样的
{{{[([({()})])]}}}
可以发现,在某个位置分为了左右两部分,右边第一个,与左边最后一个相对应 右边最后一个与左边第一个相对应 左侧相当于进行了倒序,而倒序就可以用栈来解决
我们可以将所有的左侧括号都依次压入栈中,然后依次判断右侧是否与栈顶元素相匹配 但是相匹配的括号并不相等
可以采用键值对的形式存储一下
{
‘}’: ‘{‘,
‘]’: ‘[‘,
‘)’: ‘(‘
}

或者下标的形式
[‘{‘,'[‘,'(‘]
[‘}’,’]’,’)’]
最终代码如下
function parenthesesChecker(symbols) {
let stack = new Stack(); // 新建栈
let balanced = true; // 检验括号匹配是否正确
const leftBrackets = ‘{[(‘; // 左侧的括号
const rightBrackets = ‘}])’; // 右侧的括号
for (let i = 0; i < symbols.length && balanced; i++) {
current = symbols[i]; // 取单个符号
if (leftBrackets.indexOf(current) >= 0) {// 如果是左侧的括号
stack.push(current); // 将其压栈
} else if (stack.isEmpty()) {// 不是左侧括号,且栈为空,括号匹配错误
balancd = false;
} else {// 不是左侧括号,且栈不为空,视为没有新的左侧括号,剩下的都是右侧括号
let top = stack.pop();
if (!(leftBrackets.indexOf(top) === rightBrackets.indexOf(current))) {
// 检查栈顶(最后一个左侧括号)符号在 leftBrackets 的位置以及当前符号在 rightBrackets 的位置,位置相同视为配对成功
balanced = false;
}
}
}

return balanced; // 返回结果
}
迷宫求解
这个比较复杂,之后会写个小实例,敬请期待 ……

正文完
 0