数据结构这词你们都不陌生吧,这但是计算机专业人员的必修课专业之一,若是想成为专业的开发人员,必须深刻理解这门课程,在这系列文章里,笔者将使用ES6,让你们熟悉数据结构这门专业课的内容。javascript
到底什么是数据结构?数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。一般状况下,精心选择的数据结构能够带来更高的运行或者存储效率。数据结构每每同高效的检索算法和索引技术有关(来源百度百科)。更多关于数据结构的介绍,你们能够先看看笔者的这篇文章《JavaScript 数据结构:什么是数据结构?》。html
本篇文章笔者将从数据结构最基础的结构开始介绍——stack(栈),笔者将从如下几个方面进行介绍:前端
本篇文章阅读时间预计10分钟。java
栈是一种高效的数据结构(后进先出(LIFO)原则的有序集合),由于数据只能在栈顶添加或删除,因此这样的操做很快,并且容易实现。栈的使用遍及程序语言实现的方方面面。编程语言中的编译器也会使用到堆栈,计算机内存也会使用堆栈来存储变量和方法调用,浏览器的后退功能。除了计算机方面有诸多栈的应用,现实中也有实际例子,好比咱们从一摞书中拿一本书,吃自助餐从一摞盘子里拿最上面的盘子,等等:算法
咱们如何使用JS模拟一个简单的栈呢,首先咱们建立一个stack-array.js文件,声明一个StackArray类,代码以下:编程
class StackArray {
constructor() {
this.items = []; // {1}
}
}复制代码
接下来该怎么作?咱们须要一个可以存储堆栈元素的数据结构,咱们可使用数组结构来完成,同时还须要咱们在堆栈中添加和移除数据元素,因为堆栈后进先出的原则,咱们的添加和删除方法稍微特别些,Stack这个类的实现包含如下几个方法:数组
此方法负责向堆栈添加新元素,其中最重要的特色就是:只能将新元素添加到栈顶,即堆栈的末尾,咱们可使用数组的push方法正好符合这个需求,代码以下:浏览器
push(element) {
this.items.push(element);
} 复制代码
接下来咱们来实现pop()方法,此方法实现删除栈顶的元素,因为遵循LIFO原则,删除的是最后的元素,咱们可使用数组自带的pop方法,代码以下:bash
pop() {
return this.items.pop();
} 复制代码
核心的添加和删除已经完成,如今咱们来实现相关的辅助方法, peek()方法让咱们获取堆栈最后的一个元素,实现代码以下:微信
peek() {
return this.items[this.items.length - 1];
} 复制代码
isEmpty()的方法也十分简单,判断堆栈数组的长度是否为0便可,代码以下:
isEmpty() {
return this.items.length === 0;
} 复制代码
size()方法更简单,使用数组的length方法便可,代码以下
size() {
return this.items.length;
} 复制代码
最后实现最简单的清空方法clear(),将堆栈变量从新置空赋值便可:
clear() {
this.items = [];
}复制代码
最终完成的stack-array.js代码以下:
export default class StackArray {
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;
}
clear() {
this.items = [];
}
toArray() {
return this.items;
}
toString() {
return this.items.toString();
}
}复制代码
接下来咱们建立一个stackdemo.js的文件,引入咱们的stack-array.js文件,咱们一块儿来实践下如何使用咱们建立好的StackArray类,代码以下:
import StackArray from 'stack-array.js';
const stack = new StackArray();
console.log('stack.isEmpty() => ', stack.isEmpty()); // outputs true
stack.push(5);
stack.push(8);
console.log('stack after push 5 and 8 => ', stack.toString());
console.log('stack.peek() => ', stack.peek()); // outputs 8
stack.push(11);
console.log('stack.size() after push 11 => ', stack.size()); // outputs 3
console.log('stack.isEmpty() => ', stack.isEmpty()); // outputs false
stack.push(15);
stack.pop();
stack.pop();
console.log('stack.size() after push 15 and pop twice => ', stack.size()); // outputs 2复制代码
咱们能够新建一个stackdemo.html,引入stackdemo.js(<script type="module" src="stackdemo.js"></script>),打开stackdemo.html便可,堆栈的运行示意以下图所示:
执行push()方法后的效果:
执行pop()方法后的效果:
上一小节咱们基于数组快速实现了栈,咱们清楚数组是有序数组,若是存储大数据,内容过多的话,长度过大的话,会消耗更多的计算机内存,算法的复杂度就会增长(O(n),后面的文章将会介绍),为了解决这个问题,咱们使用更原始的方法进行实现。首先咱们在stack.js文件里声明stack类,代码以下:
class Stack {
constructor() {
this.count = 0;
this.items = {};
}
// methods
} 复制代码
在JS中,对象是一组键值对,咱们能够将使用count变量做为items对象的键,元素是其值,添完新元素后,count变量加1,代码实现以下:
push(element) {
this.items[this.count] = element;
this.count++;
}复制代码
好比咱们能够向空的栈里添加新元素5和8,代码以下:
const stack = new Stack();
stack.push(5);
stack.push(8);复制代码
若是输出Stack对象的items和count,效果以下:
items = {
0: 5,
1: 8
};
count = 2;复制代码
判断栈是否空,咱们只须要判断count变量是否为0便可,代码以下:
isEmpty() {
return this.count === 0;
}复制代码
改写这个方法,咱们首先须要验证堆栈是否为空,若是未空返回undefined,若是不为空,咱们将变量count的值减1,同时删除对应的属性,代码以下:
pop() {
if (this.isEmpty()) {
return undefined;
}
this.count--;
const result = this.items[this.count];
delete this.items[this.count];
return result;
}复制代码
接下来咱们改写其余的方法,完整代码以下:
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];
}
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;
}
}复制代码
虽然咱们类完成了,你们是否是以为有点问题,因为咱们建立的类的属性对于任何开发人员都是公开的,咱们但愿只能在栈顶添加元素,不但愿在其余位置添加元素,可是咱们在Stack类中声明的items和count属性不受保护,这是JS的规则问题,难道咱们没有办法改变了吗?答案是能够,咱们能够ES6加入的新类型Symbol数据类型做为对象的属性具备私有性的特色(关于Symbol数据类型,笔者的这篇文章有过介绍《【ES6基础】Symbol介绍:独一无二的值》),改写基于stack-array.js版本的代码,代码以下:
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;
}
clear() {
this[_items] = [];
}
print() {
console.log(this.toString());
}
toString() {
return this[_items].toString();
}
}
const stack = new Stack();
const objectSymbols = Object.getOwnPropertySymbols(stack);
console.log(objectSymbols.length); // 1
console.log(objectSymbols); // [Symbol()]
console.log(objectSymbols[0]); // Symbol()
stack[objectSymbols[0]].push(1);
stack.print(); // 5, 8, 1复制代码
堆栈在实际的问题中有着各类各样的运用,好比咱们会常用各类软件的撤销操做功能,尤为是Java和C#编程语言使用堆栈来变量存储和方法调用,而且能够抛出堆栈溢出异常,尤为是在使用递归算法时。接下来,咱们亲自动手实现个10进制转2进制的功能。
咱们已经熟悉十进制。 然而,二进制表示在计算机科学中很是重要,由于计算机中的全部内容都由二进制数字(0和1)表示。 若是没有在十进制和二进制数之间来回转换的能力,与计算机进行通讯将会十分困难。要将10进制转换成2进制,咱们须要将要转换数字除以2,再将结果除以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)); // 11101001
console.log(decimalToBinary(10)); // 1010
console.log(decimalToBinary(1000)); // 1111101000”复制代码
刚才咱们实践了十进制转二进制,为了让其更有通用性,由于在实际应用中,不只仅有二进制的转换需求,好比还有8进制、16进制等,如今笔者要给你们留做业了,实现函数baseConverter(decNumber, base),第一参数是要转换的10进制数,第二个参数是须要转换的进制,让其具有10进制数任意转换成2~36进制的需求,欢迎你们在留言区贴代码。
本篇文章,咱们了解了什么是数据结构,并深刻学习了堆栈这个数据结构,以及如何用JS代码实现堆栈,并讲解了不一样的实现方式,同时了解栈在计算机领域的应用,并一块儿实践了一个十进制数转二进制的练习,接下来本系列文章,笔者将带着你们一块儿深刻学习和堆栈相似的队列结构,惟一不一样的就是先进先出(FIFO)。
更多精彩内容,请微信关注”前端达人”公众号!