【问题标题】:Java Array representation of queue data structure队列数据结构的Java数组表示
【发布时间】:2012-09-19 12:00:24
【问题描述】:

我试图通过用一维数组表示它来创建一个队列。我知道人们可能会建议我使用列表甚至默认的 java 队列对象会更轻松,但我是来学习的,所以我不想求助于如此简单的解决方案。到目前为止,我的实现遇到了一些问题:

  1. 如果队列已满,则无法添加对象。
  2. 移动对象。

例如队列数组 -> [ ]、[A]、[B] 移动它,使其最终如下所示:QueueArray -> [A], [B], [ ]

现在听我说,问题 1#。 我的逻辑思维告诉我以某种方式增加队列数组的大小以便能够接收更多对象,事情是我不知道如何做到这一点而不将所有对象保存到另一个临时数组然后将它们复制到一个新的更大尺寸的数组。我什至不确定这是否是一种明智可行的方法。

问题2# 我在考虑可能只是检查队列数组中的空对象,如果找到它们,只需将元素移动一个索引。

这是我的代码,我也愿意接受其他改进。

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);
    }
}

【问题讨论】:

  • 你通常会使用像 ArrayBlockingQueue 这样的带有数组的 RingBuffer。

标签: java arrays queue


【解决方案1】:

所以,你说得对,有些数据结构可以完全满足你的需求,但由于这更像是一个练习而不是应用程序,所以我想说的是。

#1

您将整个 n 长度数组复制到 n+1 长度数组的解决方案将起作用。但是还有另外两种通常使用的方法来做到这一点,这两种方法都不涉及复制整个数组。

首先是在创建队列时预先分配最大大小。如果您熟悉的话,这类似于您必须为 C 中的数组预先分配内存的方式。这是迄今为止 3 种解决方案中最简单的,也是最快的。

我会指出,如果您使用此解决方案,您会经常看到用于实现队列的所谓“循环缓冲区”或“环形缓冲区”[1]。您应该阅读有关此主题的链接文章。

第二个是使用某种链表[2],而不是只存储值,而是存储一个 (value, next-address) 对。然后,您可以动态添加项目。但是在 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

【讨论】:

    【解决方案2】:

    在内部,我相信大多数 Java 列表对象在某些时候实际上只有一个不可变数组,这就是您遇到的问题。

    他们所做的调整大小,然后是在某个完整的百分比阈值(比如 75%),他们将创建一个两倍大小的新数组。因此,如果您有一个 4 项数组,并插入了三个项,则该结构将在内部将其存储数组更新为 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
    

    使用此方法,如其他答案所述,您还可以为仍然可用的元素包装索引。在上面的示例中,如果您再添加两个元素,由于前两个不再使用,您可以开始用新元素替换它们。

    显然,当没有足够的对象出队以允许更多对象排队时,您仍然需要处理大小调整。

    【讨论】:

      猜你喜欢
      • 2012-03-20
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 2015-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多