人們常說,編程語言是相通的,掌握了一門,其餘語言很容易掌握。我的以爲這個觀點須要辯證看待。前端
每門編程語言都離不開變量、數組、條件判斷、循環等知識概念,這彷佛能支持上面的觀點。可是,每種編程語言都有本身的適用範圍,都有本身的優缺點。像nodejs這種語言適合I/O密集型、併發大的應用,但也有不能勝任的工做,好比機器學習。像python這樣近乎萬能的語言,也總有無能爲力的時候,好比面對高性能計算,許多python庫的底層都是C語言實現。node
從事前端開發2年多,我愈來愈以爲真正相通的不是語言,而是編程思惟——數據結構和算法。數據結構和算法是脫離編程語言存在的,不一樣語言有不一樣的實現方式,但內在的邏輯和編程思想是一致的。python
我曾在工做中有過這樣一次經歷,在使用socket.io接收後端發送的socket鏈接數據,頁面渲染推送消息提示時,後端有可能同時推送不少消息過來,沒法徹底呈現給用戶。當時產品要求實現同一時刻同一用戶接收的消息最多5條,多餘5條消息再也不疊加顯示。算法
因而咱們前端使用隊列知識,當同一時刻接收到後端推送過來超過5條消息,使用shift()方法截取消息隊列中的首條消息渲染消息提示,解決了過多消息重疊的問題,實現產品功能。今後,我就開始對數據結構和算法產生了深刻研究的興趣。編程
棧,是限定僅在表尾進行插入和刪除操做的線性表,容許插入和刪除的一端稱爲棧頂,另外一端稱爲棧底,有着後進先出(last in first out)的特性。 後端
平常生活中有不少棧的例子,例如,一疊摞在一塊兒的盤子,要從這疊盤子中取出或放入一個盤子,只有在其頂部操做是最爲方便的。咱們寫一個棧,是爲了使用它,那麼必須先定義數據存儲在哪裏,提供什麼方法實現。數組
從數據存儲的角度來看,實現棧有2種方式,一種是以數組爲基礎,一種是以鏈表作基礎。數組是你們平時使用最頻繁,最爲了解熟悉的數據類型,咱們就以數組爲示例,展開研究。 咱們先定義一個簡單的Stack類,數據存儲在items數組中。bash
function Stack() {
var items = [];// 使用數組存儲數據
}
複製代碼
棧有如下幾個方法:數據結構
// push方法向棧壓入一個元素
this.push = function(item) {
items.push(item);
}
複製代碼
// pop方法把棧頂的元素彈出
this.pop = function() {
return items.pop();
}
複製代碼
// top方法返回棧頂元素
this.top = function() {
return items[items.length-1];
}
複製代碼
// isEmpty返回棧是否爲空
this.isEmpty = function() {
return items.length == 0;
}
複製代碼
// size返回棧的大小
this.size = function() {
return items.length;
}
複製代碼
// clear清空棧
this.clear = function() {
items = [];
}
複製代碼
逆波蘭表達式,也叫後綴表達式,它將複雜的表達式轉換爲能夠依賴簡單操做獲得計算結果的表達式,例如(a+b) * (c+d) = ab+cd+ *。 示例:併發
["4","13","5","/","+"] 等價於(4+(13/5)) = 6
複製代碼
請編寫函數calc_exp(exp)實現逆波蘭表達式計算結果,exp的類型是數組。
["4","13","5","/","+"]就是一個數組,在數組層面思考解題思路,遇到/時,把13和5拿出來計算,而後把13和5刪除,並把結果放到4的後面,天吶,這樣解題思路有些複雜。 若是使用棧的特性來解決這個問題,一切都簡單明瞭,使用for循環遍歷數組,對每一個元素作以下操做:
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));
複製代碼
隊列,是隻容許在一端進行插入操做,在另外一端進行刪除操做的線性表。容許插入(也稱入隊、進隊)的一端稱爲隊尾,容許刪除(也稱出隊)的一端稱爲隊頭。隊列具備先進先出(first in first out)的特性。
平常生活中,排隊就是典型的數據結構隊列,先排隊者先辦理事務。有了上面棧數據結構作鋪墊,隊列就容易理解學習了。
同棧同樣,隊列的實現也可使用數組來存儲數據。定義一個簡單的Queue類。
function Queue() {
var items = [];// 存儲數據
}
複製代碼
隊列的方法以下:
// 向隊列尾部添加一個元素
this.enqueue = function(item) {
items.push(item);
}
複製代碼
// 移除隊列頭部的元素
this.dequeue = function() {
return items.shift();
}
複製代碼
// 返回隊列頭部的元素
this.head = function() {
return items[0];
}
複製代碼
// 返回隊列大小
this.size = function() {
return items.length;
}
複製代碼
// 清空隊列
this.clear = function() {
items = [];
}
複製代碼
// 判讀是否爲空隊列
this.isEmpty = function() {
return items.length == 0;
}
複製代碼
斐波那契數列數列是指的是這樣一個數列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........ ,有着各類各樣的解法,比較常見的是遞歸法,其實也可使用隊列來實現。
使用隊列計算裴波那契數列的第n項。
裴波那契數列的前2項是1 1,此後每一項都是該項前面2項之和,即f(n) = f(n-1)+f(n-2)。若是從數組層面來實現,比較麻煩,所以直接考慮使用隊列來實現。 先將兩個1添加到隊列中,以後使用while循環,使用index計數,循環終止的條件是index < n-2。
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));
複製代碼
棧和隊列的底層是否是使用數組實現不重要,重要的是棧有後進先出的特性,隊列有先進先出的特性。咱們要抓住其數據結構的特性,靈活應用於系統編碼中,有效地解決具體問題。
數據結構在系統設計中應用很是普遍,只是咱們水平暫未達到那個級別,知道的較少,但若是能理解並掌握數據結構的算法思想,那麼就有機會在工做中使用並解決問題,當咱們手裏除了錘子還有電鋸時,那麼咱們的眼裏就不僅是釘子,解決問題的思路也會更加開闊,解決方案也會多樣化。