Performance requirements. Your deque implementation must support each deque operation (including construction) in constant worst-case time. A deque containing n items must use at most 48n + 192 bytes of memory. Additionally, your iterator implementation must support each operation (including construction) in constant worst-case time.
/** * * Performance requirements. * * Your deque implementation must support each deque operation * (including construction) in constant worst-case time. * * < This means that only can choose linked-list implementation * < Since if use resizing-array implementation, sometimes we need to resize * < the array, some ops like add and remove may not cost constant worst-case time. * * A deque containing n items must use at most 48n + 192 bytes of memory. * Additionally, your iterator implementation must support each operation * (including construction) in constant worst-case time. * * < 48n + 192 bytes and hasNext(), remove(), next() all cost constant worst-case time. * < use 48n + 16 + 16 + 4 + 24 + 8 + 4(pad) = 48n + 72 bytes */
publicclassDeque<Item> implementsIterable<Item> { // 16(overhead) bytes private Node first, last; // 8(ref) + 8(ref) = 16 bytes privateint n; // 4(int) bytes privateclassNode { // 16(overhead) + 8(inner class) = 24 bytes Item item; // 8(ref) bytes Node prev; // 8(ref) bytes Node next; // 8(ref) bytes // 24 + 8 + 8 + 8 = 48 bytes -> 48n bytes for n items } // construct an empty deque publicDeque() { first = null; last = null; n = 0; } // is the deque empty? publicbooleanisEmpty() { return n == 0; } // return the number of items on the deque publicintsize() { return n; } // add the item to the front publicvoidaddFirst(Item item) { if (item == null) thrownewIllegalArgumentException(); NodeoldFirst= first; first = newNode(); first.item = item; first.next = oldFirst; first.prev = null; if (!isEmpty()) { oldFirst.prev = first; } else { last = first; } n++; } // add the item to the back publicvoidaddLast(Item item) { if (item == null) thrownewIllegalArgumentException(); NodeoldLast= last; last = newNode(); last.item = item; last.next = null; last.prev = oldLast; if (!isEmpty()) { oldLast.next = last; } else { first = last; } n++; } // remove and return the item from the front public Item removeFirst() { if (isEmpty()) thrownewNoSuchElementException(); Itemitem= first.item; first = first.next; n--; if (n == 0) last = first; else first.prev = null; return item; } // remove and return the item from the back public Item removeLast() { if (isEmpty()) thrownewNoSuchElementException(); Itemitem= last.item; last = last.prev; n--; if (n == 0) first = last; else last.next = null; return item; } // return an iterator over items in order from front to back public Iterator<Item> iterator() { returnnewListIterator(); } privateclassListIteratorimplementsIterator<Item> { // 16(overhead) + 8(inner class) = 24 bytes privateNodecurrent= first; // 8(ref) bytes publicbooleanhasNext() { return current != null; } publicvoidremove() { thrownewUnsupportedOperationException(); } public Item next() { if (!hasNext()) thrownewNoSuchElementException(); Itemitem= current.item; current = current.next; return item; } } // unit testing (required) publicstaticvoidmain(String[] args) { /** * Unit testing * * Your main() method must call directly every public constructor * and method to help verify that they work as prescribed * (e.g., by printing results to standard output). */ Deque<Integer> deque = newDeque<Integer>(); for (inti=0; i < 10; i += 2) { deque.addFirst(i); deque.addLast(i + 1); } Iterator<Integer> it = deque.iterator(); while (it.hasNext()) { StdOut.print(it.next() + " "); } StdOut.println("size: " + deque.size()); for (inti=0; i < 3; i++) { deque.removeLast(); deque.removeFirst(); } it = deque.iterator(); while (it.hasNext()) { StdOut.print(it.next() + " "); } StdOut.println("size: " + deque.size()); for (inti=0; i < 6; i += 2) { deque.addLast(i); deque.addFirst(i + 1); } it = deque.iterator(); while (it.hasNext()) { StdOut.print(it.next() + " "); } StdOut.println("size: " + deque.size()); for (inti=0; i < 10; i++) { deque.removeFirst(); } for (inti=0; i < 5; i++) { deque.addLast(i); } for (int a : deque) { for (int b : deque) { StdOut.print(a + "-" + b + " "); } StdOut.println(); } } }
RandomizedQueue
Performance requirements. Your randomized queue implementation must support each randomized queue operation (besides creating an iterator) in constant amortized time. That is, any intermixed sequence of m randomized queue operations (starting from an empty queue) must take at most cm steps in the worst case, for some constant c. A randomized queue containing n items must use at most 48n + 192 bytes of memory. Additionally, your iterator implementation must support operations next() and hasNext() in constant worst-case time; and construction in linear time; you may (and will need to) use a linear amount of extra memory per iterator.
// return an independent iterator over items in random order public Iterator<Item> iterator() { /** * Iterator. Each iterator must return the items in uniformly random order. * The order of two or more iterators to the same randomized queue must be * mutually independent; each iterator must maintain its own random order. */ returnnewRandomIterator(); }
intn=5; RandomizedQueue<Integer> queue = newRandomizedQueue<Integer>(); for (inti=0; i < n; i++) queue.enqueue(i); for (int a : queue) { for (int b : queue) StdOut.print(a + "-" + b + " "); StdOut.println(); }
// return an independent iterator over items in random order public Iterator<Item> iterator() { /** * Iterator. Each iterator must return the items in uniformly random order. * The order of two or more iterators to the same randomized queue must be * mutually independent; each iterator must maintain its own random order. */ returnnewRandomIterator(); } privateclassRandomIteratorimplementsIterator<Item> { privateint[] randomIdxs; privateint i; publicRandomIterator() { randomIdxs = newint[n]; for (intj=0; j < n; j++) { randomIdxs[j] = j; } StdRandom.shuffle(randomIdxs);
i = 0; } publicbooleanhasNext() { return i != randomIdxs.length; } publicvoidremove() { thrownewUnsupportedOperationException(); } public Item next() { if (!hasNext()) thrownewNoSuchElementException(); return rq[(head + randomIdxs[i++]) % rq.length]; } }
/** * * Performance requirements. * * Your randomized queue implementation must support each randomized queue * operation (besides creating an iterator) in constant amortized time. * That is, any intermixed sequence of m randomized queue operations * (starting from an empty queue) must take at most cm steps in the worst case, * for some constant c. * * < iterator's construction cost time! keyword "amortized" implies that * < we may use resizing-array implementation better. and use linked-list * < implementation for randomized may cost linear time: we have to traverse * < from the our begining node in the list. * * A randomized queue containing n items must use at most * 48n + 192 bytes of memory. * * Additionally, your iterator implementation must support operations next() * and hasNext() in constant worst-case time; and construction in linear time; * you may (and will need to) use a linear amount of extra memory per iterator. * * < iterator's construction take linear time! and may need O(n) extra memory * */
publicclassRandomizedQueue<Item> implementsIterable<Item> { private Item[] rq; privateint head; privateint tail; privateint n; // construct an empty randomized queue publicRandomizedQueue() { rq = (Item[]) newObject[1]; head = 0; tail = 0; n = 0; } // is the randomized queue empty? publicbooleanisEmpty() { return n == 0; } // return the number of items on the randomized queue publicintsize() { return n; } privatevoidresize(int capacity) { Item[] copy = (Item[]) newObject[capacity]; for (inti=0; i < n; i++) { copy[i] = rq[(head + i) % rq.length]; } rq = copy; head = 0; tail = n; } // add the item publicvoidenqueue(Item item) { if (item == null) thrownewIllegalArgumentException(); if (n == rq.length) resize(2 * rq.length); rq[tail] = item; tail = (tail + 1) % rq.length; n++; } privateintgetRandomIdx() { return (head + StdRandom.uniform(n)) % rq.length; } // remove and return a random item public Item dequeue() { if (isEmpty()) thrownewNoSuchElementException(); intrandomIdx= getRandomIdx(); Itemitem= rq[randomIdx]; rq[randomIdx] = rq[head]; rq[head] = null; head = (head + 1) % rq.length; n--; if (n > 0 && n == rq.length/4) resize(rq.length/2); return item; } // return a random item (but do not remove it) public Item sample() { if (isEmpty()) thrownewNoSuchElementException(); return rq[getRandomIdx()]; } // return an independent iterator over items in random order public Iterator<Item> iterator() { /** * Iterator. Each iterator must return the items in uniformly random order. * The order of two or more iterators to the same randomized queue must be * mutually independent; each iterator must maintain its own random order. */ returnnewRandomIterator(); } privateclassRandomIteratorimplementsIterator<Item> { privateint[] randomIdxs; privateint i; publicRandomIterator() { randomIdxs = newint[n]; for (intj=0; j < n; j++) { randomIdxs[j] = j; } StdRandom.shuffle(randomIdxs);
i = 0; } publicbooleanhasNext() { return i != randomIdxs.length; } publicvoidremove() { thrownewUnsupportedOperationException(); } public Item next() { if (!hasNext()) thrownewNoSuchElementException(); return rq[(head + randomIdxs[i++]) % rq.length]; } } // unit testing (required) publicstaticvoidmain(String[] args) { /** * Unit testing. * * Your main() method must call directly every public * constructor and method to verify that they work as prescribed * (e.g., by printing results to standard output). */ RandomizedQueue<Integer> rq = newRandomizedQueue<Integer>(); for (inti=0; i < 5; i++) { rq.enqueue(i); } Iterator<Integer> it = rq.iterator(); while (it.hasNext()) StdOut.print(it.next() + " "); StdOut.println(" size: " + rq.size()); it = rq.iterator(); while (it.hasNext()) StdOut.print(it.next() + " "); StdOut.println(" size: " + rq.size()); for (int a : rq) { for (int b : rq) { StdOut.print(a + "-" + b + " "); } StdOut.println(); } for (inti=0; i < 5; i++) { StdOut.println("Dequeue: " + rq.dequeue()); if (i < 3) { it = rq.iterator(); while (it.hasNext()) StdOut.print(it.next() + " "); StdOut.println(" size: " + rq.size()); } } StdOut.println("Queue empty? " + rq.isEmpty()); for (inti=0; i < 5; i++) { rq.enqueue(i); } int[] count = newint[5]; for (inti=0; i < 10000; i++) { count[rq.sample()]++; } for (inti=0; i < 5; i++) { StdOut.print(count[i] + " "); } } }
实际上,上面我的 RamdomizedQueue 实现还是搞复杂了一点,因为最开始我是按着 Queue 去做的:enqueue 时在 last 处加入,dequeue 时随机挑选 head~last 中的某处弹出,但却忘了很关键的一点:其实 Stack 和 Queue 的外在区别仅仅是弹出的元素不同而已。我们完全也可以按着 Stack 去做:enqueue 时在 top 压入元素,dequeue 时随机挑选 0~top 中的某处弹出,可参考他人实现:RandomizedQueue.java by Maecenas。
Permutation
Performance requirements. The running time of Permutation must be linear in the size of the input. You may use only a constant amount of memory plus either one Deque or RandomizedQueue object of maximum size at most n. (For an extra challenge and a small amount of extra credit, use only one Deque or RandomizedQueue object of maximum size at most k.)
题目要求,输入一定数量的字符串,给定 k,要求返回输入字符串的一个 k-排列。
问题很容易转化为从输入的所有字符串中不放回地随机抽取 k 个字符串,所以最简单地,把输入中所有字符串 enqueue 到一个 RandomizedQueue 对象中去,然后 dequeue k 次即可。
/** * * Performance requirements. * * The running time of Permutation must be linear in the size of the input. * You may use only a constant amount of memory plus either one Deque or * RandomizedQueue object of maximum size at most n. * * (For an extra challenge and a small amount of extra credit, use only * one Deque or RandomizedQueue object of maximum size at most k.) * */
publicclassPermutation { /** * Command-line argument. * * You may assume that 0 ≤ k ≤ n, where n is the number of string on * standard input. Note that you are not given n. */ publicstaticvoidmain(String[] args) { RandomizedQueue<String> rq = newRandomizedQueue<String>(); intk= Integer.parseInt(args[0]); if (k == 0) return; for (inti=0; !StdIn.isEmpty(); i++) { Strings= StdIn.readString(); if (i < k) rq.enqueue(s); else { intr= StdRandom.uniform(i + 1); if (r < k) { rq.dequeue(); rq.enqueue(s); } } } while (!rq.isEmpty()) { StdOut.println(rq.dequeue()); } } }