數據結構-隊列

隊列

概念

隊列:是限只容許在一端進行插入操做,而在另外一端進行刪除操做的線性表java

  • 遵循先進先出原則,簡稱FIFO
  • 線性表 —> 用數組或是鏈表來實現。

image-20200705221828232

抽象數據類型

隊列也有相似線性表的各類操做,不一樣的是算法

  • 插入數據只能在隊尾進行
  • 刪除數據只能在隊頭進行

image-20200705221918031

實際場景:銀行叫號排隊數組

數組模擬隊列

image-20200517181444935

思路分析

  • 有一個頭指針front,表示隊列最前面的元素
  • 有一個尾指針rear, 表示隊列的最後一個元素
  • addQueue 將尾指針後移 rear +1 , 當front == rear 【空】
  • 若尾指針rear小於隊列的最大下標maxSize-1,則將數據存入rear所指的數組元素總,不然沒法存入數據。rear == maxSize -1 【隊列滿】

代碼實現

package com.queue;

/**
 * 普通隊列只是一次性儲存
 * 不能複用前面的空間
 * front指向第一個元素的【前面】!
 * rear指向最後一個元素
 * 判斷空,兩指針相等
 * 判斷滿,尾指針指向最大下標
 */
public class ArrayQueue {
    private int maxSize;
    private int front;
    private int rear;
    private int[] arr;

    // 建立隊列的構造器

    public ArrayQueue(int maxSize) {
        this.maxSize = maxSize;
        // 建立指定長度的數組模擬隊列
        arr = new int[maxSize];
        // 指向隊列頭部,不包含那個數,好比指向0,實際:下標1纔是隊列頭
        this.front = -1;
        // 指向隊列尾部,包含!好比指向10,實際:下標10就是隊尾
        this.rear = -1;
    }

    /**
     * 判斷隊列是否滿
     * 無參
     * @return
     */
    public boolean isFull() {
        return rear == maxSize - 1;
    }

    /**
     * 判斷隊列是否空
     * 無參
     * @return
     */
    public boolean isEmpty() {
        return rear == front;
    }

    /**
     * 入隊
     *
     * @param n 添加的數據
     */
    public void addQueue(int n) {
        // 首先判斷是否滿
        if (isFull()) {
            System.out.println("隊列已滿,沒法加入");
            return;
        }
        // 尾指針後移
        rear++;
        arr[rear] = n;
    }

    /**
     * 出隊
     * @return
     */
    public int getQueue() {
        // 先判斷是否爲空
        if (isEmpty()) {
            throw new RuntimeException("隊列爲空,不能取數據");
        }
        // 頭指針後移
        front++;
        return arr[front];
    }

    /**
     * 打印隊列中的有效數據
     *
     */
    public void show() {
        // 遍歷
        if (isEmpty()) {
            System.out.println("隊列爲空");
            return;
        }
        for (int i = front + 1; i <= rear; i++) {
            System.out.print(arr[i] + "\t");
        }
    }

    /**
     * 不是出隊入隊
     * @return 返回隊首元素
     */
    public int headQueue() {
        // 先判斷是否爲空
        if (isEmpty()) {
            throw new RuntimeException("隊列爲空,不能取數據");
        }
        return arr[front + 1];
    }

    /**
     * 不是出隊入隊
     * @return 返回隊尾元素
     */
    public int tailQueue() {
        // 先判斷是否爲空
        if (isEmpty()) {
            throw new RuntimeException("隊列爲空,不能取數據");
        }
        return arr[rear];
    }
}

測試代碼

package com.queue;

import java.util.Scanner;

public class Application {
    public static void main(String[] args) {
        // 建立一個隊列,最大儲存4個元素
        ArrayQueue queue = new ArrayQueue(4);
       
        Scanner sc = new Scanner(System.in);
        int op = -1;
        do {
            System.out.println("請選擇測試功能");
            System.out.println("1.顯示隊列");
            System.out.println("2.入隊操做");
            System.out.println("3.出隊操做");
            System.out.println("4.查看頭元素");
            System.out.println("5.查看尾元素");
            System.out.println("0.退出");
            op = sc.nextInt();
            switch (op) {
                case 1:
                    System.out.print("隊列中的數據:");
                    queue.show();
                    break;
                case 2:
                    System.out.print("請輸入入隊元素:");
                    int n = sc.nextInt();
                    queue.addQueue(n);
                    System.out.print("隊列中的數據:");
                    queue.show();
                    break;
                case 3:
                    try {
                        queue.getQueue();
                        System.out.print("隊列中的數據:");
                        queue.show();
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 4:
                    try {
                        System.out.println("隊頭元素"+queue.headQueue());
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 5:
                    try {
                        System.out.println("隊尾元素"+queue.tailQueue());
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                default:
                    break;
            }

            System.out.println();
            System.out.println();
        } while (op != 0);

        System.out.println("測試結束");
    }
}

問題分析

  • 隊列只能使用一次,沒有複用的效果
  • 使用算法改進爲環形隊列,取模操做。

數組模擬循環隊列

把隊列頭尾相接的順序存儲結構稱爲循環隊列測試

對前面的隊列存在「假溢出」的問題進行優化,充分利用數組優化

將數組當作一個環形的,經過取模的方式便可實現。this

分析說明

image-20200517184115592

image-20200517191610079

代碼實現

package com.queue;

/**
 * 循環隊列能夠充分利用空間
 * front 指向隊列的【第一個】元素
 * rear 指向最後一個元素的【後一個】
 * 判斷爲【空】 rear == front
 * 判斷爲【滿】(rear + 1)% maxSize == front
 */
public class CircleArrayQueue {
    private int maxSize;
    private int front;
    private int rear;
    private int[] arr;

    // 建立隊列的構造器

    public CircleArrayQueue(int maxSize) {
        this.maxSize = maxSize;
        // 建立指定長度的數組模擬隊列
        arr = new int[maxSize];
        // 指向隊列頭部,包含那個數
        this.front = 0;
        // 指向隊列尾部的後一個
        this.rear = 0;
    }

    /**
     * 判斷隊列是否滿
     * 無參
     *
     * @return
     */
    public boolean isFull() {
        return (rear + 1) % maxSize == front;
    }

    /**
     * 判斷隊列是否空
     * 無參
     *
     * @return
     */
    public boolean isEmpty() {
        return rear == front;
    }

    /**
     * 入隊
     * @param n 添加的數據
     */
    public void addQueue(int n) {
        // 首先判斷是否滿
        if (isFull()) {
            System.out.println("隊列已滿,沒法加入");
            return;
        }
        // 先入隊再後移
        arr[rear] = n;
        // rear++ 會出現下標溢出
        rear = (rear + 1) % maxSize;
    }

    /**
     * 出隊
     * @return 返回隊列第一個元素,同時指針後移
     */
    public int getQueue() {
        // 先判斷是否爲空
        if (isEmpty()) {
            throw new RuntimeException("隊列爲空,不能取數據");
        }
        // 頭指針後移
        int value = arr[front];
        front = (front + 1) % maxSize;
        return value;
    }

    /**
     * 打印隊列中的有效數據
     */
    public void show() {
        // 遍歷
        if (isEmpty()) {
            System.out.println("隊列爲空");
            return;
        }
        for (int i = front ; i < front + size(); i++) {
            // i 可能會溢出,因此要取模循環
            int idx = i % maxSize;
            System.out.print(arr[idx] + "\t");
        }
    }

    public int size() {
        return (rear + maxSize -front)%maxSize;
    }

    /**
     * 不是出隊入隊
     *
     * @return 返回隊首元素
     */
    public int headQueue() {
        // 先判斷是否爲空
        if (isEmpty()) {
            throw new RuntimeException("隊列爲空,不能取數據");
        }
        return arr[front];
    }

    /**
     * 不是出隊入隊
     *
     * @return 返回隊尾元素
     */
    public int tailQueue() {
        // 先判斷是否爲空
        if (isEmpty()) {
            throw new RuntimeException("隊列爲空,不能取數據");
        }
        return arr[rear-1];
    }
}

測試代碼指針

package com.queue;

import java.util.Scanner;

public class Application {
    public static void main(String[] args) {
        // 建立一個隊列,最大儲存4個元素
        // ArrayQueue queue = new ArrayQueue(4);
        // 建立一個循環隊列,須要留出一個空位判斷是否滿,因此最大儲存4個元素
        CircleArrayQueue queue = new CircleArrayQueue(5);
        Scanner sc = new Scanner(System.in);
        int op = -1;
        do {
            System.out.println("請選擇測試功能");
            System.out.println("1.顯示隊列");
            System.out.println("2.入隊操做");
            System.out.println("3.出隊操做");
            System.out.println("4.查看頭元素");
            System.out.println("5.查看尾元素");
            System.out.println("0.退出");
            op = sc.nextInt();
            switch (op) {
                case 1:
                    System.out.print("隊列中的數據:");
                    queue.show();
                    break;
                case 2:
                    System.out.print("請輸入入隊元素:");
                    int n = sc.nextInt();
                    queue.addQueue(n);
                    System.out.print("隊列中的數據:");
                    queue.show();
                    break;
                case 3:
                    try {
                        queue.getQueue();
                        System.out.print("隊列中的數據:");
                        queue.show();
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 4:
                    try {
                        System.out.println("隊頭元素"+queue.headQueue());
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 5:
                    try {
                        System.out.println("隊尾元素"+queue.tailQueue());
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                default:
                    break;
            }

            System.out.println();
            System.out.println();
        } while (op != 0);

        System.out.println("測試結束");
    }
}

速記

區別 普通隊列 循環隊列
指針 指針向前(front指向前一個) 指針向後(rear指向後一個)
初始值 兩個都是-1 兩個都是0
判斷空 rear == front rear == front
判斷滿 rear == maxSize -1 (rear + 1) % maxSize == front
指針後移 ++ 加1取模
隊列元素個數 rear-front rear -front 或 (rear - 0) + (maxSize - front)
合併公式 (rear + maxSize - front) % maxSize

隊列的鏈式存儲結構

隊列的鏈式存儲結構,其實就是線性表的單鏈表,只不過它只能尾進頭出而已,簡稱爲鏈隊列。code

爲了操做上的方便,咱們將隊頭指針指向鏈隊列的頭結點,blog

實現代碼

package com.queue;

public class LinkQueue {
    // 隊頭指針,隊尾指針
    private QNode front;
    private QNode rear;

    public LinkQueue() {
        //初始化頭結點
        front = new QNode();
        rear = front;
    }

    public boolean isEmpty() {
        return front == rear;
    }

    public void addQueue(int num) {
        QNode qNode = new QNode(num);
        rear.next = qNode;
        rear = qNode;
    }

    public int delQueue() {
        if (isEmpty()) {
            throw new RuntimeException("隊空了");
        }
        // 第一個結點沒有數據
        int value = front.next.data;
        // 頭結點要一直保留,跳過第一個有數據的結點
        front.next = front.next.next;
        // 若是無數據結點,尾指針歸位。
        if (front.next == null) {
            rear = front;
        }
        return value;
    }

    public void list() {
        if (isEmpty()) {
            System.out.println("隊空了");
        }
        QNode temp = front.next;
        while (temp != null) {
            System.out.print(temp.data + "\t");
            temp = temp.next;
        }

    }

}

class QNode{
    public int data;
    public QNode next;

    public QNode() {}

    public QNode(int data) {
        this.data = data;
    }
}

總結回顧

棧和隊列都是特殊的線性表,只不過對插入和刪除操做作了限制隊列

棧:是限定僅在表尾進行插入和刪除操做的線性表

隊列:是隻容許在一端進行插入操做,在另外一端進行刪除操做的線性表

他們都可用線性表的順序存儲結構實現,都存在則順序存儲的一些弊端。

也均可以經過鏈式存儲結構實現,原則上與線性表基本相同。

相關文章
相關標籤/搜索