數據結構與算法系列七(隊列)

1.引子

1.1.為什麼要學習數據結構與算法?

有人說,數據結構與算法,計算機網絡,與操作系統都一樣,脫離日常開發,除了面試這輩子可能都用不到呀!

有人說,我是做業務開發的,只要熟練API,熟練框架,熟練各種中間件,寫的代碼不也能“飛”起來嗎?

於是問題來了:為什麼還要學習數據結構與算法呢?

#理由一:      面試的時候,千萬不要被數據結構與算法拖了後腿  #理由二:      你真的願意做一輩子CRUD Boy嗎  #理由三:      不想寫出開源框架,中間件的工程師,不是好廚子

1.2.如何系統化學習數據結構與算法?

我想好了,還是需要學習數據結構與算法。但是我有兩個困惑:

1.如何着手學習呢?

2.有哪些內容要學習呢?

學習方法推薦:

#學習方法  1.從基礎開始,系統化學習  2.多動手,每一種數據結構與算法,都自己用代碼實現出來  3.思路更重要:理解實現思想,不要背代碼  4.與日常開發結合,對應應用場景

學習內容推薦:

數據結構與算法內容比較多,我們本着實用原則,學習經典的、常用的數據結構、與常用算法

#學習內容:  1.數據結構的定義  2.算法的定義  3.複雜度分析  4.常用數據結構      數組、鏈表、棧、隊列      散列表、二叉樹、堆      跳錶、圖  5.常用算法      遞歸、排序、二分查找      搜索、哈希、貪心、分治      動態規劃、字符串匹配

2.考考你

你還記得在數組那一篇中,我們說過基於線性表的數據結構有哪些嗎?它們是:數組、鏈表、棧、隊列。

上一篇【數據結構與算法系列六(棧)】中,我們已經詳細了解了棧這種數據結構:棧是一種操作受限的數據結構。隊列是基於線性表的數據結構中,最後一種了,很巧!它也是一種操作受限的數據結構。

隊列同樣可以基於數組實現:順序隊列;也可以基於鏈表實現:鏈式隊列

那麼問題來了:具體如何實現一個隊列呢?它都有哪些應用場景呢?

#考考你:  1.你能用自己的話描述隊列嗎?  2.你知道常見的隊列分類嗎?  3.你知道隊列代碼實現的關鍵嗎?  4.你知道如何實現一個循環隊列嗎?  5.你知道隊列的常見的應用場景嗎?

3.案例

3.1.隊列的定義

隊列是一種基於線性表的數據結構,與棧一樣,都是操作受限的數據結構。的特點是後進先出,而隊列的特點是先進先出(FIFO),就像我們平常在火車站排隊候車一樣。

隊列有兩頭:隊頭,和隊尾。從隊頭出隊元素,在隊尾入隊新的元素。

 

 

3.2.代碼實現

順序隊列代碼:

package com.anan.struct.linetable;    /**   * 順序隊列:基於數組實現   * @param <E>   */  public class ArrayQueue<E> {      private Object[] items;      private  int n;        // 隊列需要兩個下標:對頭下標索引、隊尾下標索引      private int head;      private int tail;        public ArrayQueue(int capacity){          this.items = new Object[capacity];          this.n = capacity;      }        /**       * 入隊操作:       */      public boolean enqueue(E e){          // 檢查隊列是否滿          // 隊列滿條件 tail==n && head == 0          if(tail == n){                // 檢查對頭是否沒有出隊              if(head == 0){                  return false;              }                // 如果已經有元素出隊,則向對頭移動數據              for (int i = head; i < tail ; i++) {                  items[i - head] = items[i];              }                tail = tail - head;              head = 0;          }            // 入隊          items[tail] = e;          tail ++;            return true;      }        /**       * 出隊操作:       */      public E dequeue(){          // 檢查隊列是否空          // 隊列空條件:head == tail          if(head == tail){              return null;          }            // 出隊          E e = (E)items[head];          head ++;            return e;      }    }

測試代碼:

package com.anan.struct.linetable;    /**   * 測試隊列   */  public class ArrayQueueTest {        public static void main(String[] args) {          // 1.創建隊列          int capacity = 10;          ArrayQueue<Integer> queue = new ArrayQueue<Integer>(capacity);          System.out.println("1.創建隊列---------隊列容量:" + capacity);            // 2.入隊操作          System.out.println("2.入隊操作---------");          int count = 5;          for (int i = 0; i < count; i++) {              queue.enqueue(i);              System.out.println("入隊元素:" + i);          }              // 3.出隊操作          System.out.println("3.出隊操作---------");          for (int i = 0; i < count; i++) {              System.out.println("出隊元素:" + queue.dequeue());          }        }  }

測試結果:

D:2teach1softjdk8binjava com.anan.struct.linetable.ArrayQueueTest  1.創建隊列---------隊列容量:10  2.入隊操作---------  入隊元素:0  入隊元素:1  入隊元素:2  入隊元素:3  入隊元素:4  3.出隊操作---------  出隊元素:0  出隊元素:1  出隊元素:2  出隊元素:3  出隊元素:4    Process finished with exit code 0

3.3.循環隊列代碼實現

package com.anan.struct.linetable;    /**   * 循環隊列   */  public class CircularQueue<E> {        private Object[] items;      private int n;        // 隊頭、對尾指針      private int head;      private int tail;        public CircularQueue(int capacity){          items = new Object[capacity];          n = capacity;      }        /**       * 入隊操作       */      public boolean enqueue(E e){          // 判斷隊列是否滿          // 隊列滿條件:(tail + 1) % n == head          if((tail + 1) % n == head){              return false;          }            items[tail] = e;          tail = (tail + 1) % n;            return true;      }        /**       * 出隊操作       */      public E dequeue(){          // 判斷隊列是否空          // 隊列空條件:tail == head          if(tail == head){              return null;          }            E e = (E)items[head];          head = (head + 1) % n;            return e;      }  }

4.討論分享

#考考你答案:  1.你能用自己的話描述隊列嗎?    1.1.隊列是基於線性表的數據結構    1.2.隊列是一種操作受限的數據結構    1.3.隊列滿足先進先出(FIFO)的特點    1.4.隊列在隊頭出隊元素,在隊尾入隊元素    2.你知道常見的隊列分類嗎?    2.1.從底層數據結構分類有:順序隊列、鏈式隊列    2.2.從實現特點分類有:循環隊列、阻塞隊列、並發隊列    3.你知道隊列代碼實現的關鍵嗎?    3.1.隊列滿足先進先出(FIFO)特點    3.2.隊列在隊頭出隊元素,在隊尾入隊元素    3.3.實現隊列的關鍵:      a.需要兩個指針:head、tail分別指向隊頭和隊尾      b.入隊時,判斷隊列滿條件:tail == n && head == 0      c.出隊時,判斷隊列空條件:tail == head    4.你知道如何實現一個循環隊列嗎?    4.1.在案例中,基於數組實現了一個普通的隊列    4.2.入隊操作的時候,如果隊列滿,需要移動數據    // 如果隊列滿,且已經有元素出隊,則向對頭移動數據     for (int i = head; i < tail ; i++) {            items[i - head] = items[i];     }    4.3.這樣會將入隊操作,時間複雜度從O(1),轉變成O(n),執行效率下降    4.4.有沒有更好的方式,保持入隊操作的時間複雜度為O(1)不變呢?    4.5.答案是:通過循環隊列來實現    4.6.關於循環隊列的代碼,你可以參考【3.3】循環隊列實現    4.7.重點關注隊列滿的條件:(tail + 1) % n == head    4.8.看你是否能理解,歡迎留言我們一起討論    5.你知道隊列的常見的應用場景嗎?    5.1.隊列主要針對有限資源控制的應用場景    5.2.比如數據庫連接池的應用    5.3.比如線程池的應用    5.4.如果你有興趣,可以看一下JUC中線程池的底層實現    5.5.JUC線程池的底層,應用了:阻塞隊列    5.6.通過隊列還能實現:生產者---消費者模型

JUC創建線程池