接上篇-數據結構之棧html
隊列是一種特殊的線性表,特殊之處在於它只容許在表的前端(front)進行刪除操做,而在表的後端(end)進行插入操做,和棧同樣,隊列是一種操做受限制的線性表。進行插入操做的端稱爲隊尾,進行刪除操做的端稱爲隊首。前端
隊列的數據元素又稱爲隊列元素。在隊列中插入一個隊列元素稱爲入隊,從隊列中刪除一個隊列元素稱爲出隊。由於隊列只容許在一端插入,在另外一端刪除,因此只有最先進入隊列的元素才能最早從隊列中刪除,故隊列的特性爲 先進先出 (First-In-First-Out,FIFO)es6
請看下面的圖解 後端
從數據存儲的角度看,實現隊列有兩種方式,一種是以數組作基礎,一種是以鏈表作基礎,數組是最簡單的實現方式,本文以基礎的數組來實現隊列。數組
隊列的操做包括建立隊列、銷燬隊列、入隊、出隊、清空隊列、獲取隊頭元素、獲取隊列的長度。bash
咱們定義如下幾個隊列的方法:數據結構
- enqueue 從隊尾添加一個元素(新來一個辦業務的人,排在了隊尾)
- dequeue 從隊首刪除一個元素(隊伍最前面的人,辦完了業務,離開了)
- head 返回隊首的元素(後邊的人好奇看一下,隊伍最前面的人是誰)
- tail 返回隊尾的元素(前邊的人好奇看一下,隊伍最後面的人是誰)
- size 返回隊列的大小(營業員數一下隊伍有多少人)
- isEmpty 返回隊列是否爲空(營業員查看當前是否是有人在排隊)
- clear 清空隊列(此窗口暫停營業,你們撤了吧)
而後咱們利用es6的class的實現以上的方法 新建一個queue.js文件socket
class Queue {
constructor() {
this.items = []; // 存儲數據
}
enqueue(item) { // 向隊尾添加一個元素
this.items.push(item);
}
dequeue() { // 刪除隊首的一個元素
return this.items.shift();
}
head() { // 返回隊首的元素
return this.items[0];
}
tail() { // 返回隊尾的元素
return this.items[this.items.length - 1];
}
size() { // 返回隊列的元素
return this.items.length;
}
isEmpty() { // 返回隊列是否爲空
return this.items.length === 0;
}
clear() { // 清空隊列
this.items = [];
}
}
複製代碼
記住兩點:函數
- 棧的特性是先進後出(聯想:羽毛球桶)
- 隊列的特性是先進先出(聯想:排隊)
有一個數組存放了100個數據0-99,要求每隔兩個數刪除一個數,到末尾時再循環至開頭繼續進行,求最後一個被刪除的數字。post
好比說:有十個數字:0,1,2,3,4,5,6,8,9,每隔兩個數刪除一個數,就是2 5 8 刪除,若是隻是從0到99每兩個數刪除一個數,其實挺簡單的,可是咱們還得考慮到末尾的時候還有再重頭開始,還得考慮刪除掉的元素從數組中刪除。那咱們若是隊列的話,就比較簡單了
3.1.2 思路分析
- 先將這100個數據放入隊列,用while循環,終止的條件是隊列裏只有一個元素。
- 定義index變量從0開始計數,從隊列頭部刪除一個元素,index + 1
- 若是index%3 === 0 ,說明這個元素須要被移除隊列,不然的話就把它添加到隊列的尾部
通過while循環後,不斷的有元素出隊列,最後隊伍中只會剩下一個被刪除的元素
3.1.3 看代碼實現
// 每隔兩個數刪除一個數
{
var arr = []; // 準備0-99 100個數據
for (var i = 0; i < 100; i++) {
arr.push(i);
}
function delRang(arr) {
var queue = new Queue(); // 調用以前實現Queue類
var len = arr.length;
for (var i = 0; i < len; i++) {
queue.enqueue(i); // 將數據存入隊列
}
var index = 0;
while (queue.size() !== 1) { // 循環判斷隊列裏大小否爲還剩下1個
var item = queue.dequeue(); // 出隊一個元素,根據當前的index來判斷是否須要移除
index += 1;
if (index % 3 !== 0) {
queue.enqueue(item); // 不是的話,則添加到隊尾,繼續循環
}
}
console.log(queue.head()); // 90
return queue.head(); // 返回最後一個元素
}
delRang(arr);
}
複製代碼
是否是感受使用隊列很簡單呢,接下來再看幾個小練習
3.2.1 題目介紹
什麼是斐波那契數列: 斐波那契數列(Fibonacci sequence),又稱黃金分割數列、因數學家列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖爲例子而引入,故又稱爲「兔子數列」,指的是這樣一個數列:一、一、二、三、五、八、1三、2一、3四、……這個數列從第3項開始,每一項都等於前兩項之和。在數學上,斐波納契數列以以下被以遞歸的方法定義:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)來源:斐波那契數列——百度百科
3.2.2 咱們先考慮使用普通的方法實現 -- 遞歸 遞歸版 代碼實現
function Fibonacci (n) {
if ( n <= 2 ) {return 1};
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
Fibonacci(10) // 55
Fibonacci(100) // 堆棧溢出
Fibonacci(500) // 堆棧溢出
複製代碼
由上可見,遞歸很是消耗內存,由於須要同時保存成千上百個調用幀,很容易發生「棧溢出」錯誤。可是也有解決的辦法,採用尾遞歸優化。
函數調用自身,稱爲遞歸;若是尾調用自身,就稱爲尾遞歸。 對於尾遞歸來講,因爲只存在一個調用棧,因此永遠不會發生「棧溢出」錯誤。
尾遞歸版 代碼實現
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 2 ) {return ac2};
return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
Fibonacci2(100) // 354224848179262000000
Fibonacci2(1000) // 4.346655768693743e+208
複製代碼
上面代碼雖然簡潔,可是不易想到
3.2.3 那接下來咱們用隊列實現一遍 思路分析
- 須要先將兩個1 添加到隊列中
- 定義index來計數,採用while循環,終止條件是 index < n - 2(由於每次遍歷咱們只保留2個元素在隊列中)
- 使用dequeue方法移除隊列頭部的元素,標記爲 numDel;
- 使用head方法獲取此時頭部的元素,標記爲numHead;
- 使用enqueue方法將前二者的和從尾部放入隊列中
- index + 1
當循環結束後,隊列裏面只有兩個元素,用dequeue方法移除頭部元素後,再用head方法獲取的頭部元素就是最終的結果,並且此方法不會產生「棧溢出」錯誤。
隊列版 代碼實現
{
function fibonacci(n) {
if (n <= 2) return 1;
var queue = new Queue();
// 先存入序列的前兩個值
queue.enqueue(1);
queue.enqueue(1);
var index = 0;
while (index < n - 2) {
var delItem = queue.dequeue(); // 移除隊列的頭部元素
var headItem = queue.head(); // 獲取隊列頭部元素(由於上一步已經將頭部元素移除)
var resNum = delItem + headItem;
queue.enqueue(resNum); // 將二者之和存入隊列
index += 1;
}
queue.dequeue();
return queue.head();
}
console.log("fibonacci", fibonacci(10)); // 55
console.log("fibonacci", fibonacci(100)); // 354224848179262000000
}
複製代碼
3.3.1 題目分析 所謂楊輝三角,你們確定都不會陌生,以下圖所示 楊輝三角——百度百科介紹 計算的方式:f[i][j] = f[i-1][j-1] + f[i-1][j], i 表明行數,j表明一行的第幾個數,若是j= 0 或者 j = i ,則 f[i][j] = 1。
- 楊輝三角中的每一行,都依賴於上一行,假設如今隊列裏已經存儲了第n-1行的數據,那麼輸出第n行時,只須要將隊列裏的數據依次出隊列,進行計算獲得下一行的數值並講計算所得存儲到隊列中
- 而後咱們須要兩層for循環,將n-1行和n行的數據分開打印;有上圖能夠得出規律,n行只有n個數,因此咱們就可使用for循環控制enqueue的次數,n次結束後,隊列裏存儲的就是計算好的第n+1行的數據
3.3.3 代碼實現
<!DOCTYPE html>
<html lang="en">
<head>
<title>打印楊輝三角</title>
</head>
<body>
<script src="./queue.js"></script>
<script>
// 楊輝三角
{
function yangHui(n) {
var queue = new Queue();
queue.enqueue(1); // 先在隊列中存儲第一行的數據
for (var i = 1; i <= n; i++) { // 第一層循環控制層數
var line = "";
var pre = 0;
for (var j = n; j > i; j--) { // 打印空格
document.write(" ");
}
for (var j = 0; j < i; j++) { // 第二層控制當前層的數據
var item = queue.dequeue();
var value = item + pre; // 計算下一行的值
pre = item;
line += item + " ";
queue.enqueue(value);
}
queue.enqueue(1); // 將每層的最後一個數值 1 存入隊列中
document.write(line + "<br />");
}
}
yangHui(10);
}
</script>
</body>
</html>
複製代碼
使用隊列的例子還有不少,好比逐層打印一顆樹上的節點,還有消息通信使用的socket,當大量客戶端向服務端發起鏈接,而服務端擁擠時,就會造成隊列,先來的先處理,後來的後處理,當隊列滿時,新來的請求直接拋棄掉。 數據結構在系統設計中的應用很是普遍,只是咱們水平達不到那個級別,知道的太少,但若是能理解並掌握這些數據結構,那麼就有機會在工做中使用它們並解決一些具體的問題,當咱們手裏除了錘子還有電鋸時,那麼咱們的眼裏就不僅是釘子,解決問題的思路也會更加開闊。