データ構造とアルゴリズム(F)は、円形キュー - シーケンシャルストレージ構造

循環キュー


ノー循環キュー、キュー・サイクルの外観欠点ストレージ構造を定義するために急いで。

欠点順次ストレージ・キュー構造

順次記憶キュー構造自体はデータ要素がデキューする場合、同等の要素のArrayListヘッダを削除し、添加元素のArrayListテーブルの端部に対応する、ArrayListに、エンキューデータ要素によって達成されます。明らかに、時間計算量はO(1)エンキュー、時間複雑度はO(n)をデキューし、線状複素シンボルの追加または削除データ要素は、時間O(N)であるが、平均値が計算されます。時間複雑キューデキューO(n)のたびにチームはO(n)があるので、平均的には、計算されません。

欠点は、他のアイデアのために最適化することができ、があります

最適化:これ要素としてチームのヘッドポインタとテールポインタそのテール要素に一つだけの要素があると、テールHOL一致処理が煩雑になる時間を避けるために、全て組み込ま二つのポインタ、フロントポイントヘッド素子に、リアポインティング変更が動きます。私たちは、エンキューおよびデキュー操作はO(1)です。
問題点:テーブルの最後にリアポインタが後に移動されません、とシフトがチームのフロントされた後、スペースの前に無駄になっています。

最適化2:チームヘッドまたはテール・ポインタは、テール部分に到達するが、逆方向ヘッダのリダイレクトすることができ、それはリングに、端部に直線状接続端部に相当し、周期のヘッドエンドからのポインタがある、すなわち数フォーム直線長さ、テーブルポインタ+1モジュロポインタの直線長さは、これは無駄なスペースの問題を解決するだろう、スタートからフィニッシュジャンプに完了することができます。
質問:しかし、新たな問題があり、我々は完全なキューを決定し、キューの空の条件は、(1 +リア)%nは==ているフロント、 右の判断を下すことができない直線状の長さはn。

最適化3:確保された空間はヌル空間、時間、キュー満杯状態(1 +リア)%N ==に常に、ポイントテール・ポインタを任意の要素が存在しないフロント、 キューが空の状態リア==正面あります、正のソリューション。

実際には、上記の最適化の結果は、我々は、この接触構造が順次格納される循環キューと呼ばれる呼び出すキューの循環キューの先頭と末尾です。

ここに画像を挿入説明
円形のキュー構造、新しいArrayQueueLoopに格納されたJavaコードシーケンスで実行される方法は、次のように書き換え前記キューインタフェースを実装し、詳細なメモ

import java.util.Iterator;

//循环队列  底层用动态数组实现
public class ArrayQueueLoop<E> implements Queue<E> {
    private E[] data;     //定义数组
    private int front;    //头指针
    private int rear;     //尾指针 指向
    private int size;     //有效元素个数

    public ArrayQueueLoop(){
        data= (E[]) (new Object[11]);//因为有一个空的空间,有效能存储的空间有10个
        front=0;    //初始化
        rear=0;
        size=0;
    }

    @Override
    public int getSize() {
        return size;    //获取有效元素个数,直接返回size即可
    }

    @Override
    public boolean isEmpty() {
        return size==0&&front==rear;    //循环队列为空的条件
    }

    @Override
    public void enqueue(E e) {
        if((rear+1)%data.length==front){       //队列满时
            resize(2*data.length-1);    //扩容,扩二倍
        }
        data[rear]=e;     //把e从队尾入队列,rear角标处是空的空间
        rear=(rear+1)%data.length;  //更新尾指针,rear重新指向空
        size++;   //有效元素+1
    }

    @Override
    public E dequeue() {
        if(isEmpty()){  //队列空时不可出队,抛异常
            throw new IllegalArgumentException("队列空");
        }
        E ret=data[front];  //变量ret存头指针的元素,用于返回
        front=(front+1)%data.length;   //更新头指针
        size--;     //有效元素-1
        //当有效元素小于等于能存储的数组长度,同时缩容后长度的大于等于10,方可进行缩容操作
        if(size<=(data.length-1)/4&&(data.length-1)/2>=10){
            resize((data.length-1)/2+1);   //缩容缩一半
        }
        return ret;    //返回被删除的元素
    }
    //扩缩容函数
    private void resize(int newLen){
        E[] newData= (E[]) (new Object[newLen]);    //创新数组,即缩容后的数组
        int p=front;    //用指针p遍历原数组
        int i=0;        //用指针i遍历新数组
        while(true){
            newData[i]=data[p];    //把原数组中的元素复制给新数组
            i++;
            p=(p+1)%data.length;
            if(p==rear){    //p指针遍历的尾指针处结束循环
                break;
            }
        }
        front=0;    //把头指针指向扩容后数组表头位置
        rear=size;  //尾指针指向有效元素长度角标
        data=newData;   //把新数组给元素组,偷梁换柱
    }

    @Override
    public E getFront() {
        if(isEmpty()){
            throw new IllegalArgumentException("队列为空");
        }
        return data[front];    //获取队头,队头就是头指针处
    }

    @Override
    public E getRear() {
        if(isEmpty()){
            throw new IllegalArgumentException("队列为空");
        }
        return data[(rear-1+data.length)%data.length];  //获取队尾,队尾是尾指针的前一个,尾指针始终指向空
    }

    //清空队列元素,把构造函数里的代码拿过来(初始化状态)
    @Override
    public void clear() {
        data= (E[]) (new Object[11]);//因为有一个空的空间
        front=0;
        rear=0;
        size=0;
    }

    @Override
    public String toString() {
        StringBuilder sb=new StringBuilder();   //创建StringBuilder对象,可以直接在原字符串上添加字符,不许要每次改变都创新字符串
        /*
        输出这样的格式
            ArrayQueueLoop: 15/20
            [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
         */
        sb.append(String.format("ArrayQueueLoop: %d/%d\n",size,data.length-1));
        sb.append('[');
        if(isEmpty()){
            sb.append(']');
        }else{
            for(int i=front;i!=rear;i=(i+1)%data.length){   //打印元素,从头指针处开始,到达指针结束
                sb.append(data[i]); 
                if((i+1)%data.length==rear){    //当遍历到指针i下一个位置是尾指针处时,此时i在最后一个元素处,打印']'
                    sb.append(']');
                }else{                      //否则在元素间打印','
                    sb.append(',');
                }
            }
        }
        return sb.toString();   //返回sb对象,调用toString方法
    }

    @Override
    public Iterator<E> iterator() {
        return new ArrayQueueLoopIterator();    //创建内部类对象
    }
    //迭代器 内部类(就是打印表内元素,并让其支持foreach循环)
    private class ArrayQueueLoopIterator implements Iterator{
        int p=front;      //从头指针处开始
        @Override
        public boolean hasNext() { 
            return p!=rear;     //继续条件:p指针不指向尾指针处
        }

        @Override
        public Object next() {  
            E ret=data[p];      //ret输出元素
            p=(p+1)%data.length;    //p递增
            return ret;
        }
    }
}

テストクラスを書く、終了時に各メソッドはエラーをテストすることができ、すべての完成BUG後に見つけるのは難しいことは避けてください。

公開された70元の記事 ウォン称賛56 ビュー1984

おすすめ

転載: blog.csdn.net/qq_43624033/article/details/103572549