隊列是一種操做受限的線性表,只容許在一端進行插入,另外一端進行刪除。插入的一端稱爲隊尾,刪除的一端稱爲隊頭,因爲這樣的限制,因此隊列具備先進先出的特性,所以隊列也是一種先進先出的線性表。算法
隊列的順序存儲結構,除了存儲的數組,還須要一個隊尾指針(rear),和隊頭指針(front),初始化隊列的時候,rear和front都指向同一個下標0,這shi隊爲空。數組
在這種狀況下,會出現假溢出現象,由於入隊和出隊操做中,頭,尾指針都只增長,不減小,致使被刪除的元素空間永遠沒法從新利用。即儘管隊列中的元素個數遠遠小於數組的大小,但因爲尾指針已經超出數組的上界,致使不能進行入隊操做,這種現象稱爲假溢出。ui
數組大小爲4,隊列操做時,頭、尾指針變化過程以下圖 spa
爲了克服假溢出,能夠怎麼改進呢?很天然的就想到,把新的元素放到空餘的空間裏,即又回到數組下標0位置處,這樣看上去就像一個首尾相接的圓環,這種隊列稱爲循環隊列。指針
循環隊列的出隊和入隊,仍然是頭,尾指針加一,只不過當頭,尾指針到達數組上界時,加1操做,又回到了下界0處。code
// i 表明頭,尾指針
if i+1 == maxSize {
i = 0
} else {
i++
}
複製代碼
上述的這種操做,可使用求模運算簡化,即i = (i+1) % maxSize,這樣就能充分利用數組上的全部空間,除非數組空間被佔滿,不然不會形成溢出。 來看下循環隊列,頭尾指針的變化過程。 orm
從中會發現一個問題,當front == rear的時候,有多是隊空,也多是隊滿。爲何會出現這個問題呢?咱們以隊列最大容量爲4,來分析這個問題:cdn
怎麼解決呢?通常都兩種方案:blog
一、使用額外的標誌位tag,當入隊的時候,把tag設置成1,當出隊的時候,把tag設置成0,那麼當front==rear時,就能夠經過tag的值來判斷是空隊,仍是滿隊隊列
二、少用一個空間,即數組最大容量爲4,但咱們只用3個容量,這樣判斷空隊列仍然是front==rear,而判斷隊列是否滿,則就變成(rear+1)%maxSize == front,則爲滿。(下面的實例代碼,以此方案實現)即以下圖
固然,也可使用鏈式存儲的方式來構建隊列,若是使用鏈式,就不存在容量的問題,這樣也就不須要判斷隊滿。
type data interface{}
type Queue struct {
list []data
front int // 頭指針
rear int // 尾指針
maxSize int // 最大容量
}
func New(maxSize int) *Queue {
q := &Queue{
list: make([]data, maxSize+1),
front: 0,
rear: 0,
maxSize: maxSize + 1, // 空餘一個容量不使用
}
return q
}
複製代碼
func (q *Queue) IsFull() bool {
return (q.rear + 1) % q.maxSize == q.front
}
複製代碼
func (q *Queue) IsEmpty() bool {
return q.front == q.rear
}
複製代碼
判斷隊是否已經滿,滿就報錯,不然入隊
func (q *Queue) Enqueue(value data) (bool, error) {
if q.IsFull() {
return false, errors.New("隊已滿")
}
q.list[q.rear] = value
q.rear = (q.rear + 1) % q.maxSize
return true, nil
}
複製代碼
隊爲空,則報錯,不然出隊
func (q *Queue) Dequeue() (data, error) {
if q.IsEmpty() {
return nil, errors.New("隊爲空")
}
value := q.list[q.front]
q.list[q.front] = nil
q.front = (q.front + 1) % q.maxSize
return value, nil
}
複製代碼
func (q *Queue) GetHead() (data, error) {
if q.IsEmpty() {
return nil, errors.New("隊爲空")
}
return q.list[q.front], nil
}
複製代碼
楊輝三角,是二項式係數在三角形中的一種幾何排列,以下圖
它有兩個比較顯著的特色:基於以上的性質,使用程序輸入楊輝三角的時候,一種想法就是,利用兩個數組,在輸出當前行的時候,就計算下一行的值,放到另外一個數組裏,兩個數組交替使用。
第二種方案,咱們能夠利用隊列來輸出,在空間上能夠減小一個數組,在使用隊列輸出的楊輝三角的時候,有一個小技巧,就是在每行的兩端添加兩個0,即成以下的形式
0 1 0
0 1 1 0
0 1 2 1 0
複製代碼
在這個前提下,算法思路(n表明行數):
一、初始化一個隊列,將第一列 0 1 0 依次入隊;
二、此時每一行的元素個數爲n + 2,依次出隊並輸出該行的每個元素,0出隊但不輸出;
三、在元素出隊的同時,計算下一行對應位置的數值,即出隊元素 + 新的隊頭元素,並把計算獲得的值入隊;
四、當該行的每個元素都輸出完了,隊列裏也就計算好了下一行的元素,此時再把0入隊,這個0便是這一行結束的0,也是下一行開始的0;
五、重複2,3,4直到n結束。
咱們以 n = 4爲例,看看隊列裏元素的變化:
const maxSize = 1000
func printYangHui(n int) {
q := Queue.New(maxSize)
q.Enqueue(0)
q.Enqueue(1)
q.Enqueue(0)
for i := 1; i <= n; i++ {
formatPrint(n-i) // 格式化輸出
for j := 1; j < i+2; j++ {
// 第i行,在隊列中有i + 2個數字,包括頭尾兩個0,
// 0 1 0
// 0 1 1 0
// 0 1 2 1 0
s, _ := q.Dequeue()
if s != 0 {
fmt.Printf(" %d", s)
}
t, _ := q.GetHead()
q.Enqueue(s.(int) + t.(int)) // 下一行中的數字就是其左右肩之和
}
q.Enqueue(0) // 再把每行的0入隊
fmt.Println()
}
}
printYangHui(4) // 結果以下圖
複製代碼
以上的應用只是隊列的一個小應用,理論上數據流符合先進先出的規則,均可以考慮使用隊列解決問題。好比打印機的打印調度,先進的內容,會被先打印出來,等等。
Thanks!