棧做爲一種數據結構,它能夠應用在不少地方,當你須要常常獲取剛存放進去的數據時,那麼棧這種數據結構將是你的首選。git
棧的實現方式通常有兩種:數組實現和對象實現,這兩種實現方式最終實現的功能都是同樣的,可是在性能上卻有着很大的差異。github
本文將詳細講解這兩種實現方式的差別並用TypeScript將其實現,歡迎各位感興趣的開發者閱讀本文。web
本文講解的是棧用代碼的實現,若是對棧這種數據結構還不是很瞭解的話,能夠移步個人另外一篇文章:棧與隊列數組
棧的核心思想爲後進先出(LIFO),那麼咱們能夠用數組來描述棧。數據結構
接下來,咱們來看下,一個棧都須要具有哪些功能:編輯器
❝咱們分析完棧都須要具有哪些功能後,發現數組中提供了不少現成的API能夠實現上述功能,接下來,跟你們分享下上述功能的實現思路。函數
有了實現思路後,咱們就能夠將上述實現思路轉換爲代碼了。post
private items: any[];
複製代碼
constructor() {
this.items = []; } 複製代碼
// 入棧
push(item:any) { this.items.push(item); } // 出棧 pop() { return this.items.pop(); } // 返回棧頂元素 peek() { return this.items[this.items.length - 1]; } // 判斷棧是否爲空 isEmpty() { return this.items.length === 0; } // 清空棧棧內元素 clear() { this.items = []; } // 獲取棧內元素數量 size():number{ return this.items.length; } // 將棧內元素轉爲字符串 toString(){ return this.items.toString(); } 複製代碼
完整代碼請移步:Stack.ts性能
上述代碼咱們實現了一個棧,接下來咱們往棧中添加幾條數據,測試棧內的方法是否正確執行。測試
const stack = new Stack();
複製代碼
// 入棧
stack.push("第一條數據"); stack.push("第二條數據"); // 出棧 stack.pop(); // 返回棧頂元素 console.log(stack.peek()); // 查看棧大小 console.log(stack.size()); // 判斷棧是否爲空 console.log(stack.isEmpty()); // 返回棧內全部元素 console.log(stack.toString()) // 清空棧 stack.clear() 複製代碼
完整代碼請移步:StackTest.js
執行結果以下
實現一個棧最簡單的方式是經過數組存儲每個元素。在處理大量數據時,咱們須要評估如何操做數據是最高效的。
在使用數組時,大部分方法的時間複雜度都爲O(n),咱們須要迭代整個數組直至找到目標元素,在最壞的狀況下咱們須要迭代數組的每個位置。數組是元素的一個有序集合,爲了保證元素排列有序,它會佔用更多的內存空間。
若是咱們能夠直接獲取元素,佔用更少的內存空間,而且仍然保證全部元素都按照咱們的須要進行排列,就屬於最優解決方案了。
咱們可使用一個對象來存儲全部的棧元素,保證它們的順序而且遵循LIFO原則。接下來咱們來看看如何使用對象來實現棧。
interface StackObj {
[propName: number] : any; } 複製代碼
private items: StackObj;
private count: number; 複製代碼
this.items = {};
this.count = 0; 複製代碼
push(item: any) {
this.items[this.count] = item; this.count++; } 複製代碼
pop() {
if(this.isEmpty()){ return undefined; } this.count--; const result = this.items[this.count]; delete this.items[this.count]; console.log(this.items); return result; } 複製代碼
peek() {
if(this.isEmpty()){ return undefined; } return this.items[this.count - 1]; } 複製代碼
// 判斷棧是否爲空
isEmpty() { return this.count === 0; } // 清空棧內元素 clear() { this.items = []; this.count = 0; } // 獲取棧內元素數量 size():number{ return this.count; } 複製代碼
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; } 複製代碼
完整代碼請移步:ObjStack.ts
上述代碼咱們用對象實現了一個棧,接下來咱們往棧中添加幾條數據,測試棧內的方法是否正確執行。
const stack = new ObjStack();
複製代碼
// 入棧
stack.push("第一條數據"); stack.push("第二條數據"); // 出棧 stack.pop(); // 返回棧頂元素 console.log(stack.peek()); // 查看棧大小 console.log(stack.size()); // 判斷棧是否爲空 console.log(stack.isEmpty()); // 返回棧內全部元素 console.log(stack.toString()) // 清空棧 stack.clear() 複製代碼
完整代碼請移步:StackObjTest.js
執行結果以下
數組大部分方法的時間複雜度都爲O(n),數組中的元素是一個有序集合,爲了保證元素排列有序,它會佔用更多的內存空間。
對象能夠經過key直接訪問到目標元素時間複雜度爲O(1),咱們能夠直接目標元素進行操做,速度明顯比數組快了不少倍。
接下來,咱們經過一個實例來看看這二者在執行速度上的差別。
把十進制轉爲二進制,須要將該十進制除以2並對商取整,直到結果是0爲止。
const decimalToBinaryStack = function (decNumber) {
} 複製代碼
// 傳進來的十進制數
let number = decNumber; 複製代碼
// 餘數
let rem; // 二進制結果 let binaryString = ""; 複製代碼
while (number > 0) {
// 模運算 rem = Math.floor(number % 2); // 將餘數入棧 stack.push(rem); // 當前十進制結果除以二 number = Math.floor(number / 2); } 複製代碼
while (!stack.isEmpty()) {
binaryString += stack.pop().toString(); } 複製代碼
return binaryString;
複製代碼
完整代碼請移步:Examples.js
❝實現代碼如上所述,惟一不一樣的就是一個使用的是對象棧一個使用的數組棧,接下來咱們來看下不一樣棧的運行時間差距。