數(shù)據(jù)結(jié)構(gòu)淺析:棧

數(shù)據(jù)結(jié)構(gòu)淺析:棧

每一位想在大型技術(shù)公司申請職位的開發(fā)者酌摇,如果花了數(shù)天時間練習常見算法面試題都會說:

“哇倦始。我真的覺得數(shù)據(jù)結(jié)構(gòu)很難”

數(shù)據(jù)結(jié)構(gòu)是什么?為什么它們?nèi)绱酥匾塍希烤S基百科給出了簡明而準確的答案:

數(shù)據(jù)結(jié)構(gòu)是在計算機為了組織數(shù)據(jù)的特定方式,目的是為了高效地使用數(shù)據(jù)荚虚。

注意這里的關(guān)鍵詞是高效薛夜,一個在分析不同的數(shù)據(jù)結(jié)構(gòu)時你會常常聽說的詞語。

這些數(shù)據(jù)結(jié)構(gòu)提供不同的方式來存儲數(shù)據(jù)版述,以便快速梯澜、動態(tài)地搜索、插入院水、移除腊徙、更新數(shù)據(jù)。

盡管和計算機一樣強大檬某,它們?nèi)匀恍枰噶畈拍芡瓿扇魏斡幸饬x的任務(至少在人工智能出來之前是這樣)撬腾。在此之前你必須給它們盡可能明確、高效的指令恢恼。

如同你可以使用50種不同的方式來建造房子一樣民傻,你也可以使用50種不同方式來組織數(shù)據(jù)。幸運的是场斑,很多聰明的人已經(jīng)構(gòu)建很多偉大的經(jīng)過時間考驗的數(shù)據(jù)結(jié)構(gòu)漓踢。你所要做的就是學習它們是什么,它們?nèi)绾喂ぷ髀┮约霸鯓幼詈玫氖褂盟鼈冃搿O旅媸且恍┳畛S玫臄?shù)據(jù)結(jié)構(gòu)列表。我將在后續(xù)文章中逐一介紹它們--本文先介紹棧青责。盡管它們常常有共同之處挺据,但是為了適用于特定的場景取具,這些數(shù)據(jù)結(jié)構(gòu)中每一個都有一些細微差別:

  • 棧(Stacks)
  • 隊列(Queues)
  • 鏈表(Linked Lists)
  • 集合(Sets)
  • 樹(Trees)
  • 圖(Graphs)
  • 哈希表(Hash Tables)

你會遇到這些數(shù)據(jù)結(jié)構(gòu)變體,如雙向鏈表扁耐,B-樹以及優(yōu)先隊列暇检。但是一旦你理解了這些核心的實現(xiàn),要理解這些變體就會很簡單婉称。

所以就從分析棧開始我們的數(shù)據(jù)結(jié)構(gòu)之旅的第一部分块仆。

  • 字面意思就是一堆數(shù)據(jù)(就像一堆煎餅)
  • 添加(push)--總是添加到棧頂
  • 移除(pop)--總是從棧頂開始移除
  • 模式類型:先進后出(LIFO)
  • 使用范例:使用瀏覽器中的后退和前進按鈕

在許多編程語言中,數(shù)組內(nèi)建了棧的功能王暗。但是為了理解透徹悔据,你將使用一個 JavaScript 對象來重新實現(xiàn)一個棧。

首先你需要創(chuàng)建一個棧用于存儲你訪問過的每一個站點瘫筐,并且在你的棧中創(chuàng)建一個方法用于跟蹤你當前的位置:

class Stack {
  constructor(){
    this._storage = {};  
    this._position = -1; // 0 indexed when we add items!
  }
  top(){
    return this._position;
  }
}
let browserHistory = new Stack();

注意變量名稱前的下劃線告訴其他開發(fā)者這些變量是私有的蜜暑,只能通過該類的方法操作铐姚,不能外部直接操作策肝。例如,我不能執(zhí)行這樣的代碼:

browserHistory._position = 2.

這就是為什么為我要創(chuàng)建 top() 方法用于返回棧的當前位置隐绵。

在本例中之众,你訪問的每一個網(wǎng)站都將被存放到你的瀏覽器歷史棧中。為了幫助你跟蹤它在棧中的位置依许,你可以用每一個站點的位置作為關(guān)鍵字棺禾,然后每添加一個站點,位置就遞增峭跳。在這里我通過 push 方法實現(xiàn):

class Stack {
  constructor(){
    this._storage = {}; 
    this._position = -1;
  }
  push(value){
    this._position++; 
    this._storage[this._position] = value 
  }
  top(){
    return this._position;
  }
}
let browserHistory = new Stack();
browserHistory.push("google.com"); //navigating to Medium
browserHistory.push("medium.com"); // navigating to Free Code Camp
browserHistory.push("freecodecamp.com"); // navigating to Netflix
browserHistory.push("netflix.com"); // current site

上面的代碼執(zhí)行以后膘婶,storage 對象將是這樣的:

{
  0: “google.com”
  1: “medium.com”
  2: “freecodecamp.com”
  3: “netflix.com”
}

所有想象一下,你現(xiàn)在瀏覽 Netflix蛀醉,但是因為在 Free Code Camp 上還有一個困難的遞歸問題沒有完成而愧疚悬襟。你決定點擊返回按鈕,回去解決它拯刁。

這一行為在你的棧中是怎么表現(xiàn)的的脊岳?使用 pop:

class Stack {
 
  constructor(){
    this._storage = {};
    this._position = -1;
  }
 
  push(value){
    this._position++; 
    this._storage[this._position] = value; 
  }
 
  pop(){
    if(this._position > -1){
      let val = this._storage[this._position]; 
      delete this._storage[this._position]; 
      this._position--;
      return val;
    }
  }
  top(){
    return this._position;
  }
}
let browserHistory = new Stack();
browserHistory.push("google.com"); //navigating to Medium
browserHistory.push("medium.com"); // navigating to Free Code Camp
browserHistory.push("freecodecamp.com"); // navigating to Netflix
browserHistory.push("netflix.com"); //current site
browserHistory.pop(); //Returns netflix.com
//Free Code Camp is now our current site

通過點擊返回按鈕,將最近添加到瀏覽器歷史的站點移除垛玻,并瀏覽當前處于棧頂?shù)恼军c割捅。你同時遞減了 position 變量,所以你可以準確地表示出你當前處于瀏覽歷史的什么位置帚桩。當然這一切都只有你的棧非空時才會發(fā)生亿驾。

到目前為止看起來都還不錯,但是被移除的對象呢账嚎?

當你解決完問題時莫瞬,決定獎賞自己回去繼續(xù)瀏覽 Netflix参淹,點擊前進按鈕就可以。但是你的棧里有 Netflix 嗎乏悄?為了節(jié)省空間你已經(jīng)將它刪除了浙值,在你的瀏覽器歷史中再也沒有這個站點。

幸運的是檩小,pop 函數(shù)將它返回了开呐,所以也許你可以在別的地方將它存起來以備后用。用另一個棧存起來怎么樣规求!

你可以創(chuàng)建一個 “forward” 棧用于存儲每一個從瀏覽器歷史中移除的站點筐付。所以當你想再次瀏覽的時候,只需要將它們從 forward 棧 彈出來就可以阻肿,并將它們重新壓入瀏覽器歷史棧:

class Stack {
 
  constructor(){
    this._storage = {};
    this._position = -1;
  }
 
  push(value){
    this._position++; 
    this._storage[this._position] = value; 
  }
 
  pop(){
    if(this._position > -1){
      let val = this._storage[this._position]; 
      delete this._storage[this._position]; 
      this._position--;
      return val;
    }
  }
top(){
    return this._position;
  }
}
let browserHistory = new Stack();
let forward = new Stack() //Our new forward stack!
browserHistory.push("google.com");
browserHistory.push("medium.com");
browserHistory.push("freecodecamp.com");
browserHistory.push("netflix.com");
//hit the back button
forward.push(browserHistory.pop()); // forward stack holds Netflix
// ...We crush the Free Code Camp problem here, then hit forward!
  browserHistory.push(forward.pop());
//Netflix is now our current site

你已經(jīng)使用一種數(shù)據(jù)結(jié)構(gòu)重新實現(xiàn)了瀏覽器基本的前進瓦戚、后退導航!

為了理解透徹丛塌,我們假設你從 Free Code Camp 網(wǎng)站進入以一個全新的站點较解,比如 LeetCode 去做更多的練習。技術(shù)實現(xiàn)上 Netflix 仍然位于你的 forward 棧赴邻,這是沒有意義的印衔。

幸運的是,你可以實現(xiàn)一個簡單的 while 循環(huán)來快速去除 Netflix 以及其他站點:

//When I manually navigate to a new site, make forward stack empty
while(forward.top() > -1){
  forward.pop();
}

非常棒姥敛!現(xiàn)在你的導航可以按照預期的方式工作奸焙。

快速總結(jié)一下。棧:

  • 1彤敛、遵循 后進先出(LIFO )規(guī)則
  • 2与帆、有用于管理棧內(nèi)內(nèi)容的 push(add) 和 pop(remove) 方法
  • 3、有一個 top 屬性用于跟蹤棧的大小以及當前棧頂位置
    在這一系列文章中墨榄,每一篇末尾我會對每一種數(shù)據(jù)結(jié)構(gòu)的方法做一個簡單的時間復雜度分析玄糟。

再來看一下代碼:

push(value){
   this._position++; 
   this._storage[this._position] = value; 
 }

 pop(){
   if(this._position > -1){
     let val = this._storage[this._position]; 
     delete this._storage[this._position]; 
     this._position--;
     return val;
   }
 }
 
 top(){
   return this._position;
 }

Push(添加)O(1)。因為你總是知道當前位置渠概,你不需要通過遍歷來完成項目的添加茶凳。

Pop(刪除)O(1)。同樣不需要遍歷就可以移除播揪,因為你總是知道當前棧頂?shù)奈恢谩?/p>

TopO(1)贮喧。當前位置總是已知的。

棧本身沒有搜索方法猪狈,但是如果你想添加一個箱沦,你想一下時間復雜度應該是什么?

Find(查找) 應該是 O(n)雇庙。技術(shù)上你必須遍歷存儲的內(nèi)容知道找到你查找的內(nèi)容谓形。這就是數(shù)組的 indexOf 方法的本質(zhì)灶伊。

本文譯自:A Gentle Introduction to Data Structures: How Stacks Work

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市寒跳,隨后出現(xiàn)的幾起案子聘萨,更是在濱河造成了極大的恐慌,老刑警劉巖童太,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件米辐,死亡現(xiàn)場離奇詭異,居然都是意外死亡书释,警方通過查閱死者的電腦和手機翘贮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爆惧,“玉大人狸页,你說我怎么就攤上這事〕对伲” “怎么了芍耘?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長叔收。 經(jīng)常有香客問我齿穗,道長傲隶,這世上最難降的妖魔是什么饺律? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮跺株,結(jié)果婚禮上复濒,老公的妹妹穿的比我還像新娘。我一直安慰自己乒省,他們只是感情好巧颈,可當我...
    茶點故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著袖扛,像睡著了一般砸泛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蛆封,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天唇礁,我揣著相機與錄音,去河邊找鬼惨篱。 笑死盏筐,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的砸讳。 我是一名探鬼主播琢融,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼界牡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了漾抬?” 一聲冷哼從身側(cè)響起宿亡,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纳令,沒想到半個月后她混,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡泊碑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年坤按,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片馒过。...
    茶點故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡臭脓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腹忽,到底是詐尸還是另有隱情来累,我是刑警寧澤,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布窘奏,位于F島的核電站嘹锁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏着裹。R本人自食惡果不足惜领猾,卻給世界環(huán)境...
    茶點故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骇扇。 院中可真熱鬧摔竿,春花似錦、人聲如沸少孝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稍走。三九已至袁翁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間婿脸,已是汗流浹背粱胜。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盖淡,地道東北人年柠。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親冗恨。 傳聞我的和親對象是個殘疾皇子答憔,可洞房花燭夜當晚...
    茶點故事閱讀 45,747評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)掀抹,斷路器虐拓,智...
    卡卡羅2017閱讀 134,714評論 18 139
  • 從三月份找實習到現(xiàn)在,面了一些公司傲武,掛了不少蓉驹,但最終還是拿到小米、百度揪利、阿里态兴、京東、新浪疟位、CVTE瞻润、樂視家的研發(fā)崗...
    時芥藍閱讀 42,278評論 11 349
  • 請參看我github中的wiki,不定期更新甜刻。https://github.com/ivonzhang/Front...
    zhangivon閱讀 7,135評論 2 19
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,238評論 0 4
  • 紅蘿卜一個绍撞,檸檬半個,蕃茄一個得院,芹菜2棵傻铣,榨汁,加開水里祥绞,如果想要口感好點非洲,放些蜂蜜或紅糖,熱乎乎的喝下就谜。凈化血液
    秦小涵閱讀 369評論 0 0