队列与环形队列使用数组模拟
隊列
該文是觀看尚硅谷韓老師視頻學習自己總結學習得,有的是來源于網絡收集
隊列引入
進的一端稱為隊尾(rear),出的一端稱為隊頭(front)。隊列可以用順序存儲,也可以用鏈式存儲。
隊列介紹
數組模擬隊列
隊列本身是有序列表,若使用數組的結構來存儲隊列的數據,則隊列數組的 如上圖,其中 maxSize 是該隊列的最大容量。
思路分析
添加數據的時候將尾指針往后移:rear+1 , 當 取出的數據front+1,若front == rear 【所明隊列數據為空,并非數組數據空,】
若尾指針 rear 小于隊列的最大下標 maxSize-1,則將數據存入 rear 所指的數組元素中,否則無法存入數據。
rear = = maxSize - 1[隊列滿]
代碼模擬數組實現隊列
package com.fs.demo_2020_07_11_queueArray;import java.util.Scanner;/*** 隊列* 隊列是一個有序列表,可以用數組或者鏈表來實現* 遵循先入先出的原則,先存入的數據先取出來,后存入的后取出來** 數組模擬隊列*/ public class QueueArray {public static void main(String[] args) {//測試一把//創建一個隊列SimulationQueueArray queue = new SimulationQueueArray(3);char key = ' '; //接收用戶輸入Scanner scanner = new Scanner(System.in);//boolean loop = true;//輸出一個菜單while (loop) {System.out.println("s(show): 顯示隊列");System.out.println("e(exit): 退出程序");System.out.println("a(add): 添加數據到隊列");System.out.println("g(get): 從隊列取出數據");System.out.println("h(head): 查看隊列頭的數據");key = scanner.next().charAt(0);//接收一個字符switch (key) {case 's':queue.showQueue();break;case 'a':System.out.println("輸出一個數");int value = scanner.nextInt();queue.addQueue(value);break;case 'g': //取出數據try {int res = queue.getQueue();System.out.printf("取出的數據是%d\n", res);} catch (Exception e) {System.out.println(e.getMessage());}break;case 'h': //查看隊列頭的數據try {int res = queue.headQueue();System.out.printf("隊列頭的數據是%d\n", res);} catch (Exception e) {// System.out.println(e.getMessage());e.printStackTrace();}break;case 'e': //退出scanner.close();loop = false;break;default:break;}}System.out.println("程序退出~~");} }//創建一個類來模擬隊列 class SimulationQueueArray {private int maxSize;//表示數字的最大容納量private int front;//模擬指針,默認指向數組有效數據索引的前一個位置private int rear;//模擬指針指向尾部數據,默認初始化指向和front一個位置,因為沒有數據private int[] arr;/*** 創建隊列數組的構造器** @param arrMaxSize 初始化這個隊列數組的容量*/public SimulationQueueArray(int arrMaxSize) {maxSize = arrMaxSize;arr = new int[maxSize];//初始化數組的容量front = -1;rear = -1;}//判斷隊列數組是否滿了public boolean isFull() {//假設數組最大索引為2,那么容量就為3,當添加3個數據后,rear就加了3次,就是-1+3=2//所以-1+3 = 2 = 3 -1 = 2,所以就滿了return rear == maxSize - 1;}//判斷隊列是否為空public boolean isEmpty() {//為空有兩中情況,//1.就是數組中的元素取完了,我還是假設容量為3,要么一條數據都沒有添加,front和rear都為-1,表示容量為空//2.數組中的元素添加3個,那么front為2,然后我們又取了3個元素,那么front也為2,那么rear=front,說明數組中為空return rear == front;}//添加數據到隊列public void addQueue(int data) {//添加前先判斷隊列是否滿了if (isFull()) {System.out.println("隊列滿,不能加入數據~~~");return;}rear++;//讓指針向后移一下//在rear索引下添加這個數據,假設第一次添加,就是0索引添加data數據arr[rear] = data;}//獲取隊列的數據,出隊列public int getQueue() {//先判斷隊列是否為空if (isEmpty()) {//為空就拋出異常,我們這里結束方法使用拋出異常方式throw new RuntimeException("隊列空,不能獲取數據~~~");}front++;//先將指針后移,//這里的取出并不是吧數組中的數據真正的取出,而且將指針后移,取出指針后移的這位數據//提現了先進先出的隊列思想return arr[front];}//顯示隊列的所有數據public void showQueue() {//同樣先判斷隊列是否為空if (isEmpty()) {System.out.println("隊列空,沒有這個數據~~~");return;}//遍歷for (int i = 0; i < arr.length; i++) {System.out.println("arr[" + i + "]=" + arr[i]);}}//顯示隊列的頭數據,不是取數劇public int headQueue() {if (isEmpty()) {//為空就拋出異常,我們這里結束方法使用拋出異常方式throw new RuntimeException("隊列空,沒有數據~~~");}//顯示是front指針當前指的哪位數據,因為假如還沒有從列隊中取出數據,那么當前就指向索引為0的位子,故為front+1 為0//若已經取了索引為0的數據,那么當前指的就是索引為1的位子,故front+1 為1//若數據已經取完,那么就判斷直接拋出異常不會到這一步,假設到了這一步,也會索引越界異常return arr[front + 1];}}若看不懂,將代碼拷貝到idea中DEBUG看數據變化自然會懂
上面是線性的隊列,不能循環復用其中的排隊邏輯,當隊列滿后,第一個走后,后來的不能加入子第一個位置,所以沒有達到復用效果
使用一些取模的方式達到讓線性的隊列轉換成環形隊列,從而實現先進先出,后進后出,循環環形隊列
數組模擬環形隊列
上述到達尾部又向前存儲的隊列稱為循環隊列,為了避免"假溢出",我們通常采用循環隊列。
分析
一 尾索引的下一個為頭索引時表示隊列滿,即將隊列容量空出一個作為約定,這個在做判斷隊列滿的時候需要注意 (rear + 1) % maxSize == front 滿]
二 rear == front [空]
代碼實現環形隊列
package com.fs.demo_2020_07_11_queueArray;import java.util.Scanner;/*** 模擬環形隊列數組** 1.頭尾部指針取模重新計算指針值:(rear/front + 1) % maxSize* 2.隊列空的判斷邏輯:rear == front* 3.隊列滿的判斷邏輯:(rear + 1) % maxSize == front* 4.隊列內有效數據個數:(rear - front + maxSize) % maxSize**/ public class CircleQueueArray {public static void main(String[] args) {//測試一把//創建一個隊列SimulationCircleQueueArray queue = new SimulationCircleQueueArray(4);char key = ' '; //接收用戶輸入Scanner scanner = new Scanner(System.in);//boolean loop = true;//輸出一個菜單while (loop) {/*** 遍歷打印下每次執完后數組中的數據*/int[] arr = queue.getArr();System.out.println("-------------------------------------------------");for (int i = 0; i < arr.length; i++) {int i1 = arr[i];System.out.println("arr數組索引:"+i+"位子當前的數據為:"+i1+".");}System.out.println("-------------------------------------------------");System.out.println("s(show): 顯示隊列");System.out.println("e(exit): 退出程序");System.out.println("a(add): 添加數據到隊列");System.out.println("g(get): 從隊列取出數據");System.out.println("h(head): 查看隊列頭的數據");key = scanner.next().charAt(0);//接收一個字符switch (key) {case 's':queue.showQueue();break;case 'a':System.out.println("輸出一個數");int value = scanner.nextInt();queue.addQueue(value);break;case 'g': //取出數據try {int res = queue.getQueue();System.out.printf("取出的數據是%d\n", res);} catch (Exception e) {System.out.println(e.getMessage());}break;case 'h': //查看隊列頭的數據try {int res = queue.headQueue();System.out.printf("隊列頭的數據是%d\n", res);} catch (Exception e) {// System.out.println(e.getMessage());e.printStackTrace();}break;case 'e': //退出scanner.close();loop = false;break;default:break;}}System.out.println("程序退出~~");} }//創建一個類來模擬環形隊列數組 class SimulationCircleQueueArray{private int maxSize;//表示數字的最大容納量private int front;//模擬指針指向頭部數據,對于普通的隊列做了調整,現在默認指向第一個索引位子,也就是0private int rear;//模擬指針指向尾部數據,同樣也是默認指向第一個索引的位子,也就是0private int[] arr;//數組用于存放數據,模擬隊列//提供一個get方法觀察數組中數據的變化public int[] getArr() {return arr;}/*** 創建環形隊列數組的構造器** @param arrMaxSize 初始化這個環形隊列數組的容量*/public SimulationCircleQueueArray(int arrMaxSize) {maxSize = arrMaxSize;arr = new int[maxSize];//初始化數組的容量front = 0;rear = 0;}//判斷隊列數組是否滿了public boolean isFull() {/*判斷隊列是否滿環形隊列,判斷隊列滿,不僅僅在通過rear = maxSize -1 來判斷因為是環形,尾部指針會回到之前的位子,所以通過取模來重新計算指針的值(rear+1)%maxSize == front;假設隊列內部數組arr的最大容量為4隊列添加數據,rear指針需取模重新計算指針指向值:rear = (rear + 1) % maxSize,并通過(rear + 1) % maxSize == front來判斷隊列是否已滿1.隊列添加數據10,arr[0]=10,rear重新指向1,(1+1)%4!=0,隊列未滿2.隊列添加數據20,arr[1]=20,rear重新指向2,(2+1)%4!=0,隊列未滿3.隊列添加數據30,arr[2]=30,rear重新指向3,(3+1)%4==0,隊列滿,不能再添加數據此時,隊列內所有數據為:arr[0]=10,arr[1]=20,arr[2]=30,隊列有效數據個數:(3-0+4)%4=3*/return (rear+1)%maxSize == front;}//判斷隊列是否為空public boolean isEmpty() {/*隊列當取得指針和添加的指針一致的時候,所明去完了,沒有數據*/return rear == front;}//添加數據到隊列public void addQueue(int data) {/*隊列的添加:一般線性隊列,尾部指針直接遞增而環形隊列,尾部指針可能在次指向索引為0的位子,因此不能單一的通過遞增來處理,需要取模重新計算尾部的指針位置*///添加前先判斷隊列是否滿了if (isFull()) {System.out.println("隊列滿,不能加入數據~~~");return;}arr[rear] = data;//在尾部指針指向的位子添加傳遞的數據//添加值后,尾部指針的走向不能在++遞增來實現,需要從新取模運算rear = (rear + 1)%maxSize;}//獲取隊列的數據,出隊列public int getQueue() {/*環形列隊取數據:和添加數據一樣,添加是尾部指針不能一直遞增,需要取模重新計算尾部指針的位子而頭部指針在每次取出數據后也不能一直遞增,需要取模讓指針回到最初的位子*///先判斷隊列是否為空if (isEmpty()) {//為空就拋出異常,我們這里結束方法使用拋出異常方式throw new RuntimeException("隊列空,不能獲取數據~~~");}//先獲取到頭部指針指向的索引位子獲取出數據int value = arr[front];//對取模計算出下一次頭部指針的位子front = (front + 1) % maxSize;//將數據返回return value;}/*** 顯示隊列所有的數據*/public void showQueue() {if (isEmpty()) {System.out.println("隊列為空,無法顯示隊列的全部數據");return;}// 遍歷數組// 不能單一的對角標遞增處理,會出現角標越界異常// 應對環形隊列內部的有效數據,從頭部數據開始遍歷依次取出// 有效數據的下標:i % maxSizefor (int i = front; i < front + size(); i++) {System.out.printf("arr[%d] = %d\n", i % maxSize, arr[i % maxSize]);}}/*** 獲取隊列有效數據的個數* @return 隊列有效數據的個數*/private int size() {return (rear - front + maxSize) % maxSize;}//顯示隊列的頭數據,不是取數據public int headQueue() {/*獲取列隊頭部數據就是獲取頭部指針指向的位子*/if (isEmpty()) {//為空就拋出異常,我們這里結束方法使用拋出異常方式throw new RuntimeException("隊列空,沒有數據~~~");}return arr[front];} }代碼運行控制臺測試
D:\Java\JDK\java1.8 ------------------------------------------------- arr數組索引:0位子當前的數據為:0. arr數組索引:1位子當前的數據為:0. arr數組索引:2位子當前的數據為:0. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 a 輸出一個數 10 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:0. arr數組索引:2位子當前的數據為:0. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 a 輸出一個數 20 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:0. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 a 輸出一個數 30 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 s arr[0] = 10 arr[1] = 20 arr[2] = 30 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 g 取出的數據是10 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 s arr[1] = 20 arr[2] = 30 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 g 取出的數據是20 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 s arr[2] = 30 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 h 隊列頭的數據是30 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 g 取出的數據是30 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 h java.lang.RuntimeException: 隊列空,沒有數據~~~at com.fs.demo_2020_07_11_queueArray.SimulationCircleQueueArray.headQueue(CircleQueueArray.java:203)at com.fs.demo_2020_07_11_queueArray.CircleQueueArray.main(CircleQueueArray.java:60) ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 s 隊列為空,無法顯示隊列的全部數據 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:0. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 a 輸出一個數 40 ------------------------------------------------- arr數組索引:0位子當前的數據為:10. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:40. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 a 輸出一個數 50 ------------------------------------------------- arr數組索引:0位子當前的數據為:50. arr數組索引:1位子當前的數據為:20. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:40. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據 a 輸出一個數 60 ------------------------------------------------- arr數組索引:0位子當前的數據為:50. arr數組索引:1位子當前的數據為:60. arr數組索引:2位子當前的數據為:30. arr數組索引:3位子當前的數據為:40. ------------------------------------------------- s(show): 顯示隊列 e(exit): 退出程序 a(add): 添加數據到隊列 g(get): 從隊列取出數據 h(head): 查看隊列頭的數據環形隊列的解釋
下面解釋來源于網絡收集
作者:rainchxy
鏈接:https://www.jianshu.com/p/9ba8a65464dd
來源:簡書
為什么要%Maxsize呢?
主要是為了處理臨界狀態,即Q.rear向后移動一個位置Q.rear+1后,很有可能超出了數組的下標,這時它的下一個位置其實是0,如果將一維數組畫成環形圖,如圖所示:
上圖中最大空間Maxsize,當Q.rear=Maxsize-1時,(Q.rear+1)%Maxsize=0,而且Q.front=0,正好滿足隊滿的條件:(Q.rear+1) %Maxsize= Q.front,此時為隊滿。
因此無論是front還是rear向后移動一個位置時,都要加1與最大空間Maxsize取模運算,處理臨界問題。
總結:
隊空:Q.front=Q.rear; // Q.rear和Q.front指向同一個位置
隊滿: (Q.rear+1) %Maxsize=Q.front; // Q.rear向后移一位正好是Q.front
入隊:
Q.base[Q.rear]=x; //將元素放入Q.rear所指空間,
Q.rear =( Q.rear+1) %Maxsize; // Q.rear向后移一位
出隊:
e= Q.base[Q.front]; //用變量記錄Q.front所指元素,
Q.front=(Q.front+1) %Maxsize // Q. front向后移一位
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的队列与环形队列使用数组模拟的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈:稀疏数组与二维数组之间的转换
- 下一篇: 浅谈:数据结构之单链表,java代码演示