Javascript常见数据结构——栈
一、概述
数据结构是指 相互之间存在一种或多种特定关系的数据组成的集合
二、栈
1.创建数据结构
栈 是一种遵循 后进先出 原则的有序数据集
栈顶 栈底 入栈 出栈
栈结构也被用在编译器和内存中保存变量
功能需求:
入栈也叫压栈 栈顶添加数据 push
出栈也叫弹栈 栈顶删除并返回数据 pop
返回栈顶数据 peek
清空栈 clear
返回栈中数据个数
自定义栈
class Stack{
constructor() {
this.items =[]
}
// 入栈 压栈
push(data){
this.items.push(data)
}
// 出栈 弹栈
pop(){
if(!this.size()){
console.warn('当前栈为空,默认返回undefined')
}
return this.items.pop()
}
// 返回栈顶数据
peek(){
if(!this.size()){
console.warn('当前栈为空,默认返回undefined')
}
return this.items[this.items.length-1]
}
// 清空栈
clear(){
this.items = []
}
//返回栈中数据个数
size(){
return this.items.length
}
}
当前栈还是存在一些问题 外界是可以轻松拿到 items 的值的可以直接通过数组的api 进行修改 所以在这里 需要进行改进一下
// 改进 1
let Stack = (function (){
let items = []
return class {
// 入栈 压栈
push(data) {
items.push(data)
}
// 出栈
pop() {
if (!this.size()) {
console.warn('当前栈为空,默认返回undefined')
}
return items.pop()
}
// 返回栈顶数据
peek() {
if (!this.size()) {
console.warn('当前栈为空,默认返回undefined')
}
return items[items.length - 1]
}
// 清空栈
clear() {
items = []
}
//返回栈中数据个数
size() {
return items.length
}
}
})()
通过当前栈中创建出来的所有实例对象 他们的值都能相互访问到 并不是我们所期望的这个时候 我们可以采用 Symbol 作为变量名 Symbol是独一无的虽然 外界可以看到 里边的值 但是外界直接访问是访问不到的
// 改进 2
let Stack = (function (){
let symbol = Symbol()
return class {
constructor() {
this[symbol] = []
}
// 入栈 压栈
push(...rest) {
// 利用Es6 新增的剩余参数 可以优化入栈 一次性可以push多个
this[symbol].push(rest)
}
// 出栈
pop() {
if (!this.size()) {
console.warn('当前栈为空,默认返回undefined')
}
return this[symbol].pop()
}
// 返回栈顶数据
peek() {
if (!this.size()) {
console.warn('当前栈为空,默认返回undefined')
}
return this[symbol][this[symbol].length - 1]
}
// 清空栈
clear() {
this[symbol] = []
}
//返回栈中数据个数
size() {
return this[symbol].length
}
}
})()
2.使用栈来实现十进制转二进制
// 十进制二进制转换
function baseConverter2(number){
let stack = new Stack()
let result = ''
// 余数入栈
while(number){
// 取余数
stack.push(number%2)
// 取商 取整运算
number = number/2|0
}
// 余数出栈
while (stack.size()){
result += stack.pop()
}
return result
}
3.使用栈来实现十进制转多进制
这里我们采用了闭包
特点 :
1.两个函数 内部函数 使用了外部函数的参数和变量 导致参数不会被回收 使其一直保存在内存中
2.并且外部无法访问到闭包内的内容 不会污染全局变量
3.闭包内可以访问函数内部的变量
缺点 :
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页性能问题 在ie中 会导致 内存泄漏 解决方法是 再退出函数之前 将不使用的局部变量删除
// 十进制 多进制转换
let baseConverter = (function(){
let sign = '';
for (let i=0;i<10;i++)sign+=i;
for (let i=97;i<123;i++){
sign+=String.fromCharCode(i)
}
// 第一个参数表示 需要转换的十进制数 第二个参数表示 需要转换的进制数
return function (number,base=2){
let stack = new Stack()
let result =''
// 特殊情况 0 的转换
if(number===0)return "0"
// 余数 入栈 压栈
while(number){
// 取余数 字符串是可以通过下标进行取值的
stack.push(sign[number%base])
// 取值 位运算符 x|0 如果是整数 返回本身 如果是小数 那那么会舍去小数
number =number/base|0
}
// 余数 出栈 弹栈
while(stack.size()){
result +=stack.pop()
}
return result
}
})()
4.使用栈来实现判断是否为回文字符串
判断是否是回文字符串
思路 将字符串倒过来 与 原字符串 进行对比
上海自来水来自海上
蜜蜂采蜂蜜 我爱你你爱我
// 通过栈判断是否为回文字符串
function isPalindrome(str){
let stack = new Stack();
let pStr = "";
// 按照顺序入栈
// 此时的...rest 不是一组数据 它是一种结构
// 类似于 "1" "2" "3" "4"
stack.push(...str)
// 按照顺序出栈
while(stack.size()){
pStr +=stack.pop()
}
return pStr === str
}
5.判断字符串括号是否匹配
// 判断符号是否闭合
// 思路 最先出现的右括号 一定与最后出现的左括号匹配
function isSignMatch(str) {
// 定义基础符号
let startSign = ['{', '[', '('],
endSign = ['}', ']', ')']
// 定义栈
let stack = new Stack()
// 逐字检查
for (let i = 0; i < str.length; i++) {
let s = str[i]
// 左括号 入栈操作
if (startSign.includes(s)) {
stack.push(s)
}
// 右括号
if (endSign.includes(s)) {
// 最后出现的左括号(左括号执行出栈操作)要和 最先出现的右括号匹配
if (startSign.indexOf(stack.pop()) !== endSign.indexOf(s)) {
return false
}
}
}
// 最后判断 左括号是否出栈完毕
return stack.size()===0;
}