詩を考えます
金山竹影几千秋,云索高飞水自流;
万里长江飘玉带,一轮银月滚金球。
远自湖北三千里,近到江南十六州;
美景一时观不透,天缘有分画中游。
序文
あなたは、データ構造、ジュニアパートナーを共有するため、この章では、一連の記事-この章では、「第三版のJavaScriptデータ構造とアルゴリズムを学ぶ」 -再読される栈
物語は、胡主席の兄弟はにあなたを取るせてください栈
世界
スタック
スタックとは何ですか?スタックは以下の通りである最初のアウト(LIFO)、内の最後の原則の順序集合。
またはスタックの同じ端部に記憶されている削除する新たな要素を追加し、スタックの上部と呼ばれる、他の端がスタックの底部と呼ばれています。
スタックでは、新しい要素はスタックの最上部に近い、古い要素は、スタックの最下部に近いです。
スタックベースのアレイ
私たちは、スタックベースの配列を作成したスタック構造を理解し、運用ルールます
/**
* 基于数组array的栈Stack
* @author huxiaoshuai
*/
class Stack {
// 初始化
constructor () {
this.items = []
}
}
要素のスタックを保存するために、配列を使用して、
アレイは、私たちは任意の位置に要素を追加したり削除することができ、そのスタックベースが続くLIFOの原則を、私たちは、インサートをカプセル化し、要素を削除します
方法 | 説明 |
---|---|
プッシュ(要素(複数可)) | スタックの最上部に1つ(またはそれ以上)の新しい要素を追加します。 |
ポップ() | 取り除かれた要素を返し、先頭の要素を削除します |
ピーク() | スタック要素の上部を返し、スタックに変更を加えないでください |
isEmpty() | スタックが空であるかどうかを判断し、空の場合、それはそうでない場合はfalse、trueを返します |
クリア() | スタックのすべての要素を削除します |
サイズ() | スタック内の要素の数を返します。 |
コードの実装
class Stack {
// 初始化
constructor () {
this.items = []
}
/**
* push() 添加元素到栈顶
*/
push (element) {
this.items.push(element)
}
/**
* pop() 移除栈顶元素并返回
*/
pop () {
return this.items.pop()
}
/**
* peek() 返回栈顶部元素
*/
peek () {
return this.items[this.items.length - 1]
}
/***
* isEmpty() 检测栈是否为空
*/
isEmpty () {
return this.items.length === 0
}
/**
* size() 返回栈的长度
*/
size () {
return this.items.length
}
/**
* clear() 清空栈元素
*/
clear () {
this.items = []
}
}
スタッククラスを使用します
const stack = new Stack()
console.log(stack.isEmpty()) // true
// 添加元素
stack.push(5)
stack.push(8)
// 输出元素
console.log(stack.peek()) // 8
stack.push(11)
console.log(stack.size()) // 3
console.log(stack.isEmpty()) // false
stack.push(15)
上記の概略的なスタック操作に基づいて
stack.pop()
stack.pop()
console.log(stack.size()) // 2
上記の概略的なスタック操作に基づいて
オブジェクトベースのスタック
Stackクラスを作成する最も簡単な方法は、要素を格納する配列を使用することです。大量のデータを扱う場合、我々はまた、データが最も効率的であるかを評価する必要があります。
アレイを使用する場合、メソッドの時間複雑さのほとんどはO(N)です。理解するシンプル:O(n)を意味し、我々はあなたが最悪のケースを見ては、nは、配列の長さを表す配列を、反復する必要があることが見つかるまで配列要素全体のすべての場所を反復処理する必要があります。配列長く、必要な時間が長くなります。また、配列は要素の規則的な配列を確保するために、より多くのメモリ空間を占有し、要素の順序付きコレクションです。
使用JavaScript对象来存储所有的栈元素,以实现可以直接获取元素,同时占用较少的内存空间,同时保证所有的元素按照我们的需要进行排列,遵循后进先出(LIFO)原则。
代码实现
/**
* 基于对象的Stack类
* @author huxiaoshai
*/
class Stack {
// 初始化
constructor () {
this.items = {}
this.count = 0
}
/**
* push() 向栈中添加元素
*/
push (element) {
this.items[this.count] = element
this.count++
}
/**
* isEmpty() 判断是否为空
*/
isEmpty () {
return this.count === 0
}
/**
* size() 返回栈的长度
*/
size () {
return this.count
}
/**
* pop() 栈顶移除元素并返回
*/
pop () {
if (this.isEmpty()) {
return undefined
}
this.count--
let result = this.items[this.count]
delete this.items[this.count]
return result
}
/**
* peek() 返回栈顶元素,如果为空则返回undefined
*/
peek () {
if (this.isEmpty()) {
return undefined
}
return this.items[this.count - 1]
}
/**
* clear() 清空栈数据
*/
clear () {
this.items = {}
this.count = 0
}
/**
* toString() 实现类似于数组结构打印栈内容
*/
toString () {
if (this.isEmpty()) {
return ''
}
let objStr = `${this.items[0]}`
for (let i = 1; i < this.count; i++) {
objStr = `${objStr},${this.items[i]}`
}
return objStr
}
}
保护数据结构内部元素
私有属性
有时候我们需要创建供其他开发者使用的数据结构和对象时,我们希望保存内部元素,只有使用允许的方法才能修改内部结构。很不幸,目前JS是没有办法直接声明私有属性的,目前业内主要使用一下几种方式实现私有属性。
下划线命名约定
class Stack { constructor () { this._items = {} this._count = 0 } }
这只是约定,一种规范,并不能实际保护数据
基于ES6的限定作用域Symbol实现类
const _items = Symbol('stackItems') class Stack { constructor () { this[_items] = [] } }
假的私有属性,ES6新增的Object.getOwnPropertySymbols方法能够获取类里面声明的所有Symbols属性
基于ES6的WeakMap实现类
/** * 使用WeekMap实现类的私有属性 */ const items = new WeakMap() console.log(items) // WeakMap { [items unknown] } class Stack { constructor () { items.set(this, []) } push (element) { const s = items.get(this) s.push(element) } pop () { const s = items.get(this) const r = s.pop() return r } toString () { const s = items.get(this) return s.toString() } } const stack = new Stack() stack.push(1) stack.push(2) stack.push(3) console.log(stack.toString()) // 1,2,3 console.log(stack.items) // undefined
使用该方式,items是Stack类里的私有属性,但是此种方式代码的可读性不强,而且在扩展该类时无法继承私有属性。
ECMAScript类属性提案
有一个关于JavaScript类中增加私有属性的提案。通过在属性前添加井号(#)作为前缀来声明私有属性。
class Stack {
#count = 0
#items = []
}
使用栈来解决问题
栈的实际应用非常广泛。在回溯问题中,它可以存储访问过的任务或路径、撤销的操作(后续会在讨论图和回溯问题时进一步详细讲解)。栈的使用场景有很多,如汉诺塔问题、平衡圆括号、计算机科学问题:十进制转二进制问题
/**
* decimalToBinary() 实现十进制转二进制的算法
*/
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(10)) // 1010
console.log(decimalToBinary(23)) // 10111
后记
以上就是胡哥今天给大家分享的内容,喜欢的小伙伴记得收藏
、转发
、点击右下角按钮在看
,推荐给更多小伙伴呦,欢迎多多留言交流...
胡哥有话说,一个有技术,有情怀的胡哥!京东开放平台首席前端攻城狮。与你一起聊聊大前端,分享前端系统架构,框架实现原理,最新最高效的技术实践!
押しスキャンコード懸念し、よりハンサム、より美しくヨ!いいえ世間の注目胡兄弟何かが胡兄ヨとの深い交流を続けることができ、言わないように!