数据结构 - 队列(图解+源码)
隊列
概念
隊列是一種特殊的線性表,特殊之處在于它遵循先入先出(FIFO)原則,只允許在表的前端(front)進行刪除操作,而在表的后端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。
隊列區分
根據隊列的存儲方式不同可以分為順序存儲(數組)和鏈式存儲(鏈表),根據鏈表的形式又可以分為順隊列與循環隊列
順序隊列
順序隊列顧名思義就是一條路走到黑,front指向隊頭,rear指向隊尾,通過這兩個下標對隊列進行操作,如下圖所示:
每次在隊尾插入一個元素是,rear增1;每次在隊頭刪除一個元素時,front增1。隨著插入和刪除操作的進行,隊列元素的個數不斷變化,隊列所占的存儲空間也在為隊列結構所分配的連續空間中移動。當front=rear時,隊列中沒有任何元素,稱為空隊列。當rear增加到指向分配的連續空間之外時,隊列無法再插入新元素。
順序隊列實現
package queueimport "fmt"type Queue struct {maxSize intfront int // 隊列頭節點默認為節點的前一個rear int // 隊列尾節點queue []int }func NewQueue(maxSize int) *Queue {q := new(Queue)q.maxSize = maxSizeq.queue = make([]int, 0,maxSize)q.front = -1q.rear = -1return q }// Add 向隊列中添加元素 func (q *Queue) Add (val int) {if q.isFull() {fmt.Println("當前隊列已滿!")return}q.queue = append(q.queue, val)q.rear++return }// Get 從隊列中獲取元素 func (q *Queue) Get () int {if q.isEmpty() {return -1}q.front++return q.queue[q.front] }// Show 查看整個隊列元素 func (q *Queue) Show() []int{if q.isEmpty() {return []int{}}queues := make([]int, 0,q.maxSize)for i := q.front +1; i <= q.rear; i++{queues = append(queues, q.queue[i])}return queues }// 隊列是否滿了? func (q *Queue) isFull() bool {return q.rear == q.maxSize-1 }// 隊列是否為空 func (q *Queue) isEmpty() bool {return q.rear == q.front } `` ```bash package mainimport ("Queue/queue""fmt" )func main() {fmt.Println("add 添加元素")fmt.Println("get 獲取元素")fmt.Println("show 查看所有元素")fmt.Println("exit 退出程序")fmt.Printf("請輸入隊列長度:")var queueLen intfmt.Scanln(&queueLen)q := queue.NewQueue(queueLen)fmt.Println("隊列容積:", queueLen)for {var point stringfmt.Scanln(&point)switch point {case "add":fmt.Printf("輸入添加元素:")var val int_, err := fmt.Scanln(&val)if err != nil {continue}q.Add(val)case "get":getVal := q.Get()if getVal == -1{fmt.Println("隊列為空,請先添加元素^_^")}else{fmt.Println("獲取元素:", getVal)}case "show":vals := q.Show()if len(vals) == 0 {fmt.Println("隊列為空,請先添加元素^_^")}else{fmt.Println("隊列全部元素為:", vals)}case "exit":fmt.Println("bey~~")returndefault :fmt.Println("輸入有誤,請重新輸入-_-!!")}} }運行效果
順序隊列中的溢出現象:
(1) "下溢"現象:當隊列為空時,做出隊運算產生的溢出現象。“下溢”是正常現象,常用作程序控制轉移的條件。
(2)"真上溢"現象:當隊列滿時,做進棧運算產生空間溢出的現象。“真上溢”是一種出錯狀態,應設法避免。
(3)"假上溢"現象:由于入隊和出隊操作中,頭尾指針只增加不減小,致使被刪元素的空間永遠無法重新利用。當隊列中實際的元素個數遠遠小于向量空間的規模時,也可能由于尾指針已超越向量空間的上界而不能做入隊操作。該現象稱為"假上溢"現象。
可以發現,當rear增加到指向分配的連續空間之外時,隊列無法再插入新元素,但這時往往還有大量可用空間未被占用,這些空間是已經出隊的隊列元素曾經占用過得存儲單元。既然這個問題這么明顯,肯定會有一個方案去解決。
循環隊列
像前面說的,順序隊列很容易就會造成空間的浪費,為了使隊列空間能重復使用,需要對順序隊列的使用方法稍加改進:
這實際上是把隊列空間想象成一個環形空間,環形空間中的存儲單元循環使用,用這種方法管理的隊列也就稱為循環隊列。除了一些簡單應用之外,真正實用的隊列是循環隊列。
在循環隊列中,當隊列為空時,有front=rear,而當所有隊列空間全占滿時,也有front=rear。為了區別這兩種情況,規定循環隊列最多只能有MaxSize-1個隊列元素,當循環隊列中只剩下一個空存儲單元時,隊列就已經滿了。因此,隊列判空的條件時front=rear,而隊列判滿的條件時front=(rear+1)%MaxSize。
循環的隊列的狀態如圖所示:
循環隊列實現
package circleQueueimport "fmt"type CircleQueue struct {front int //頭節點下標rear int //尾節點下標maxSize int //隊列容積queue []int }func NewCircleQueue(maxSize int) *CircleQueue {q := new(CircleQueue)q.maxSize = maxSize + 1 // 預留一個空位置做判斷q.front = 0q.rear = 0q.queue = make([]int, q.maxSize)return q }// Add 向隊列中添加元素 func (q *CircleQueue) Add(val int) {if q.isFull() {fmt.Println("當前隊列已滿!")return}q.queue[q.rear] = valq.rear = (q.rear + 1) % q.maxSizereturn }// Get 從隊列中獲取元素 func (q *CircleQueue) Get() int {if q.isEmpty() {return -1}defer func() {q.front = (q.front + 1) % q.maxSize}()return q.queue[q.front] }// Show 查看整個隊列元素 func (q *CircleQueue) Show() []int {if q.isEmpty() {return []int{}}// rear = 1 front = 3 1+4-3// rear = 3 front = 1 3+4-1queues := make([]int, 0, q.maxSize)for i := q.front; i < q.front + q.queueSize(); i++ {queues = append(queues, q.queue[i % q.maxSize])}return queues }// 隊列是否滿了? func (q *CircleQueue) isFull() bool {return (q.rear+1)%q.maxSize == q.front }// 隊列是否為空 func (q *CircleQueue) isEmpty() bool {return q.rear == q.front }// 獲取隊列元素個數 func (q *CircleQueue) queueSize() int {return (q.rear + q.maxSize - q.front) % q.maxSize } package mainimport ("Queue/circleQueue""fmt" )func main() {fmt.Println("add 添加元素")fmt.Println("get 獲取元素")fmt.Println("show 查看所有元素")fmt.Println("exit 退出程序")fmt.Printf("請輸入隊列長度:")var queueLen intfmt.Scanln(&queueLen)q := circleQueue.NewCircleQueue(queueLen)fmt.Println("隊列容積:", queueLen)for {var point stringfmt.Scanln(&point)switch point {case "add":fmt.Printf("輸入添加元素:")var val int_, err := fmt.Scanln(&val)if err != nil {continue}q.Add(val)case "get":getVal := q.Get()if getVal == -1{fmt.Println("隊列為空,請先添加元素^_^")}else{fmt.Println("獲取元素:", getVal)}case "show":vals := q.Show()if len(vals) == 0 {fmt.Println("隊列為空,請先添加元素^_^")}else{fmt.Println("隊列全部元素為:", vals)}case "exit":fmt.Println("bey~~")returndefault :fmt.Println("輸入有誤,請重新輸入-_-!!")}}}運行效果
由于最近在學習go語言,所以就用golang實現了^_^
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的数据结构 - 队列(图解+源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android手势锁实现
- 下一篇: ORB论文研读与代码实现