Java 数据结构-随机化队列

Java 数据结构-随机化队列,java,random,data-structures,queue,Java,Random,Data Structures,Queue,我目前正在从事普林斯顿算法第一部分的工作。其中一项任务是实现随机队列。这是一个关于使用不同数据结构的实现和权衡的问题 问题: 随机化队列类似于堆栈或队列,不同之处在于移除的项是从数据结构中的项中统一随机选择的。创建实现以下API的通用数据类型RandomizedQueue: public class RandomizedQueue<Item> implements Iterable<Item> { public RandomizedQueue() // const

我目前正在从事普林斯顿算法第一部分的工作。其中一项任务是实现随机队列。这是一个关于使用不同数据结构的实现和权衡的问题

问题:

随机化队列类似于堆栈或队列,不同之处在于移除的项是从数据结构中的项中统一随机选择的。创建实现以下API的通用数据类型RandomizedQueue:

public class RandomizedQueue<Item> implements Iterable<Item> {
    public RandomizedQueue() // construct an empty randomized queue
    public boolean isEmpty() // is the queue empty?
    public int size() // return the number of items on the queue
    public void enqueue(Item item) // add the item
    public Item dequeue() // remove and return a random item
    public Item sample() // return (but do not remove) a random item
    public Iterator<Item> iterator() // return an independent iterator over items in random order
    public static void main(String[] args)   // unit testing
}
查询1.2:为了让迭代器满足随机返回元素的要求,我创建了一个包含队列所有索引的新数组,然后使用Knuth shuffle操作对数组进行洗牌,并返回队列中特定索引处的元素。但是,这需要创建一个等于队列长度的新数组。同样,我确信我错过了一个更有效的方法

2。内部类实现

第二个实现涉及一个内部节点类

public class RandomizedQueue<Item> {
    private static class Node<Item> {
        Item item;
        Node<Item> next;
        Node<Item> previous;
    }
}
公共类随机化队列{
私有静态类节点{
项目;
节点下一步;
节点前向;
}
}
问题2.1。在本例中,我了解如何高效地执行出列操作:返回一个随机节点并更改相邻节点的引用

然而,我对如何返回一个迭代器感到困惑,该迭代器以随机顺序返回节点,而不必创建一个以随机顺序连接节点的全新队列

此外,除了可读性和易于实现之外,在阵列上使用这种数据结构还有什么好处


这篇文章有点长。我感谢你们花时间阅读我的问题并帮助我解决问题。谢谢

在数组实现中,查询1.1似乎是最好的方法。移除随机元素的唯一其他方法是将所有元素向上移动以填充其位置。因此,如果您有
[1,2,3,4,5]
并且删除了
2
,那么您的代码将向上移动项目3、4和5,并减少计数。这将采取,平均n/2的项目移动,每次删除。所以移除是O(n)。糟糕

如果在迭代时不添加和删除项目,那么只需在现有数组上使用Fisher-Yates shuffle,并开始从前到后返回项目。没有理由复制。这取决于你的使用模式。如果您设想在迭代时从队列中添加和删除项目,那么如果您不创建副本,事情就会变得不稳定

使用链表方法,很难有效地实现随机出列操作,因为为了获得随机项,必须从前面遍历列表。因此,如果您在队列中有100个项目,并且您想要删除第85个项目,那么您必须从前面开始,在到达要删除的项目之前,遵循85个链接。由于您使用的是一个双链接列表,当要删除的项目超过中间点时,您可以通过从末尾倒数来将时间减少一半,但当队列中的项目数量很大时,这仍然是非常低效的。想象一下,从一百万个项目的队列中删除第500000个项目


对于随机迭代器,可以在开始迭代之前将链表洗牌。这需要O(n logn)时间,但只需要O(1)额外的空间。同样,在添加或删除的同时,也存在迭代的问题。如果您想要这种能力,那么您需要创建一个副本。

您不需要在创建迭代器时洗牌整个数组副本,而是在使用
next()
方法访问每个元素时懒洋洋地对其进行洗牌。

使用数组实现(必须是动态的/可调整大小的),以实现常量(摊销)除构建迭代器之外的所有操作的最坏情况运行时(由于无序排列,这需要线性时间)

以下是我的实现:

import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Random;

/* http://coursera.cs.princeton.edu/algs4/assignments/queues.html
 * 
 * A randomized queue is similar to a stack or queue, except that the item
 * removed is chosen uniformly at random from items in the data structure. 
 */
public class RandomizedQueue<T> implements Iterable<T> {

    private int queueEnd = 0;   /* index of the end in the queue,
                                   also the number of elements in the queue. */

    @SuppressWarnings("unchecked")
    private T[] queue = (T[]) new Object[1];    // array representing the queue

    private Random rGen = new Random();     // used for generating uniformly random numbers

    /**
     * Changes the queue size to the specified size.
     * @param newSize the new queue size.
     */
    private void resize(int newSize) {
        System.out.println("Resizing from " + queue.length + " to " + newSize);
        T[] newArray = Arrays.copyOfRange(queue, 0, newSize);
        queue = newArray;
    }


    public boolean isEmpty() {
        return queueEnd == 0;
    }

    public int size() {
        return queueEnd;
    }

    /**
     * Adds an element to the queue.
     * @param elem the new queue entry.
     */
    public void enqueue(T elem) {
        if (elem == null)
            throw new NullPointerException();

        if (queueEnd == queue.length) 
            resize(queue.length*2);

        queue[queueEnd++] = elem;
    }

    /**
     * Works in constant (amortized) time.
     * @return uniformly random entry from the queue.
     */
    public T dequeue() {    
        if (queueEnd == 0)  // can't remove element from empty queue
            throw new UnsupportedOperationException();

        if (queueEnd <= queue.length/4)     // adjusts the array size if less than a quarter of it is used
            resize(queue.length/2);

        int index = rGen.nextInt(queueEnd);     // selects a random index

        T returnValue = queue[index];   /* saves the element behind the randomly selected index 
                                            which will be returned later */

        queue[index] = queue[--queueEnd];   /* fills the hole (randomly selected index is being deleted) 
                                               with the last element in the queue */
        queue[queueEnd] = null;         // avoids loitering

        return returnValue;
    }

    /**
     * Returns the value of a random element in the queue, doesn't modify the queue.
     * @return random entry of the queue.
     */
    public T sample() {
        int index = rGen.nextInt(queueEnd);     // selects a random index
        return queue[index];
    }

    /*
     * Every iteration will (should) return entries in a different order.
     */
    private class RanQueueIterator implements Iterator<T> {

        private T[] shuffledArray;

        private int current = 0;

        public RanQueueIterator() {
            shuffledArray = queue.clone();
            shuffle(shuffledArray);
        }

        @Override
        public boolean hasNext() {
            return current < queue.length;
        }

        @Override
        public T next() {
            if (!hasNext())
                throw new NoSuchElementException();

            return shuffledArray[current++];
        }


        /**
         * Rearranges an array of objects in uniformly random order
         * (under the assumption that {@code Math.random()} generates independent
         * and uniformly distributed numbers between 0 and 1).
         * @param array the array to be shuffled
         */
        public void shuffle(T[] array) {
            int n = array.length;
            for (int i = 0; i < n; i++) {
                // choose index uniformly in [i, n-1]
                int r = i + (int) (Math.random() * (n - i));
                T swap = array[r];
                array[r] = array[i];
                array[i] = swap;
            }
        }

    }

    @Override
    public Iterator<T> iterator() {
        return new RanQueueIterator();
    }

    public static void main(String[] args) {
        RandomizedQueue<Integer> test = new RandomizedQueue<>();

        // adding 10 elements
        for (int i = 0; i < 10; i++) {
            test.enqueue(i);
            System.out.println("Added element: " + i);
            System.out.println("Current number of elements in queue: " + test.size() + "\n");

        }


        System.out.print("\nIterator test:\n[");
        for (Integer elem: test)
            System.out.print(elem + " ");
        System.out.println("]\n");

        // removing 10 elements
        for (int i = 0; i < 10; i++) {
            System.out.println("Removed element: " + test.dequeue());
            System.out.println("Current number of elements in queue: " + test.size() + "\n");
        }       

    }   
}
导入java.util.array;
导入java.util.Iterator;
导入java.util.NoSuchElementException;
导入java.util.Random;
/* http://coursera.cs.princeton.edu/algs4/assignments/queues.html
* 
*随机化队列类似于堆栈或队列,不同之处在于
*删除是从数据结构中的项中随机统一选择的。
*/
公共类RandomizedQueue实现了Iterable{
private int queueEnd=0;/*队列中结束的索引,
还包括队列中的元素数*/
@抑制警告(“未选中”)
私有T[]队列=(T[])新对象[1];//表示队列的数组
private Random rGen=new Random();//用于生成一致随机数
/**
*将队列大小更改为指定的大小。
*@param newSize新队列大小。
*/
私有void resize(int newSize){
System.out.println(“从“+queue.length+”调整到“+newSize”);
T[]newArray=Arrays.copyOfRange(队列,0,新闻大小);
队列=新数组;
}
公共布尔值为空(){
返回queueEnd==0;
}
公共整数大小(){
返回队列结束;
}
/**
*将元素添加到队列中。
*@param elem创建新的队列条目。
*/
公共无效排队(T元素){
if(elem==null)
抛出新的NullPointerException();
if(queueEnd==queue.length)
调整大小(队列长度*2);
队列[queueEnd++]=elem;
}
/**
*以固定(摊销)时间工作。
*@return从队列中统一随机输入。
*/
公共T出列(){
if(queueEnd==0)//无法从空队列中删除元素
抛出新的UnsupportedOperationException();

if(queueEnd对于查询1.1:这里确实可以在固定时间内删除随机元素。 其思路如下:

  • 选择要返回的随机元素
  • 西南
    import java.util.Arrays;
    import java.util.Iterator;
    import java.util.NoSuchElementException;
    import java.util.Random;
    
    /* http://coursera.cs.princeton.edu/algs4/assignments/queues.html
     * 
     * A randomized queue is similar to a stack or queue, except that the item
     * removed is chosen uniformly at random from items in the data structure. 
     */
    public class RandomizedQueue<T> implements Iterable<T> {
    
        private int queueEnd = 0;   /* index of the end in the queue,
                                       also the number of elements in the queue. */
    
        @SuppressWarnings("unchecked")
        private T[] queue = (T[]) new Object[1];    // array representing the queue
    
        private Random rGen = new Random();     // used for generating uniformly random numbers
    
        /**
         * Changes the queue size to the specified size.
         * @param newSize the new queue size.
         */
        private void resize(int newSize) {
            System.out.println("Resizing from " + queue.length + " to " + newSize);
            T[] newArray = Arrays.copyOfRange(queue, 0, newSize);
            queue = newArray;
        }
    
    
        public boolean isEmpty() {
            return queueEnd == 0;
        }
    
        public int size() {
            return queueEnd;
        }
    
        /**
         * Adds an element to the queue.
         * @param elem the new queue entry.
         */
        public void enqueue(T elem) {
            if (elem == null)
                throw new NullPointerException();
    
            if (queueEnd == queue.length) 
                resize(queue.length*2);
    
            queue[queueEnd++] = elem;
        }
    
        /**
         * Works in constant (amortized) time.
         * @return uniformly random entry from the queue.
         */
        public T dequeue() {    
            if (queueEnd == 0)  // can't remove element from empty queue
                throw new UnsupportedOperationException();
    
            if (queueEnd <= queue.length/4)     // adjusts the array size if less than a quarter of it is used
                resize(queue.length/2);
    
            int index = rGen.nextInt(queueEnd);     // selects a random index
    
            T returnValue = queue[index];   /* saves the element behind the randomly selected index 
                                                which will be returned later */
    
            queue[index] = queue[--queueEnd];   /* fills the hole (randomly selected index is being deleted) 
                                                   with the last element in the queue */
            queue[queueEnd] = null;         // avoids loitering
    
            return returnValue;
        }
    
        /**
         * Returns the value of a random element in the queue, doesn't modify the queue.
         * @return random entry of the queue.
         */
        public T sample() {
            int index = rGen.nextInt(queueEnd);     // selects a random index
            return queue[index];
        }
    
        /*
         * Every iteration will (should) return entries in a different order.
         */
        private class RanQueueIterator implements Iterator<T> {
    
            private T[] shuffledArray;
    
            private int current = 0;
    
            public RanQueueIterator() {
                shuffledArray = queue.clone();
                shuffle(shuffledArray);
            }
    
            @Override
            public boolean hasNext() {
                return current < queue.length;
            }
    
            @Override
            public T next() {
                if (!hasNext())
                    throw new NoSuchElementException();
    
                return shuffledArray[current++];
            }
    
    
            /**
             * Rearranges an array of objects in uniformly random order
             * (under the assumption that {@code Math.random()} generates independent
             * and uniformly distributed numbers between 0 and 1).
             * @param array the array to be shuffled
             */
            public void shuffle(T[] array) {
                int n = array.length;
                for (int i = 0; i < n; i++) {
                    // choose index uniformly in [i, n-1]
                    int r = i + (int) (Math.random() * (n - i));
                    T swap = array[r];
                    array[r] = array[i];
                    array[i] = swap;
                }
            }
    
        }
    
        @Override
        public Iterator<T> iterator() {
            return new RanQueueIterator();
        }
    
        public static void main(String[] args) {
            RandomizedQueue<Integer> test = new RandomizedQueue<>();
    
            // adding 10 elements
            for (int i = 0; i < 10; i++) {
                test.enqueue(i);
                System.out.println("Added element: " + i);
                System.out.println("Current number of elements in queue: " + test.size() + "\n");
    
            }
    
    
            System.out.print("\nIterator test:\n[");
            for (Integer elem: test)
                System.out.print(elem + " ");
            System.out.println("]\n");
    
            // removing 10 elements
            for (int i = 0; i < 10; i++) {
                System.out.println("Removed element: " + test.dequeue());
                System.out.println("Current number of elements in queue: " + test.size() + "\n");
            }       
    
        }   
    }