标签:generic 决定 removing hat rod testing 方案 line index
Write a generic data type for a deque and a randomized queue. The goal of this assignment is to implement elementary data structures using arrays and linked lists, and to introduce you to generics and iterators.
Algorithms 第二周的编程任务,主要目的是编写一个双向链表,随机队列和一个测试客户端。
Dequeue. A double-ended queue or deque (pronounced “deck”) is a generalization of a stack and a queue that supports adding and removing items from either the front or the back of the data structure. Create a generic data type Deque
that implements the following API:
public class Deque<Item> implements Iterable<Item> { public Deque() // construct an empty deque public boolean isEmpty() // is the deque empty? public int size() // return the number of items on the deque public void addFirst(Item item) // add the item to the front public void addLast(Item item) // add the item to the end public Item removeFirst() // remove and return the item from the front public Item removeLast() // remove and return the item from the end public Iterator<Item> iterator() // return an iterator over items in order from front to end public static void main(String[] args) // unit testing (optional) }
Randomized queue. 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. Create a generic data type RandomizedQueue
that implements the following API:
public class RandomizedQueue<Item> implements Iterable<Item> { public RandomizedQueue() // construct an empty randomized queue public boolean isEmpty() // is the randomized queue empty? public int size() // return the number of items on the randomized queue public void enqueue(Item item) // add the item public Item dequeue() // remove and return a random item public Item sample() // return a random item (but do not remove it) public Iterator<Item> iterator() // return an independent iterator over items in random order public static void main(String[] args) // unit testing (optional) }
Deque
类需要同时维护指向头部和尾部的两个节点,并同为在单向链表的基础上,在内部类Node上添加previous属性用于指向上一个节点。import java.util.Iterator;
public class Deque<Item> implements Iterable<Item> {
private Node first = null;
private Node last = null;
private int capacity = 0;
private class Node {
Item item;
Node next;
Node previous;
}
/**
* Construct an empty deque
*/
public Deque() {}
/**
* Check whether the deque is empty.
*
* @return true when the deque is empty, false otherwise.
*/
public boolean isEmpty() {return capacity == 0;}
/**
* Return the number of items on the deque.
*
* @return the number of items on the deque.
*/
public int size() {return capacity;}
/**
* Add the item to the front
*
* @throws java.util.NoSuchElementException when {@code item} is empty.
*/
public void addFirst(Item item)
{
if (item == null) throw new IllegalArgumentException();
Node oldFirst = first;
first = new Node();
first.item = item;
if (capacity == 0)
{
first.next = null;
first.previous = null;
last = first;
}
else {
first.next = oldFirst;
oldFirst.previous = first;
}
++capacity;
}
/**
* Add the item to the end
*
* @throws java.util.NoSuchElementException when {@code item} is empty.
*/
public void addLast(Item item)
{
if (item == null) throw new IllegalArgumentException();
Node oldLast = last;
last = new Node();
last.item = item;
if (capacity == 0)
{
last.previous = null;
last.next = null;
first = last;
}
else {
oldLast.next = last;
last.previous = oldLast;
}
++capacity;
}
/**
* Remove and return the item from the front
*
* @throws java.util.NoSuchElementException when {@code Deque} is empty.
*/
public Item removeFirst()
{
if (isEmpty()) throw new java.util.NoSuchElementException();
Item item = first.item;
first = first.next;
if (capacity == 1)
{
last = first;
}
else
{
first.previous = null;
}
capacity--;
return item;
}
/**
* Remove and return the item from the end
*
* @throws java.util.NoSuchElementException when {@code Deque} is empty.
*/
public Item removeLast()
{
if (isEmpty()) throw new java.util.NoSuchElementException();
Item item = last.item;
last = last.previous;
if (capacity == 1) {
first = last;
}
else
{
last.next = null;
}
capacity--;
return item;
}
/**
* Return an iterator over items in order from front to end.
*
* @return an iterator over items in order from front to end.
*
* @throws java.util.NoSuchElementException when called {@code next()} method if
* there is no next element.
* @throws UnsupportedOperationException when called {@code remove()} method.
*/
@Override
public Iterator<Item> iterator()
{
return new ListIterator();
}
private class ListIterator implements Iterator<Item>
{
private Node current = first;
@Override
public boolean hasNext() { return current != null; }
@Override
public void remove() {
throw new java.lang.UnsupportedOperationException();
}
@Override
public Item next()
{
if (!hasNext()) throw new java.util.NoSuchElementException();
Item item = current.item;
current = current.next;
return item;
}
}
}
RandomizedQueue
类因为要返回随机元素,所以我们采用数组的方法。dequeue
方法随机选择一个元素,并将选择节点指向最后节点,再让最后节点指向null。import edu.princeton.cs.algs4.StdRandom;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class RandomizedQueue<Item> implements Iterable<Item> {
private int amount = 0;
private Item[] queue;
/**
* Construct an empty randomized queue.
*/
public RandomizedQueue() {
queue = (Item[]) new Object[2];
}
/**
* Check whether the randomized queue is empty.
*
* @return true when the randomized queue is empty, false otherwise.
*/
public boolean isEmpty() {
return amount == 0;
}
/**
* Return the number of items on the randomized queue.
*
* @return the number of items on the randomized queue.
*/
public int size() {
return amount;
}
/**
* Add the item.
*/
public void enqueue(Item item) {
if (item == null) {
throw new java.lang.IllegalArgumentException();
}
if (amount == queue.length) {
resize(queue.length * 2);
}
queue[amount++] = item;
}
/**
* Remove and return a random item.
*
* @return a random item.
*/
public Item dequeue()
{
if (isEmpty()) {
throw new NoSuchElementException();
}
int randomIndex = StdRandom.uniform(amount);
Item item = queue[randomIndex];
queue[randomIndex] = queue[amount - 1];
queue[--amount] = null;
if (amount > 0 && amount == queue.length / 4) {
resize(queue.length / 2);
}
return item;
}
/**
* Return a random item (but do not remove it).
*
* @return a random item (but do not remove it).
*/
public Item sample() {
if (isEmpty()) {
throw new NoSuchElementException();
}
int randomIndex = StdRandom.uniform(amount);
return queue[randomIndex];
}
/**
* Return an independent iterator over items in random order.
*
* @return an independent iterator over items in random order.
*
* @throws NoSuchElementException when called {@code next()} method if
* there is no next element.
* @throws UnsupportedOperationException when called {@code remove()} method.
*/
public Iterator<Item> iterator() {
return new ListIterator();
}
private void resize(int capacity) {
Item[] temp = (Item[]) new Object[capacity];
System.arraycopy(queue, 0, temp, 0, queue.length);
queue = temp;
}
private class ListIterator implements Iterator<Item> {
private int[] randomIndexes = StdRandom.permutation(amount);
private int i = 0;
@Override
public boolean hasNext() {
return i < amount;
}
@Override
public Item next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return queue[randomIndexes[i++]];
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}
Permutation
类(参考某论坛的楼主):我最开始的想法是如果知道了k和N,那么读每个数的时候就知道他进RandomizedQueue
的概率了;
但问题是input stream读完之前,我不可能知道N
是多少;而input stream读完之后,每个数都按自己应有的概率enqueue了;
所以解决问题的方法就是:N
是动态的;每读一个数,N++
,然后对之前的概率分配进行调整;
以1,2,3,4,5
; k = 3
, N=5
为例:
在queue
长度达到k
之前,照单全收;所以queue
长度达到k
时里面的元素是:1, 2 ,3
这时候从input stream里面读到4
,一共读了4个数,N
变成了4
;
由于queue
的长度不能超过k
,超过1个也不行,所以我要先从1,2,3
里面随机洗出一张牌,再把4放进去;保证queue
的长度一直是k
;
由于dequeue()是随机的,所以(1,2,4),(1,3,4),(2,3,4)
都是等概的;
问题是没有(1,2,3)
; 所以有一定概率我不要4
从而得出(1,2,3)
。这个概率是多少呢?
也就是从N
个数里面挑k
个数,没有某一个数的概率 \(\frac{C_{N-1}^k}{C_N^k} = \frac{N-k}{N}\)
此时k = 3, N = 4
,(1,2,3)
的概率就是\(\frac{1}{4}\)了;
下一步,读到5
,N = 5
; 又要面临是否要把5
弄进去的抉择,此时的\(\frac{N-k}{N}\)= \(\frac{2}{5}\); 也就是说不要5
的概率是\(\frac{2}{5}\).
由此保证了每个(a,b,c)
的permutation一定是等概的。
这个算法的思路在于:
每次决定要不要一个新数加进来的时候,我都可以保证:如果加进来,然后我可以把包含它的所有组合洗的等概;那么我只要保证不加他进来的总概率(或加他进来的总概率)是对的即可;
这个不加进来的概率就是 \(\frac{N-k}{N}\)(加进来的概率是\(\frac{k}{N}\)),最后给出实现代码。
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
public class Permutation
{
/**
* Creates one {@code RandomizedQueue} object
* of maximum size at most k.
*
* @param args the command-line arguments
*/
public static void main(String[] args)
{
int k = Integer.parseInt(args[0]);
RandomizedQueue<String> rq = new RandomizedQueue<String>();
double n = 1.0;
while (!StdIn.isEmpty())
{
String s = StdIn.readString();
if(k == 0) {
break;
}
else if (rq.size() < k)
{
rq.enqueue(s);
}
else if (StdRandom.uniform() > ((n-k)/n)) {
rq.dequeue();
rq.enqueue(s);
}
n++;
}
for (String s : rq) StdOut.println(s);
}
}
标签:generic 决定 removing hat rod testing 方案 line index
原文地址:https://www.cnblogs.com/revc/p/9226716.html