數組實現棧與對象實現棧的區別

前言

棧做爲一種數據結構,它能夠應用在不少地方,當你須要常常獲取剛存放進去的數據時,那麼棧這種數據結構將是你的首選。git

棧的實現方式通常有兩種:數組實現和對象實現,這兩種實現方式最終實現的功能都是同樣的,可是在性能上卻有着很大的差異。github

本文將詳細講解這兩種實現方式的差別並用TypeScript將其實現,歡迎各位感興趣的開發者閱讀本文。web

數組實現棧

本文講解的是棧用代碼的實現,若是對棧這種數據結構還不是很瞭解的話,能夠移步個人另外一篇文章:棧與隊列數組

實現思路

棧的核心思想爲後進先出(LIFO),那麼咱們能夠用數組來描述棧。數據結構

接下來,咱們來看下,一個棧都須要具有哪些功能:編輯器

  • 入棧,添加一個新元素至棧頂( 數組的末尾)。
  • 出棧,將棧頂的元素移除並返回被移除的元素。
  • 獲取棧頂元素,獲取當前棧頂元素返回。
  • 判斷棧是否爲空,判斷棧( 數組)內是否有數據。
  • 清空棧,移除棧內全部的元素。
  • 獲取棧大小,返回棧裏的元素個數。
  • 輸出棧內數據,將棧中的全部元素以字符串的形式返回。

咱們分析完棧都須要具有哪些功能後,發現數組中提供了不少現成的API能夠實現上述功能,接下來,跟你們分享下上述功能的實現思路。函數

  • 入棧( push),可使用數組的push方法直接往數組的末尾添加元素。
  • 出棧( pop),可使用數組的pop方法直接移除棧中的元素,該方法會返回當前被移除的元素。
  • 棧頂元素( peek),能夠經過數組的長度-1獲取到數組中的最後一個元素。
  • 棧是否爲空( isEmpty),能夠經過判斷數組的長度是否爲0來實現。
  • 清空棧( clear),能夠將數組直接賦值爲空或者調用出棧方法直至棧中的數據爲空。
  • 棧大小( size),能夠返回數組的長度。
  • 輸出棧內數據,能夠調用數組的toString方法將數組轉換爲字符串。

實現代碼

有了實現思路後,咱們就能夠將上述實現思路轉換爲代碼了。post

  • 新建一個Stack.ts文件
  • 定義棧並規定其類型
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性能

編寫測試代碼

上述代碼咱們實現了一個棧,接下來咱們往棧中添加幾條數據,測試棧內的方法是否正確執行。測試

  • 新建一個StackTest.js文件
  • 實例化一個棧
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原則。接下來咱們來看看如何使用對象來實現棧。

  • 新建一個ObjStack.ts文件
  • 定義棧對象結構
interface StackObj {
 [propName: number] : any; } 複製代碼
  • 定義棧並規定其類型,count用於記錄棧的大小。
private items: StackObj;
private count: number; 複製代碼
  • 在構造器中初始化棧相關變量
this.items = {};
this.count = 0; 複製代碼
  • 入棧,當前棧的大小爲新元素的key。
push(item: any) {
 this.items[this.count] = item;  this.count++; } 複製代碼
  • 出棧,當前棧大小-1,取出棧頂元素,刪除棧頂元素,返回取出的棧頂元素
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; } 複製代碼
  • 返回棧頂元素,以當前棧大小-1爲key獲取其對應的value值。
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

編寫測試代碼

上述代碼咱們用對象實現了一個棧,接下來咱們往棧中添加幾條數據,測試棧內的方法是否正確執行。

  • 新建一個StackObjTest.js文件
  • 實例化一個棧
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循環,判斷當前參數進行除法運算後獲得的值是否爲0,若是不爲0就對當前結果進行模運算,將模運算獲得的值入棧,對當前結果進行除法運算,直至當前結果爲0。
while (number > 0) {
 // 模運算  rem = Math.floor(number % 2);  // 將餘數入棧  stack.push(rem);  // 當前十進制結果除以二  number = Math.floor(number / 2); } 複製代碼
  • while循環,將棧中的數據取出拼接到二進制結果字符串中去
while (!stack.isEmpty()) {
 binaryString += stack.pop().toString(); } 複製代碼
  • 返回二進制結果字符串
return binaryString;
複製代碼

完整代碼請移步:Examples.js

實現代碼如上所述,惟一不一樣的就是一個使用的是對象棧一個使用的數組棧,接下來咱們來看下不一樣棧的運行時間差距。

寫在最後

  • 文中若有錯誤,歡迎在評論區指正,若是這篇文章幫到了你,歡迎點贊和關注😊
  • 本文首發於掘金,未經許可禁止轉載💌
相關文章
相關標籤/搜索