數據結構-----4.隊列:

1.特點:隊列有“先進先出”的特點,使用的場景多數用於資源池這種,例如:線程池中對等待線程的處理,數據庫連接池中對線程的管理

2. 隊列的生成有兩種方式:(1)基於數組生成,稱爲順序隊列。(2)基於鏈表生成,稱爲鏈式隊列。

3.基於數組生成隊列的程序:

(1)在隊列中進行入隊操作時,tail指針會不斷地向後移動,直到數組已經被裝滿。進行出隊操作時,head指針會不斷地從0開始向後移動,這樣會出現一個情況:(當tail指針移動到最後時,即使數組的0---head區間有位置,也不會承裝數據,造成數組資源的浪費),所以我們需要採用 “數據搬移” 的操作。

數據搬移有兩種方式:(1)每次有數據入隊,便會搬移一次,相當於每次都刪除下標爲0的數據。這樣入隊操作的時間複雜對便會從O(1)變成O(n)。(2)只有當tail指針到達最後時,並且head指針不爲0,才進行一次整體的數據搬移操作,這樣入隊操作的平均時間複雜度還是O(1)。

/*
* 基於數組實現的順序隊列*/
public class ArrayQueue {
    /*定義隊列中承裝數據的數組*/
    private Object[] elementData;

    /*定義隊列的容量*/
    private int capacity;

    /*定義隊列的頭指針和尾指針*/
    private int head;
    private int tail;

    public ArrayQueue(int capacity) {
        this.elementData = new Object[capacity];
        this.capacity = capacity;
        this.head = 0;
        this.tail = 0;
    }

    /*
    * 入隊操作*/
    public boolean enQueue(Object element) {
        /*判斷當前隊列是否已滿*/
        if(capacity == tail) return false;
        elementData[tail] = element;
        tail++;
        return true;
    }

    /*
    * 採用數據搬移的方法,使數組中的空閒空間,得以充分的利用*/
    public boolean enQueueMove(Object element) {
        /*如果尾指針tail=capacity && head=0時,表明數組已經裝滿*/
        if(tail == capacity ) {
            if(head == 0) return false;
            /*如果尾指針tail=capacity,並且head!=0時,進行數據搬移操作*/
            for(int i=head;i<tail;i++) {
                elementData[i-head] = elementData[i];
            }
            //搬移完數據之後,重新設置head,tail的指針
            tail = tail - head;
            head = 0;
        }
        /*如果隊列後面還有剩餘的位置,那麼數據正常加入隊尾*/
        elementData[tail] = element;
        tail++;
        return true;
    }

    /*
    * 出隊操作*/
    public Object deQueue() {
        /*判斷當前隊列是否爲空*/
        if(tail == head) return null;
        Object element = elementData[head];
        head++;
        return element;
    }

    public static void main(String[] args) {
        ArrayQueue queue = new ArrayQueue(5);
        for(int i=0;i<=5;i++) {
            System.out.println(queue.enQueue(i)+" , "+i);
        }
        for(int i=0;i<=2;i++) {
            System.out.println(queue.deQueue()+" ,出隊");
        }
        System.out.println(queue.enQueueMove("chen")+" , chen");
        System.out.println(queue.enQueueMove("chen")+" , chen");
        System.out.println(queue.enQueueMove("chen")+" , chen");
        System.out.println(queue.enQueueMove("chen")+" , chen");
    }
}
/*基於鏈表生成的隊列*/
public class LinkedListQueue<E> {

    /*定義兩個指針,head指向隊列頭部,tail指向隊列尾部*/
    private Node head = null;

    private Node tail = null;

    /*入隊操作*/
    public void push(E value) {
        Node<E> node = new Node<>(value,null);
        /*tail == null 表示隊列中還沒有加入元素*/
        if(tail == null) {
            head = node;
            tail = node;
        }else {
            tail.setNext(node);
            tail = tail.getNext();
        }
    }

    /*出隊操作*/
    public E pop() {
        if(head == null) return null;
        E value = (E)head.getValue();
        head = head.getNext();
        return value;
    }

    public void printAll() {
        //if(head == null) return;
        while (head != null) {
            System.out.println(head.getValue());
            head = head.getNext();
        }
        return;
    }

    private class Node<E>{

        private E value;

        private Node<E> next;

        public Node(E value,Node<E> next) {
            this.value = value;
            this.next = next;
        }

        public E getValue() {
            return value;
        }

        public void setValue(E value) {
            this.value = value;
        }

        public Node<E> getNext() {
            return next;
        }

        public void setNext(Node<E> next) {
            this.next = next;
        }
    }

    public static void main(String[] args) {
        LinkedListQueue queue = new LinkedListQueue();
        queue.push("1");
        queue.push("2");
        queue.pop();
        queue.push("3");
        queue.push("4");
        queue.pop();
        queue.push("5");
        queue.pop();
        queue.printAll();
    }
}基於數組生成的循環隊列,它的優點在於,在隊列中刪除數據後,空閒的空間可以得到充分的利用,並且不需要進行“搬移數據操作”


/*基於數組生成一個循環隊列*/
public class CircularQueue<E> {
    /*循環隊列中的內部數組*/
    private Object[] elementData;
    /*循環隊列的容量*/
    private int capacity;
    /*循環隊列的頭指針和尾指針*/
    private int head = 0;
    private int tail = 0;

    public CircularQueue(int capacity) {
        this.elementData = new Object[capacity];
        this.capacity = capacity;
    }

    /*入隊操作:
    * 當tail指針指向數組中最後一位時,(tail+1)%capacity == head,最後一位加不上數據,
    * 所以使用循環隊列會浪費一個數組空間*/
    public boolean enQueue(E value) {
        /*如果隊列滿了,返回false*/
        if((tail+1)%capacity == head)
            return false;
        elementData[tail] = value;
        /*因爲當前是一個環形隊列,所以判斷下一位時需要使用(tail+1) % capacity*/
        tail = (tail+1) % capacity;
        return true;
    }

    /*出隊操作*/
    public E deQueue() {
        if(head == tail) return null;
        E value = (E)elementData[head];
        head = (head + 1) % capacity;
        return value;
    }

    public void printAll() {
        while ((head+1)%capacity < tail) {
            System.out.println(elementData[head]);
            head = (head+1)%capacity;
        }
        /*因爲在while循環中,由於判斷的原因,tai指針的前一個元素遍歷不到,所以需要另外打印*/
        if((head+1) % capacity == tail) {
            System.out.println(elementData[head]);
        }
    }

    public static void main(String[] args) {
        /*由於在循環隊列中,最後一個位置不會承裝數據,所以如果定義數組的容量爲9,
          那麼實際上只能承裝8個數據*/
        CircularQueue<String> queue = new CircularQueue<>(9);
        queue.enQueue("1");
        queue.enQueue("2");
        queue.enQueue("3");
        queue.enQueue("4");
        queue.deQueue();
        queue.enQueue("5");
        queue.enQueue("6");
        queue.printAll();
    }
}

阻塞隊列:

(1)定義:阻塞隊列就是在正常隊列的情況下,添加阻塞操作。當隊列爲空時,從隊列獲取數據的操作會被阻塞,直到隊列中    有數據。當隊列滿時,向隊列添加數據時會被阻塞,直到隊列中存在空閒位置。

基於阻塞隊列可以實現一個 生產者---消費者模型,並且還可以通過協調生產者和消費者的個數,來提高數據的處理效率。

線程池的底層,也是通過一個隊列實現的,當線程池中的線程全部都在工作的時候,當有新的任務請求過來時,就會將任務加入到線程池的隊列中,等待線程去執行。

實現隊列有兩種方式,數據和鏈表,基於鏈表實現的隊列,是一個沒有界限的隊列,所以當前任務等待的時間會比較長,對於時間響應敏感的系統來說,不太適合,這時可以選用基於數組實現的隊列,可以固定隊列的長度,隊列裝滿之後的任務會被拒絕掉,所以系統的響應時間會比較快。

合理的設置隊列的容量也是有講究的,容量太大,會降低系統的響應時間,容量太小,系統的資源得不到充分的利用。實際上對於大部分資源有限的場景,在沒有空閒資源時,都可以使用隊列這種數據結構來實現請求排隊。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章