4

一次元配列で表現してキューを作ろうとしています。リストやデフォルトのJavaキューオブジェクトを使用することで、より簡単な時間を過ごすことができると人々が提案するかもしれないことは知っていますが、私はここで学ぶために、そのような簡単な解決策に頼りたくありません。これまでのところ、実装に関していくつかの問題が発生しています。

  1. キューがいっぱいの場合、オブジェクトを追加できません。
  2. オブジェクトをシフトします。

例:QueueArray-> []、[A]、[B]次のようになるようにシフトします:QueueArray-> [A]、[B]、[]

問題番号1#について、私に聞いてください。私の論理的思考は、より多くのオブジェクトを取り込むことができるようにキュー配列のサイズをどうにかして増やすように指示しています。すべてのオブジェクトを別の一時配列に保存してから新しいものにコピーしない限り、それを行う方法がわかりません。より大きなサイズの配列。これがこれを実行するための賢明で実行可能な方法であるかどうかさえわかりません。

問題2#キュー配列内のnullオブジェクトをチェックし、見つかった場合は、要素を1つのインデックスだけシフトすることを考えていました。

これが私のコードです。他の改善も可能です。

import java.util.Scanner;

public class Queue {
    Object[] q;
    int head, tail;
    Scanner in;

    public Queue(int size){ 
        q = new Object[size];
        menu();
    }

    public void menu(){
        System.out.println("\t");   
        System.out.println("1. add(x) - Adds the object x to end of the queue");
        System.out.println("2. remove() - Removes the first object in the queue");
        System.out.println("3. view - Shows contents in queue");
        System.out.println("4. exit - Exits program");
        System.out.println("Select your operation:"+"\t");  

        while(true){
            in = new Scanner(System.in);
            int userInput = in.nextInt();

            if(userInput == 1){
                System.out.println("Give value of object");
                in = new Scanner(System.in);
                Object userIn = in.next();
                add(userIn);
                menu();
            }

            if(userInput == 2){
                remove();
                menu();
            }

            if(userInput == 3){
                peek();
                menu();
            }

            if(userInput == 4){
                System.exit(0);
            }
        }
    }

    // Adds the object to the end of the queue
    Object add(Object letter){

        // If the queue is not full, add to back of queue
        if(tail >= q.length){
            System.out.println("Queue is full");
            return null;
        } else {
            q[tail] = letter;
            tail++;
        }
        return tail;
    }

    // Removes the first object in the queue
    Object remove(){
        if(q.length == 0){
            return null;
        } else {
            q[head] = null;
            head++;
        }
        return head;
    }

    // Returns the head, tail and all other objects in the queue
    void peek(){
        System.out.println("Head: "+q[head]);
        System.out.println("Tail: "+q[tail-1]);
        System.out.println(q[0]+", "+q[1]+", "+q[2]+", "+q[3]+", "+q[4]+".");
    }

    public static void main(String[] args){
        new Queue(5);
    }
}
4

2 に答える 2

2

内部的には、ほとんどの Java リスト オブジェクトは、ある時点で実際には不変の配列だけを持っていると思います。

サイズを変更するために行うことは、完全な % しきい値 (たとえば 75%) で、2 倍のサイズで新しい配列を作成します。したがって、4 項目の配列があり、3 つの項目を挿入した場合、構造体はストレージ配列を内部的にサイズ 8 に更新し、項目が 6 のときにこれを繰り返します。

実際のコピーを行う限り、コピー元とコピー先のアレイを指定できる System.arraycopy を見てください。

シフトに関して、なぜシフトするのですか?現在のインデックスへのポインターを保持しないのはなぜですか?

たとえば、この操作チェーンを見てください...

キュー オブジェクト、キュー オブジェクト、デキュー オブジェクト、キュー オブジェクト。

あなたの構造は次のようになります

[,,,] - head at 0, tail at 0
[obj1,,,] - head at 0, tail at 1
[obj1,obj2,,] - head at 0, tail at 1
[obj1,obj2,,] - head at 1, tail at 1
[obj1,obj2,obj3,] - head at 1, tail at 2
[obj1,obj2,obj3,] - head at 2, tail at 2

この方法を使用すると、他の回答で説明されているように、まだ使用可能な要素のインデックスをラップすることもできます。上記の例で、さらに 2 つの要素を追加した場合、最初の 2 つが使用されなくなったため、それらを新しい要素に置き換えることができます。

明らかに、十分な数のオブジェクトがキューから取り出されず、さらに多くのオブジェクトをキューに入れることができる場合、サイズ変更を処理する必要があります。

于 2012-09-27T16:24:14.840 に答える
2

つまり、あなたが望むことを正確に実行するデータ構造があることは正しいですが、これはアプリケーションというよりも演習であるため、ここで私が言いたいことは次のとおりです。

#1

nの長さの配列全体をn +1 の長さの配列にコピーするソリューションは機能します。しかし、これを行うには通常使用される方法が他に 2 つありますが、どちらも配列全体をコピーする必要はありません。

1 つ目は、キューの作成時に最大サイズを事前に割り当てることです。これは、慣れている場合、C で配列にメモリを事前に割り当てる必要がある方法に似ています。これは、3 つのソリューションの中で最も簡単で、最速です。

このソリューションを使用すると、キューの実装に使用される「循環バッファー」または「リング バッファー」[1] と呼ばれるものがよく見られることを指摘しておきます。このトピックに関するリンクされた記事を読む必要があります。

2 つ目は、ある種の連結リスト [2] を使用することです。ここでは、値だけを格納する代わりに、(値、次のアドレス) のペアを格納します。その後、項目を動的に追加できます。しかし、これを Java で行うのはかなり困難です。リンクされた記事は、リンクされたリストに関するより多くの洞察を提供します。

#2

シフトしないでください。 キューのサイズと要素のサイズによっては、さらに時間がかかる場合があります -- シフトはO (n) になります -- これが何を意味するかわからない場合は、[3] を参照してください。

シフトする代わりに、循環バッファー [1] のようなものを使用します。頭と尾 (または頭とサイズ) を追跡します。

リングバッファ挿入を使用してこのようなことを行うと、O (1) になります。はるかに優れています。

さておき

私の意見では、データ構造について学ぶことは非常に重要です。しかし (これも私の意見ですが)、Java のような言語は、データ構造の基本を理解することを、たとえば C や C++ などの言語よりもはるかに困難にします。不可能ではありませんが、Java のマネージ メモリの側面により、C では実現できないデータ構造の実装の細かい点が難読化されます。これらのデータ構造を本当に理解したい場合は、何が必要で、どのように機能するかを確認するために、ある時点で C/C++ で実装することを強くお勧めします。

[1] http://en.wikipedia.org/wiki/Circular_buffer
[2] http://en.wikipedia.org/wiki/Linked_list
[3] http://en.wikipedia.org/wiki/Big_O_notation

于 2012-09-27T16:27:40.600 に答える