隊列(單鏈表實現)


  隊列,就是排隊,先到的站前面,先離開,后到的排后面,后離開。對應到計算機中,就是添加元素在隊尾,刪除元素是在隊頭,先進先出或后進后出。添加元素也叫入隊(enqueue),刪除元素也叫出隊(dequeue)。當然還可以查看隊頭元素,隊中元素個數,以及是否為空,所以隊列提供了API 就是enqueue, dequeue,getFront, size, isEmpty。

  使用單鏈表實現隊列

  隊列在尾部添加元素,在頭部刪除元素。那就讓鏈表頭作為隊列的頭部,因為鏈表頭部容易執行刪除操作(出隊)。鏈表尾部只能作為隊列的尾部,執行插入操作(入隊)。但鏈表尾部執行插入操作,有一個問題,那就是每次都要遍歷整個鏈表,找到最后一個元素,才能執行插入操作。為了減少遍歷,要再維護一個尾指針,指向鏈表的尾部。因此,使用單鏈表實現一個隊列,鏈表需要維護兩個指針,頭指針和尾指針。頭指針指向鏈表的頭部,用於出隊。尾指針指向鏈表尾部,用於入隊。

public class LinkedQueue<T> {
    private class Node {
T data;
Node next;
Node(T data) {
this.data = data;
}
}

private Node firstNode; //頭指針,隊頭
private Node lastNode; // 尾指針,隊尾
private int size;

public void enqueue(T data){}
public T dequeue(){}
public int size(){}
public boolean isEmpty(){}
public T getFront(){}
public void clear(){}
}

  enqueue的實現,就是向鏈表尾部插入一個節點。創建一個新節點,如果隊列(鏈表)為空,直接讓頭尾指針都指向它

  如果鏈表不空,讓尾節點的next指向它,同時更新尾指針的指向,讓它指向最新的尾節點

public void enqueue(T data){
    Node newNode = new Node(data);
    if(isEmpty()){
        firstNode = lastNode =newNode;
    } else {
        lastNode.next = newNode;
        lastNode = newNode;
    }
size++; }

  dequeue的實現,就是鏈表頭部刪除一個節點。鏈表為空,肯定是不能被刪除的,如果鏈表不空,取出第一個節點,然后讓頭指針指向它的next節點就好了,

  要注意的是一直刪除,頭指針會指向null,也就是鏈表中沒有元素了,尾指針也要指向null

public T dequeue(){
    if(isEmpty()){
        throw new RuntimeException("鏈表為空");
    }

    T frontData = firstNode.data;
    firstNode = firstNode.next;

    if(firstNode == null){
        lastNode = null;
    }

    size--;

    return frontData;
}

  其它幾個實現比較簡單

public int size(){
    return size;
}
public boolean isEmpty(){
    return firstNode == null;
}
public T getFront(){
    if(isEmpty()){
        throw new RuntimeException("鏈表為空");
    }

    return firstNode.data;
}

public void clear(){
    firstNode = null;
    lastNode = null;
}

  使用隊列模擬現實的隊列,比如買奶茶,以測算奶茶店的服務能力。如果要統計1小時內的服務能力,可以計算,在一小時內的到達人數,服務人數,等待時間等等。怎么統計呢?1小時,可以分60分鍾,每一分鍾檢測一次,有沒有顧客來,如果有就加到隊列中,如果沒有,就看有沒有顧客在服務,如要有,就繼續服務,如果沒有,服務下一位顧客。怎么知道有沒有人來?由於每一個顧客的到達時間是隨機的,可以使用一個隨機數,如果生成的隨機數小於一個閾值,就說明有顧客到,反之,則沒有顧客到。 由於每個顧客的服務時間也不一樣,可以再使用一個隨機數,計算出服務時間。可以看出有兩個類,WaitLine和Customer,在WaitLine中有到達人數(numberOfArrived),服務人數(numberServed),  等待時間(totalTimeWaited),在Customer中有到達時間(arriveTime),服務時間(transactionTime)和排隊號碼(customerNum)。

public class WaitLine {
    private int numberOfArrivals;
    private int numberServed;
    private int totalTimeWaited;

    private class Customer {
        int arriveTime;
        int transactionTime;
        int customerNum;

        Customer(int arriveTime, int transactionTime, int customerNum) {
            this.arriveTime = arriveTime;
            this.transactionTime = transactionTime;
            this.customerNum = customerNum;
        }
    }
}

  現在模擬一下隊列的情形,顧客到來的時間是隨機的,假設有50%的概率會來,那就表示,只要生成的隨機數小於50%,就表明顧客到了,加入隊列。顧客的服務時間也是不固定的,可以聲明一個最大服務時間,然后和隨機數相乘,假設最大服務時間是5s。顧客有沒有在服務,就是看它的服務時間有沒有到0,如果到了,就表示服務完成,到下一位顧客。

// duration: 要統計的服務時間區間,比如60分鍾
// arrivalProbability:每秒鍾顧管到達的概率, 比如50%
// maxTransactionTime:每位顧客的最長服務時間
public void simulate(int duration, double arrivalProbability, int maxTransactionTime) {
    var line = new LinkedQueue<Customer>(); // 創建一個隊列, 
    var transactionTimeLeft = 0;  // 每個顧客服務時間的剩余時間,表示一個顧客在不在服務

   // clock就是每一秒,用戶的到達時間和用戶的服務時間都用clock記錄 for (int clock = 0; clock < duration; clock++) { // 監測用戶到沒到,隨機數小於規定的概率,表示有顧客到 if (Math.random() < arrivalProbability) { numberOfArrivals++; var transactionTime = (int) (Math.random() * maxTransactionTime + 1);//生成每位顧客的服務時間 var customer = new Customer(clock, transactionTime, numberOfArrivals); // 創建到達的顧客 line.enqueue(customer); } // 某位顧客是否還在服務中 if (transactionTimeLeft > 0) { transactionTimeLeft--; //還在服務中,繼續服務,不過服務時間要減1 } else { if(!line.isEmpty()) { var nextCustomer = line.dequeue(); // 顧客離開隊列,被服務 transactionTimeLeft = nextCustomer.transactionTime - 1; // 賦值服務時間,下次驗證是不是在服務他 var waitingTime = clock - nextCustomer.arriveTime; // 每個用戶等待服務的時間 totalTimeWaited = totalTimeWaited + waitingTime; // 整個隊列中用戶等待的時間 numberServed++; } } } }

  測試一下

public static void main(String[] args) {
    WaitLine customerLine = new WaitLine();
    customerLine.simulate(20, 0.5, 5);
    System.out.println(customerLine.numberServed);
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM