前端應該瞭解的數據結構-棧與隊列

1、爲何須要學習數據結構?

一、編程思惟是相通的

  人們常說,編程語言是相通的,掌握了一門,其餘語言很容易掌握。我的以爲這個觀點須要辯證看待。前端

  每門編程語言都離不開變量、數組、條件判斷、循環等知識概念,這彷佛能支持上面的觀點。可是,每種編程語言都有本身的適用範圍,都有本身的優缺點。像nodejs這種語言適合I/O密集型、併發大的應用,但也有不能勝任的工做,好比機器學習。像python這樣近乎萬能的語言,也總有無能爲力的時候,好比面對高性能計算,許多python庫的底層都是C語言實現。node

  從事前端開發2年多,我愈來愈以爲真正相通的不是語言,而是編程思惟——數據結構和算法。數據結構和算法是脫離編程語言存在的,不一樣語言有不一樣的實現方式,但內在的邏輯和編程思想是一致的。python

二、高質量編碼離不開數據結構作指導

  我曾在工做中有過這樣一次經歷,在使用socket.io接收後端發送的socket鏈接數據,頁面渲染推送消息提示時,後端有可能同時推送不少消息過來,沒法徹底呈現給用戶。當時產品要求實現同一時刻同一用戶接收的消息最多5條,多餘5條消息再也不疊加顯示。算法

  因而咱們前端使用隊列知識,當同一時刻接收到後端推送過來超過5條消息,使用shift()方法截取消息隊列中的首條消息渲染消息提示,解決了過多消息重疊的問題,實現產品功能。今後,我就開始對數據結構和算法產生了深刻研究的興趣。編程

2、數據結構——棧

一、棧的定義

,是限定僅在表尾進行插入和刪除操做的線性表,容許插入和刪除的一端稱爲棧頂,另外一端稱爲棧底,有着後進先出(last in first out)的特性。 後端

平常生活中有不少棧的例子,例如,一疊摞在一塊兒的盤子,要從這疊盤子中取出或放入一個盤子,只有在其頂部操做是最爲方便的。

二、棧的實現

咱們寫一個棧,是爲了使用它,那麼必須先定義數據存儲在哪裏,提供什麼方法實現。數組

2.1 數據存儲

從數據存儲的角度來看,實現棧有2種方式,一種是以數組爲基礎,一種是以鏈表作基礎。數組是你們平時使用最頻繁,最爲了解熟悉的數據類型,咱們就以數組爲示例,展開研究。 咱們先定義一個簡單的Stack類,數據存儲在items數組中。bash

function Stack() {
    var items = [];// 使用數組存儲數據
}
複製代碼

2.2 棧的方法

棧有如下幾個方法:數據結構

  • push 添加一個元素到棧頂(向一疊盤子放入一個盤子)
  • pop 彈出棧頂元素(從一疊盤子取出一個盤子)
  • top 返回棧頂元素,注意不是彈出(看一眼最頂端的盤子,可是不拿)
  • isEmpty 判斷棧是否爲空(看盤子是否是都用完了)
  • size 返回棧裏元素的個數(數一下一疊盤子有多少盤子)
  • clear 清空棧(把一疊盤子都扔掉)

2.2.1 push方法

// push方法向棧壓入一個元素
this.push = function(item) {
    items.push(item);
}
複製代碼

2.2.2 pop方法

// pop方法把棧頂的元素彈出
this.pop = function() {
    return items.pop();
}
複製代碼

2.2.3 top方法

// top方法返回棧頂元素
this.top = function() {
    return items[items.length-1];
}
複製代碼

2.2.4 isEmpty方法

// isEmpty返回棧是否爲空
this.isEmpty = function() {
    return items.length == 0;
}
複製代碼

2.2.5 size方法

// size返回棧的大小
this.size = function() {
    return items.length;
}
複製代碼

2.2.6 clear方法

// clear清空棧
this.clear = function() {
    items = [];
}
複製代碼

三、棧的應用

3.1 計算逆波蘭表達式

3.1.1 題目要求

逆波蘭表達式,也叫後綴表達式,它將複雜的表達式轉換爲能夠依賴簡單操做獲得計算結果的表達式,例如(a+b) * (c+d) = ab+cd+ *。 示例:併發

["4","13","5","/","+"] 等價於(4+(13/5)) = 6
複製代碼

請編寫函數calc_exp(exp)實現逆波蘭表達式計算結果,exp的類型是數組。

3.1.2 思路分析

["4","13","5","/","+"]就是一個數組,在數組層面思考解題思路,遇到/時,把13和5拿出來計算,而後把13和5刪除,並把結果放到4的後面,天吶,這樣解題思路有些複雜。 若是使用棧的特性來解決這個問題,一切都簡單明瞭,使用for循環遍歷數組,對每一個元素作以下操做:

  • 若是元素不是+ - * /中的某一個,就壓入棧中
  • 若是元素是+ - * /中的某一個,則從棧裏連續彈出2個元素,並對這2個元素進行計算,將計算結果壓入棧中 for循環結束以後,棧裏只有一個元素,這個元素就是整個表達式的計算結果。

3.1.3 實現代碼

function calc_exp(exp) {
    var stack = new Stack();
    for(var i = 0; i < exp.length; i++) {
        var item = exp[i];
        if(["+","-","*","/"].indexOf(item)>= 0){
            // 從棧頂彈出2個元素
            var value_1 = stack.pop();
            var value_2 = stack.pop();
            // 拼接表達式
            var exp_str = value_2 + item + value_1;
            // 計算取整
            var res = parseInt(eval(exp_str));
            stack.push(res.toString());
        }else{
            stack.push(item);
        }
    }
    return stack.pop();
}
var exp_1 = ["4","13","5","/","+"];
console.log(calc_exp(exp_1));
複製代碼

3、數據結構——隊列

一、隊列的定義

隊列,是隻容許在一端進行插入操做,在另外一端進行刪除操做的線性表。容許插入(也稱入隊、進隊)的一端稱爲隊尾,容許刪除(也稱出隊)的一端稱爲隊頭。隊列具備先進先出(first in first out)的特性。

平常生活中,排隊就是典型的數據結構隊列,先排隊者先辦理事務。

二、隊列的實現

有了上面棧數據結構作鋪墊,隊列就容易理解學習了。

2.1 數據存儲

同棧同樣,隊列的實現也可使用數組來存儲數據。定義一個簡單的Queue類。

function Queue() {
    var items = [];// 存儲數據
}
複製代碼

2.2 隊列的方法

隊列的方法以下:

  • enqueue 從隊列尾部添加一個元素(新來的一個排隊人,文明禮貌,站在了隊伍末尾)
  • dequeue 從隊列頭部刪除一個元素(隊伍最前面的人辦理完事務,離開了隊伍)
  • head 返回頭部的元素,注意,不是刪除(只是看一下,誰排在最前面)
  • size 返回隊列大小(數一數有多少人在排隊)
  • clear 清空隊列(隊列解散)
  • isEmpty 判斷隊列是否爲空(看看是否有人在排隊)

2.2.1 enqueue方法

// 向隊列尾部添加一個元素
this.enqueue = function(item) {
    items.push(item);
}
複製代碼

2.2.2 dequeue方法

// 移除隊列頭部的元素
this.dequeue = function() {
    return items.shift();
}
複製代碼

2.2.3 head方法

// 返回隊列頭部的元素
this.head = function() {
    return items[0];
}
複製代碼

2.2.4 size方法

// 返回隊列大小
this.size = function() {
    return items.length;
}
複製代碼

2.2.5 clear方法

// 清空隊列
this.clear = function() {
    items = [];
}
複製代碼

2.2.6 isEmpty方法

// 判讀是否爲空隊列
this.isEmpty = function() {
    return items.length == 0;
}
複製代碼

三、隊列的應用

3.1 斐波那契數列

斐波那契數列數列是指的是這樣一個數列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........ ,有着各類各樣的解法,比較常見的是遞歸法,其實也可使用隊列來實現。

3.1.1 題目要求

使用隊列計算裴波那契數列的第n項。

3.1.2 思路分析

裴波那契數列的前2項是1 1,此後每一項都是該項前面2項之和,即f(n) = f(n-1)+f(n-2)。若是從數組層面來實現,比較麻煩,所以直接考慮使用隊列來實現。 先將兩個1添加到隊列中,以後使用while循環,使用index計數,循環終止的條件是index < n-2。

  • 使用dequeue方法從隊列頭部刪除一個元素,該元素爲del_item
  • 使用head方法得到隊列頭部的元素,該元素爲head_item
  • del_item + head_item = next_item, 將next_item放入隊列,注意,只能從尾部添加元素
  • index+1

3.1.3 實現代碼

function fibonacci(n) {
    var queue = new Queue();
    var index = 0;
    // 先放入裴波那契數列的前兩個數值
    queue.enqueue(1);
    queue.enqueue(1);
    while(index < n-2) {
        // 取隊列第一個元素
        var del_item = queue.dequeue();
        // 取隊列頭部元素
        var head_item = queue.head();
        // 計算下個元素
        var next_item = del_item + head_item;
        // 將計算結果放入隊列
        queue.enqueue(next_item);
        index += 1;
    }
    queue.dequeue();
    return queue.head();
}
console.log(fibonacci(8));
複製代碼

4、小結

  棧和隊列的底層是否是使用數組實現不重要,重要的是棧有後進先出的特性,隊列有先進先出的特性。咱們要抓住其數據結構的特性,靈活應用於系統編碼中,有效地解決具體問題。

  數據結構在系統設計中應用很是普遍,只是咱們水平暫未達到那個級別,知道的較少,但若是能理解並掌握數據結構的算法思想,那麼就有機會在工做中使用並解決問題,當咱們手裏除了錘子還有電鋸時,那麼咱們的眼裏就不僅是釘子,解決問題的思路也會更加開闊,解決方案也會多樣化。

相關文章
相關標籤/搜索