JAVAのデータ構造とアルゴリズム(2)キュー

先週、JAVAデータ構造とアルゴリズムのキューについて学び、何かを書き、後で確認するために記録しました。主に次の4つの側面からです。(1)キューアプリケーションのシナリオ(2)キューの詳細な紹介(3)アレイシミュレーションキューのアイデア分析とコードの実装(4)循環キューとコード実装のアイデアの分析(5)要約。

(1)キューの適用シナリオ

       現実的なシナリオ:キューは日常生活で非常に一般的です。たとえば、銀行はビジネスのために並んでおり、食堂は食事のために並んでいます。銀行はその番号に電話をかけるために列を作っており、4人の店員が列に並んでいる人にサービスを提供します。各店員がサービスを完了すると、次にサービスを受ける人が下の列から生成されます。待ち行列にいる人は先着順で、必要なサービスを提供するために一度ウィンドウから呼び出されるのを待ちます。そうすると、待ち行列にいる人は待ち行列に相当します。

           

たとえば、映画館に並んでチケットを購入する場合、最初に列に入るのは最初にチケットを購入して列を離れ、最後に列に入るのは最後の人です。チケットを購入します。

たとえば、コンピュータのオペレーティングシステムには、印刷キューでの印刷を待機しているプリンタなど、静かに動作するさまざまなキューがあります。

それで、キューはいつですか?Javaを使用してキューを実装する方法は?

(2)キューの詳細な紹介

      キューは、配列またはリンクリストで実装できる順序付き線形リストであり、先入れ先出しの原則に従います。つまり、キューに格納されているデータを最初に取り出す必要があり、後で保存されたデータは後で取り出す必要があります。スタックとの違いは、キューが両端で使用できる構造である、一方の端が新しい要素を追加するために使用され、もう一方の端が要素を削除する、または一方の端だけがデータを挿入または削除できることです。挿入操作を実行する端はキューの終わりと呼ばれ、削除操作を実行する端はキューの先頭と呼ばれます。キューに要素がない場合、それは空のキューと呼ばれます。

å¨è¿éæå¥å¾çæè¿°

 キューは次のように分割されます。

  1.   一方向キュー(キュー):一方の端でのみデータを挿入し、もう一方の端でデータを削除できます。
  2.   双方向キュー(Deque):両端でデータの挿入とデータの削除操作を行うことができます。

キューでは、ヘッドとテール:要素の挿入を許可する端をテールと呼び、要素の削除を許可する端をキューのヘッドと呼びます。エンキュー:キューの挿入操作、デキュー:キューの削除操作、次のように、完全なキューへのプッシュ操作を示し、それに番号1、2、3を追加します。プロセスは次のとおりです。

キューから1、2、3を取り出します。ポップの操作は次のとおりです。

 

(3)アレイシミュレーションキューの思考分析とコード分析

         キュー自体は順序付きリストなので、配列を使用してキューを追加、削除、変更、およびチェックするにはどうすればよいですか?

        配列の構造を使用してキューのデータを格納する場合、キュー配列の宣言は次の図のようになります。ここで、maxSizeはキューの最大容量です。キューの出力と入力はフロントエンドとバックエンドで別々に処理されるため、キューのフロントエンドとバックエンドの添え字をそれぞれ記録するには、フロントエンドとリアの2つの変数が必要です。フロントはデータ出力に応じて変化します。写真が示すように、背面はデータ入力に応じて変化します。

å¨è¿éæå¥å¾çæè¿°

データをキューに保存するとき、それは「addQueue」と呼ばれます。addQueueの処理には2つのステップが必要です。

思考の分析により、テールポインタが後方に移動します。rear+ 1、front == Rear [empty]の場合、テールポインタrearがキューの最大添え字maxSize-1より小さい場合、データは指定された配列要素に格納されます。そうしないと、データを保存できません。リア== maxSize-1 [キューがいっぱい];すなわち:

  • frontは、キューのfront要素の前の位置です[frontは含まれません]
  • 後部はキューの最後の要素です[最後を含む]
  • キューを挿入し、キューがいっぱいかどうかを判断し、テールポインタを後方に移動します:rear ++、次にarr [rear]を挿入します
  • キューから移動し、キューが空かどうかを判断し、フロントポインタを後ろに移動します:front ++、次にarr [front]から移動します。
  • キューが空の判定条件:フロント==リアの場合【空】
  • キューがいっぱいかどうかを判断するための条件:rear == maxSize-1 [queue full]

キューの実装ロジックを理解すると、配列を使用した実装コードは次のようになります。

package JAVADATASTRTUCRE;

import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 * User:  yongping Li
 * Date: 2020/11/16
 * Time: 17:06
 * Description: No Description
 */

/*
问题:队列不能重复使用
 */
public class arrayQueueDemo {
    public static void main(String[] args) {
        ArrayQueue arrayQueue = new ArrayQueue(3);
        char key;//接受用户输入
        Scanner scanner =new Scanner(System.in);
        boolean loop=true;
        while(loop){
            System.out.println("s(show):显示队列");
            System.out.println("e(exit):退出程序");
            System.out.println("a(add):添加数据到队列");
            System.out.println("g(get):从队列中取数据");
            System.out.println("h(head):查看队列头部数据");
            key=scanner.next().charAt(0);
            switch (key){
                case 's':
                    arrayQueue.showQueue();
                    break;
                case 'a':
                    System.out.println("请输入一个数");
                    int value=scanner.nextInt();
                    arrayQueue.addQueue(value);
                    break;
                case 'g':
                    try{
                        int res =arrayQueue.getQueue();
                        System.out.println("去除的数据为"+res);
                    }catch(Exception E){
                        System.out.println(E.getMessage());
                    }
                    break;
                case 'h':
                    try{
                        int head =arrayQueue.headQueue();
                        System.out.println("头部的数据为"+head);
                    }catch(Exception E){
                        System.out.println(E.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop=false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出!!!!");
    }
}

class ArrayQueue{

    private int maxSiaze;//队列的最大容量
    private int front;//队列头部
    private int rear;//队列尾部
    private int []arr;//用于1存放数据,模拟队列

    //创建队列的构造器
    public ArrayQueue(int arrMaxSize){
          maxSiaze=arrMaxSize;
          arr=new int[maxSiaze];
          front=-1;//指向队列头部的前一个位置,
          rear=-1;//指向队列尾部的数据(即队列的最后一个数据)
    }

//判断队列是否满
    public  boolean isFull(){
        return rear==maxSiaze-1;
    }
//判断队列是否为空
    public boolean isEmpty(){
     return rear==front;
    }
//添加数据到队列
public void addQueue(int n){
    //判断队列是否满
    if(isFull()){
        System.out.println("队列已满,");
        return;
    }
      rear++;
      arr[rear]=n;

}
//出队列
public  int getQueue(){
        //判断队列是否为空

    if(isEmpty()){
        //抛出异常
        throw new RuntimeException("队列空,不能取数据");
    }
    front++;
    return arr[front];
}
//显示队列所有数据
public void showQueue(){
 if(isEmpty()){
     System.out.println("队列为空,没有数据");
     return;
 }
   for(int i=0;i<arr.length;i++){
       System.out.printf("arr[%d]=%d\n",i,arr[i]);
   }
}
//显示队列的头数据,并非取数据
public int headQueue(){
        if(isEmpty()){
            //System.out.println("队列为空");
            throw new RuntimeException("队列为空");
        }
        return arr[front+1];
    }
}

操作の結果は次のとおりです。

 s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
s
队列为空,没有数据
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
a
请输入一个数
12
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
a
请输入一个数
23
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
a
请输入一个数
32
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
s
arr[0]=12
arr[1]=23
arr[2]=32
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
g
去除的数据为12
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
h
头部的数据为23
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据

実現アイデア2:

package DataStrtureDemo;

import java.util.LinkedList;

/**
 * Created with IntelliJ IDEA.
 * User:  yongping Li
 * Date: 2020/11/20
 * Time: 8:57
 * Description: No Description
 */
public class arrayQueueDemo {
    public static void main(String[] args) {
        ArrayQueue queue = new ArrayQueue(5);
        queue.printAll();
        System.out.println(queue.isFull());
        //queue.enQueue(6);
        System.out.println(queue.isEmpty());



    }
}

class ArrayQueue{
    private int first,last,size;
    private Object[]  storage;

    public ArrayQueue(){
        this(100);
    }
    public ArrayQueue(int n){
        size=n;
        storage=new Object[size];
        first=last=-1;
    }
    public boolean isFull(){
        return first==0 && last==size-1||first==last+1;
    }
    public boolean isEmpty(){
        return first==-1;
    }
    //队列尾部添加元素
    public void enQueue(Object e){
        if(last==size-1||last==-1){
            storage[0]=e;
            last=0;
            if(first==-1){
                first=0;
            }
        } else{
            storage[++last]=e;
        }
    }
    //从队列中取出元素
    public Object  dequeue(){
        Object tmp=storage[first];
        if(first==last){
            last=first=-1;
        }else if(first==size-1){
            first=0;
        }else {
            first++;
        }
        return tmp;
    }
    public void printAll(){
        for(int i=0;i<size;i++){
            System.out.println(storage[i]+"   ");
        }
    }



}

class  Queue{
    private LinkedList list=new  LinkedList();

    //构造器
    public Queue(){

    }
    public boolean isEmpty(){
        return list.isEmpty();
    }
    //第一个元素
    public Object firstEl(){
        return list.getFirst();
    }
//获取最后一个元素
    public Object  lastEl(){
        return list.getLast();
    }
    public Object dequeue(){
        return list.removeFirst();
    }
    public void enqueue(Object e){
        list.addLast(e);
    }
    public String toString(){
        return list.toString();
    }
}

操作の結果は次のとおりです。

null   
null   
null   
null   
null   
false
true

Process finished with exit code 0

 

(4)循環キューの思考分析とコード実装

循環キューを使用する理由:

(1)現在、キューは1回使用でき、再利用はできません。

(2)循環キューを設計し、モジュラス%を取得します

 以下に示すように、チームヘッドポインタがより高い添え字位置を指す場合:

  

  新しいデータがキューに追加されると、キューの最後にある後部ポインタが上方向、つまり下方向に移動します。データ項目が削除されると、行頭のフロントポインタが上に移動します。そのため、映画のチケットを買うために列を作るなど、このデザインは現実に反しているようで、チームの長はチケットを購入した後に去り、チーム全体が前進します。コンピュータのキュー内の番号を削除することも可能で、キュー全体が前方に移動しますが、これは非常に非効率的です。私たちが選択する方法は、チームの先頭と末尾にあるポインターを移動することです。

 このようにポインタを動かすと、まもなくチームのテールポインタがデータの最後に移動すると思いますが、この時点でデータが削除されている可能性があります。チームに入ると、新しいデータ項目が表示されます。もう一度上に移動すると、どうすればよいですか。以下に示すように:

  

  キューが不満であるが新しいデータを挿入できないことを回避するために、「循環キュー」とも呼ばれる配列の先頭にテールポインタをラップアラウンドさせることができます。

  

それは以下のとおりです。

循環キューの実装に関する考慮事項:

1.フロント変数の意味を調整します。フロントはキューの最初の要素を指します。つまり、arr [front]はキューの最初の要素です。

2.後部変数の意味を調整します。後部はキューの最後の要素の後の要素を指します。これは、慣例としてスペースを解放することを望んでいるためです(キューの実際の容量= maxSize-1、これは次のように理解されます)エラーが配列範囲を超える場所を指すのを防ぎます)

3.キューがいっぱいになると、条件は次のようになります。(rear + 1)%maxSize == front [full] 

4.キューが空の場合、条件は次のとおりです。リア==フロント[空]

5.キュー内の有効なデータの数:(リア+ maxSize-フロント)%maxSize

6.キューに挿入するときは、キューがいっぱいであると判断し、最初にキューに挿入しますarr [rear]、次にリア++

7.キューから移動するときは、チームが空であると判断し、最初にキューから移動しますarr [front]、次にfront ++

要約:循環キューの設計アイデアは次のとおりです:

循環キューのコードは次のように実装されます。

package JAVADATASTRTUCRE;

import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 * User:  yongping Li
 * Date: 2020/11/16
 * Time: 21:24
 * Description: No Description
 */
public class circleArrayQueueDemo {
    public static void main(String[] args) {
        System.out.println("测试数组环形队列");
        circleArrayQueue arrayQueue = new circleArrayQueue(3);
        char key;//接受用户输入
        Scanner scanner =new Scanner(System.in);
        boolean loop=true;
        while(loop){
            System.out.println("s(show):显示队列");
            System.out.println("e(exit):退出程序");
            System.out.println("a(add):添加数据到队列");
            System.out.println("g(get):从队列中取数据");
            System.out.println("h(head):查看队列头部数据");
            key=scanner.next().charAt(0);
            switch (key){
                case 's':
                    arrayQueue.showQueue();
                    break;
                case 'a':
                    System.out.println("请输入一个数");
                    int value=scanner.nextInt();
                    arrayQueue.addQueue(value);
                    break;
                case 'g':
                    try{
                        int res =arrayQueue.getQueue();
                        System.out.println("去除的数据为"+res);
                    }catch(Exception E){
                        System.out.println(E.getMessage());
                    }
                    break;
                case 'h':
                    try{
                        int head =arrayQueue.headQueue();
                        System.out.println("头部的数据为"+head);
                    }catch(Exception E){
                        System.out.println(E.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop=false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出!!!!");
    }
}

class circleArrayQueue{

    private int maxSiaze;//队列的最大容量
    private int front;//队列头部,指向队列第一个元素,初始值为0
    private int rear;//队列尾部,指向对列最后一个元素的最后一个位置,初始值为0
    private int []arr;//用于1存放数据,模拟队列

    //创建队列的构造器
    public circleArrayQueue(int arrMaxSize){
        maxSiaze=arrMaxSize;
        arr=new int[maxSiaze];

    }

    //判断队列是否满
    public  boolean isFull(){
        return (rear+1)%maxSiaze==front;
    }
    //判断队列是否为空
    public boolean isEmpty(){
        return rear==front;
    }
    //添加数据到队列
    public void addQueue(int n){
        //判断队列是否满
        if(isFull()){
            System.out.println("队列已满,");
            return;
        }
        arr[rear]=n;
        //将rear后移,取模
        rear=(rear+1) % maxSiaze;

    }
    //出队列,取数据
    public  int getQueue(){
        //判断队列是否为空
        if(isEmpty()){
            //抛出异常
            throw new RuntimeException("队列空,不能取数据");
        }
        //分析front指向队列第一个元素
        //1,front保存到一个临时变量 2.临时保存的变量返回
        //3,front后移,考虑取模
        int value=arr[front];
        front=(front+1)%maxSiaze;
        return value;
    }
    //显示队列所有数据
    public void showQueue(){
        if(isEmpty()){
            System.out.println("队列为空,没有数据");
            return;
        }
        //从front开始便利,遍历多少个元素
        for(int i=0;i<front+getQueueSize();i++){
            System.out.printf("arr[%d]=%d\n",i%maxSiaze,arr[i%maxSiaze]);
        }
    }
    //显示队列的头数据,并非取数据
    public int headQueue(){
        if(isEmpty()){
            //System.out.println("队列为空");
            throw new RuntimeException("队列为空");
        }
        return arr[front];
    }

    //返回当前队列有效数据的个数
    public int getQueueSize(){
        return (rear+maxSiaze-front)%maxSiaze;
    }

}

操作の結果は次のとおりです。

测试数组环形队列
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
s
队列为空,没有数据
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
a
请输入一个数
12
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
a
请输入一个数
34
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
a
请输入一个数
234
队列已满,
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
s
arr[0]=12
arr[1]=34
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
h
头部的数据为12
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
g
去除的数据为12
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
s
arr[0]=12
arr[1]=34
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
g
去除的数据为34
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
g
队列空,不能取数据
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
a
请输入一个数
1
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据
s
arr[0]=12
arr[1]=34
arr[2]=1
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头部数据

(5)まとめ

キューは、先入れ先出しの原則に従ってデータを格納する線形テーブルです。これは通常、主にデータを格納するためではなく、特定のプログラム操作のデータ構造を単純化するために使用されます。キューの場合、追加、削除、変更、およびチェックは、配列を介して実装できます。循環リストを設計する目的は、配列を繰り返し使用することです。

 

 

 

 

 

 

 

 

 

おすすめ

転載: blog.csdn.net/weixin_41792162/article/details/109906039