深入分析數(shù)據(jù)結(jié)構(gòu)中的隊列(java語言實(shí)現(xiàn))

不知道你有沒有過在餐廳打飯的經(jīng)歷倔矾,我們排的隊其實(shí)就是我們今天所講的主題妄均,我們在排隊的時候柱锹,在隊列頭部的人打好飯離開,新來的人排在隊尾丰包。這就是入隊和出隊的操作禁熏。所以,隊列的特性就是先進(jìn)先出邑彪。有了這個概念瞧毙,就可以開始今天的主題。先給出這篇文章的大致脈絡(luò):

首先寄症,使用java語言描述了隊列的基本操作宙彪,有鏈?zhǔn)酱鎯晚樞?/p>

然后,介紹循環(huán)隊列和一系列需要注意的知識點(diǎn)

最后有巧,對隊列進(jìn)行一個總結(jié)释漆。

OK,開始今天的文章篮迎。

一灵汪、隊列基本操作

1、隊列的基本認(rèn)識

隊列的基本特性就是先進(jìn)先出(FIFO)柑潦。也就是第一個進(jìn)去的元素享言,第一個出來。現(xiàn)在給出一個標(biāo)準(zhǔn)的定義:

隊列就是一個只允許在一端進(jìn)行插入渗鬼,在另一端進(jìn)行刪除操作的線性表览露。

既然是線性表,按照存儲方式那就有兩種存儲方式譬胎,基于數(shù)組的順序存儲方式和基于鏈表的鏈?zhǔn)酱鎯Ψ绞健?/p>

隊列按照實(shí)現(xiàn)方式也分為兩種:

①差牛、單向隊列(Queue):只能在一端插入數(shù)據(jù),另一端刪除數(shù)據(jù)堰乔。

②偏化、雙向隊列(Deque):每一端都可以進(jìn)行插入數(shù)據(jù)和刪除數(shù)據(jù)操作。

有了對概念分類有了了解镐侯,下面我們就是用java代碼來實(shí)現(xiàn)這些隊列

2侦讨、順序隊列的實(shí)現(xiàn)

順序隊列是基于數(shù)組實(shí)現(xiàn)的,針對于隊列的操作主要有以下幾個

(1)插入(2)刪除(3)查找元素(4)隊列長度(5)是否為空苟翻、

我們先給出最基本的操作圖解

2-順序隊列入隊出隊.png

下面就根據(jù)這幾個常用的操作使用java代碼來實(shí)現(xiàn)隊列

首先韵卤,我們定義一個操作隊列的接口

public interface Queue<T> {
      //增加元素
     void add(T t);
      //刪除元素
      T remove();
      //隊列長度
      int size();
      //返回對頭元素,并未刪掉
      T front();  
      //是否為空
      boolean isEmpty();
      //是否已滿
      boolean isFull()
}

然后我們就開始去實(shí)現(xiàn)崇猫。

插入操作沈条,我們首先要先判斷當(dāng)前隊列是否已滿,如果滿了直接返回诅炉。接下來得到當(dāng)前元素的位置蜡歹。最后插入元素屋厘,再移動位置。

刪除操作與插入操作類似月而,首先要先判斷當(dāng)前隊列是否為空擅这,如不為空再移動指針(這里不是指針,指的是指向當(dāng)前元素位置的標(biāo)志)景鼠,刪除元素。

判斷當(dāng)前元素是否為空和是否已滿類似痹扇,只需要判斷當(dāng)前隊列的元素數(shù)量铛漓。

下面給出代碼實(shí)現(xiàn)。

public class ArrayQueue<T> implements Queue<T>{
    private T[] data;
    private int size;//元素個數(shù)
    private int front;//隊列中第一個對象的位置
    private int rear;//隊列中當(dāng)前對象的位置
    //初始化方法
    public ArrayQueue() {
        data = (T[]) new Object[10];
        size = 0;
        front =0;
        rear = 0;
    }
    //增加元素
    @Override
    public void add(T t) {
        if(isFull()){
            resize();
            front = 0;
        }
        rear = (front+size)%data.length;
        System.out.println(rear);
        data[rear] = t;
        size++;
    }
    //刪除元素
    @Override
    public T remove() {
         if (isEmpty()) {  
                throw new RuntimeException("隊列為空!");  
            }  
            T tempData = data[front];  
            data[front] = null;  
            front = (front + 1) % (data.length);
            size--;  
            return tempData;  
    }
    //取得隊列大小
    @Override
    public int size() {
        return size;
    }
    @Override
    public boolean isEmpty() {
        return size == 0;
    }
    //判斷當(dāng)前隊列是否已滿
    @Override
    public boolean isFull(){
        return size == data.length;
    }
    //取得當(dāng)前隊頭元素
    @Override
    public T front() {
         if (isEmpty()) {  
                throw new RuntimeException("隊列為空!");  
            }  
            return data[front];
    }
    
    //擴(kuò)容
    public void resize(){
        /*注意重新擴(kuò)容的時候并不需要去設(shè)置size
         * 隊列的大小并不能通過數(shù)組的大小直觀的顯示出來鲫构。
         * 但是棧就可以直觀的通過數(shù)組的大小顯示出來*/
        T[] tmp = (T[]) new Object[data.length*2];
        System.arraycopy(data, 0, tmp, 0, data.length);  
        data = tmp;  
        tmp = null;//引用置為空浓恶,便于gc處理  
    }
}

3、鏈?zhǔn)疥犃械膶?shí)現(xiàn)

鏈?zhǔn)疥犃信c順序隊列不同结笨,每個節(jié)點(diǎn)不僅保存當(dāng)前元素的值包晰,還有指向下一個元素的指針。所以我們先看一下每個節(jié)點(diǎn)的圖解形式


3-鏈?zhǔn)疥犃行问?png

使用代碼來表示就是

public class QueueNode<Item>{
    Item item;
    QueueNode next;
}

然后我們看鏈?zhǔn)疥犃腥腙牶统鲫牭牟僮?/p>

4-鏈?zhǔn)疥犃性鰟h改查.png

(a)空隊列 (b)X入隊 (c)y入隊 (d)x出隊

從上面的操作我們可以看到炕吸,實(shí)現(xiàn)上面的操作我們需要實(shí)現(xiàn)下面的代碼

(1)一個指向FIFO頭節(jié)點(diǎn)的指針

(2)一個指向FIFO尾節(jié)點(diǎn)的指針

(3)一個記錄節(jié)點(diǎn)數(shù)的Int變量

private QueueNode first;//指向FIFO頭節(jié)點(diǎn)的指針
private QueueNode last; //指向FIFO尾節(jié)點(diǎn)的指針
private int nodeNum;    //記錄節(jié)點(diǎn)數(shù)的Int變量

(4)判斷當(dāng)前隊列是否為空

 public boolean isEmpty(){
    return first == null;
 }

(5)隊列的大小

public int size(){
    return nodeNum;
}

(6)入隊和出隊操作

//入隊
public void enqueue(Item item) {
    QueueNode oldLast = last;
    last = new QueueNode();
    last.item = item;
    last.next = null;
    if (isEmpty()){
       first = last;
    } else{
        oldLast.next = last;
    }
    nodeNum++;    
}
//出隊
public Item dequeue(){
    Item item = (Item) first.item;
    first = first.next;
    if (isEmpty()){
       last = null;
    }
    nodeNum--;
    return item;
}

(7)遍歷

    private class LinkListIterator implements Iterator<Item>{
        QueueNode node = first;
        @Override
        public boolean hasNext(){
            return node.next != null;
        }
        @Override
        public Item next(){ 
            Item item = (Item) node.item;
            node = node.next;
            return item;
        }
    }

OK伐憾。有了上面的這些操作之后,下面給出一個完整的代碼

public class Queue<Item> implements Iterable<Item>{
    private QueueNode first;
    private QueueNode last;
    private int nodeNum;
    public boolean isEmpty(){
        return first == null;
    }
    public int size(){
        return nodeNum;
    }
    public void enqueue(Item item){
        QueueNode oldLast = last;
        last = new QueueNode();
        last.item = item;
        last.next = null;
        if (isEmpty()){
            first = last;
        } else{
            oldLast.next = last;
        }
        nodeNum++;
    }
    public Item dequeue(){
        Item item = (Item) first.item;
        first = first.next;
        if (isEmpty()){
            last = null;
        }
        nodeNum--;
        return item;
    }
    @Override
    public Iterator<Item> iterator(){
        return new LinkListIterator();
    }
    private class LinkListIterator implements Iterator<Item>{
        QueueNode node = first;
        @Override
        public boolean hasNext(){
            return node.next != null;
        }
        @Override
        public Item next(){
            Item item = (Item) node.item;
            node = node.next;
            return item;
        }
    }
}

OK赫模,上面就是順序隊列和鏈?zhǔn)疥犃械幕緦?shí)現(xiàn)树肃。下面我們將介紹一種新的隊列循環(huán)隊列,為什么要有這個隊列瀑罗,我們先要考慮一個問題胸嘴,也就是我們的隊列彈出一個元素,那么這個空間將不能使用斩祭。這就是假溢出問題:

4-5假溢出問題.png

為了解決這個問題所以才出現(xiàn)了一個新的隊列-循環(huán)隊列

二劣像、循環(huán)隊列

我們先給出隊列的圖解形式


5-循環(huán)隊列.png

有了循環(huán)隊列的基本實(shí)現(xiàn),我們思考一個問題摧玫,在之前我們可以根據(jù)rear直接得到當(dāng)前隊列是否為空和是否已滿耳奕。那么在循環(huán)隊列里面還可以這樣嘛?當(dāng)然不可以诬像,我們需要考慮font和rear的位置關(guān)系來判斷吮铭。我們看下面兩種情況:


6-判斷位置.png

一種是隊列為空的時候,front和rear都指向同一個位置颅停。一種是隊列已滿的時候谓晌,front和rear指向的元素緊鄰。

我們可以使用下面的公式來判斷


7-計算隊列長度的公式.png

我們代碼實(shí)現(xiàn)一下循環(huán)隊列

public interface IQueue {
    public void clear();
    public boolean isEmpty();
    public int length();
    public Object peek();// 取隊首元素
    public void offer(Object x) throws Exception;// 入隊
    public Object poll();// 出隊
    public void display();
}

然后是接口的實(shí)現(xiàn)

public class CircleSqQueue implements IQueue {
    private Object[] queueElem;//隊列存儲空間
    private int front;//隊首引用癞揉,若隊列不為空纸肉,指向隊首元素
    private int rear;//隊尾引用溺欧,若隊列不為空,指向隊尾的下一個元素
    public CircleSqQueue(int maxsize) {
        front=rear=0;
        queueElem=new Object[maxsize];//分配maxsize個單元
    }
    @Override
    public void clear() {
        front=rear=0;
    }
    @Override
    public boolean isEmpty() {
        return rear==front;
    }
    @Override
    public int length() {
        return (rear-front+queueElem.length)%queueElem.length;
    }
    @Override
    public Object peek() {
        if(front==rear){
            return null;
        }
        else{
            return queueElem[front];
        }
    }
    @Override
    public void offer(Object x) throws Exception {
        if((rear+1)%queueElem.length==front){//隊滿
            throw new Exception("隊列已滿");
        }
        else{
            queueElem[rear]=x;
            rear=(rear+1)%queueElem.length;//修改隊尾指針
        }
    }
    @Override
    public Object poll() {
        if(front==rear){
            return null;//隊列為空
        }
        else{
            Object t=queueElem[front];
            front=(front+1)%queueElem.length;
            return t;
        }
    }
    @Override
    public void display() {
        if(!isEmpty()){
            for(int i=front;i!=rear;i=(i+1)%queueElem.length){
                System.out.println(queueElem[i].toString()+" ");
            }
        }
        else{
            System.out.println("此隊列為空");
        }
    }
}

三柏肪、總結(jié)

對于隊列主要在于面試中常見的算法題姐刁,因?yàn)楦拍罘浅H菀桌斫猓覀冎灰鶕?jù)當(dāng)前實(shí)現(xiàn)的代碼進(jìn)行一些變化就好烦味。謝謝大家的關(guān)注聂使。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谬俄,隨后出現(xiàn)的幾起案子柏靶,更是在濱河造成了極大的恐慌,老刑警劉巖溃论,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屎蜓,死亡現(xiàn)場離奇詭異,居然都是意外死亡钥勋,警方通過查閱死者的電腦和手機(jī)炬转,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來算灸,“玉大人扼劈,你說我怎么就攤上這事》坡浚” “怎么了测僵?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谢翎。 經(jīng)常有香客問我捍靠,道長,這世上最難降的妖魔是什么森逮? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任榨婆,我火速辦了婚禮,結(jié)果婚禮上褒侧,老公的妹妹穿的比我還像新娘良风。我一直安慰自己,他們只是感情好闷供,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布烟央。 她就那樣靜靜地躺著,像睡著了一般歪脏。 火紅的嫁衣襯著肌膚如雪疑俭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天婿失,我揣著相機(jī)與錄音钞艇,去河邊找鬼啄寡。 笑死,一個胖子當(dāng)著我的面吹牛哩照,可吹牛的內(nèi)容都是我干的挺物。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼飘弧,長吁一口氣:“原來是場噩夢啊……” “哼识藤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起次伶,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤痴昧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后学少,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡秧骑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年版确,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乎折。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡绒疗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出骂澄,到底是詐尸還是另有隱情吓蘑,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布坟冲,位于F島的核電站磨镶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏健提。R本人自食惡果不足惜琳猫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望私痹。 院中可真熱鬧脐嫂,春花似錦、人聲如沸紊遵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暗膜。三九已至匀奏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間学搜,已是汗流浹背攒射。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工醋旦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人会放。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓饲齐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親咧最。 傳聞我的和親對象是個殘疾皇子捂人,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容