Algorithm I assignment Deque and Randomized Queue
这周的作业时要实现一个双向队列和一个随机队列。并且实现要保证一定的效率。
要保证效率的话其实首先想到的是HashMap,不过由于作业只能使用一个iterator的包,所以不能使用HashMap。想了下,思路其实很简单,用用双向链表就可以实现,不过涉及到链表,要注意一些容易犯错的情况。
我在这个assignment中犯了一下错误:
- 在Randomized Queue中,为了实现一个random的iterator,为了优化时间复杂度,我把当前状态的变量存到了iterator内部,这导致了如果获取iterator之后又enqueue,iterator并不会更新。
- 时间复杂度没有通过,sample()和dequeue()不是常数时间。原因是我采用了双向链表,每次得到一个随机数时,就要遍历一遍,所以这里是O(n),应该实现类似hash的映射。这里对memory的要求只要是O(n)即可,所以可以用一个数组来映射对应的链表。不过这样的话,我其实应该直接用数组的,不然memory可能又要超出了。
- 改用数组实现后,依旧在iterator这个地方出了好几次错。要想实例化的iterator随着队列变化而变化,需要监控随机队列的变化,一旦发生变化,则需要重新设置迭代的顺序和元素。这里采用了一个局部变量size作为标志位,一旦和全局变量N不一致,则重新设置迭代顺序和元素,并将标志位设置为新的N。
- memory超了3个用例。检查后发现是因为在deque时,废弃的对象没有删掉对应的引用,导致了内存的浪费。这点对java程序员来说却是容易忽略,毕竟有了GC,就很少再关心过内存了,但是这里还是必须要把引用指向null,不然会一直有处于未被使用,但是仍有引用的对象。这些对象会一直占用内存。
下面是代码:
1 import java.util.Iterator; 2 3 public class Deque<Item> implements Iterable<Item> { 4 5 private Node front, end; 6 7 private class Node { 8 Item item; 9 Node left, right; 10 11 Node(Item item, Node left, Node right) { 12 this.item = item; 13 this.left = left; 14 this.right = right; 15 } 16 } 17 18 private int N; 19 20 // construct an empty deque 21 public Deque() { 22 front = end = null; 23 N = 0; 24 } 25 26 // is the deque empty? 27 public boolean isEmpty() { 28 return N == 0; 29 } 30 31 // return the number of items on the deque 32 public int size() { 33 return N; 34 } 35 36 // add the item to the front 37 public void addFirst(Item item) { 38 if (item == null) 39 throw new java.lang.NullPointerException(); 40 if (N == 0) { 41 front = end = new Node(item, null, null); 42 } else { 43 Node temp = new Node(item, front, null); 44 front.right = temp; 45 front = temp; 46 } 47 N++; 48 } 49 50 // add the item to the end 51 public void addLast(Item item) { 52 if (item == null) 53 throw new java.lang.NullPointerException(); 54 if (N == 0) { 55 end = front = new Node(item, null, null); 56 } else { 57 Node temp = new Node(item, null, end); 58 end.left = temp; 59 end = temp; 60 } 61 N++; 62 } 63 64 // remove and return the item from the front 65 public Item removeFirst() { 66 if (N == 0) { 67 throw new java.util.NoSuchElementException(); 68 } 69 Item result = front.item; 70 if (N == 1) { 71 front = end = null; 72 } else if (N == 2) { 73 front = end; 74 end.left = end.right = null; 75 } else { 76 front = front.left; 77 front.right = null; 78 } 79 N--; 80 return result; 81 } 82 83 // remove and return the item from the end 84 public Item removeLast() { 85 if (N == 0) { 86 throw new java.util.NoSuchElementException(); 87 } 88 Item result = end.item; 89 if (N == 1) { 90 end = front = null; 91 } else if (N == 2) { 92 end = front; 93 front.left = front.right = null; 94 } else { 95 end = end.right; 96 end.left = null; 97 } 98 N--; 99 return result; 100 } 101 102 // return an iterator over items in order from front to end 103 public Iterator<Item> iterator() { 104 return new Iterator<Item>() { 105 int index = N; 106 Node current = front; 107 108 @Override 109 public boolean hasNext() { 110 return index > 0; 111 } 112 113 @Override 114 public Item next() { 115 if (index == 0) 116 throw new java.util.NoSuchElementException(); 117 Node temp = current; 118 current = current.left; 119 index--; 120 return temp.item; 121 } 122 123 }; 124 } 125 126 // unit testing 127 public static void main(String[] args) { 128 Deque<Integer> unittest = new Deque<Integer>(); 129 unittest.addFirst(1); 130 unittest.addFirst(2); 131 132 unittest.addLast(7); 133 unittest.addLast(8); 134 Iterator<Integer> it = unittest.iterator(); 135 while (it.hasNext()) { 136 System.out.println(it.next()); 137 } 138 System.out.println(unittest.removeFirst()); 139 System.out.println(unittest.removeFirst()); 140 System.out.println(unittest.removeFirst()); 141 System.out.println(unittest.removeLast()); 142 } 143 }
1 import java.util.Iterator; 2 3 import edu.princeton.cs.algs4.StdRandom; 4 5 public class RandomizedQueue<Item> implements Iterable<Item> { 6 private int N; 7 private Item[] item; 8 9 // construct an empty randomized queue 10 @SuppressWarnings("unchecked") 11 public RandomizedQueue() { 12 item = (Item[]) new Object[2]; 13 } 14 15 // is the queue empty? 16 public boolean isEmpty() { 17 return N == 0; 18 } 19 20 // return the number of items on the queue 21 public int size() { 22 return N; 23 } 24 25 // add the item 26 public void enqueue(Item item) { 27 if (item == null) 28 throw new java.lang.NullPointerException(); 29 if (N > this.item.length / 2) { 30 resize(2 * this.item.length); 31 } 32 this.item[N] = item; 33 N++; 34 } 35 36 // remove and return a random item 37 public Item dequeue() { 38 if (N == 0) { 39 throw new java.util.NoSuchElementException(); 40 } 41 int index = StdRandom.uniform(N); 42 if (N < this.item.length / 4) { 43 resize(this.item.length / 2); 44 } 45 Item temp = this.item[index]; 46 this.item[index] = this.item[N - 1]; 47 this.item[N - 1] = null; 48 N--; 49 return temp; 50 } 51 52 // return (but do not remove) a random item 53 public Item sample() { 54 if (N == 0) { 55 throw new java.util.NoSuchElementException(); 56 } 57 int index = StdRandom.uniform(N); 58 return this.item[index]; 59 } 60 61 // return an independent iterator over items in random order 62 public Iterator<Item> iterator() { 63 Iterator<Item> it = new IteratorOfRand(); 64 return it; 65 } 66 67 private class IteratorOfRand implements Iterator<Item> { 68 private int used = 0; 69 private int size = N; 70 private int[] shuffle; 71 72 IteratorOfRand() { 73 shuffle = new int[N]; 74 for (int i = 0; i < N; i++) { 75 shuffle[i] = i; 76 } 77 StdRandom.shuffle(shuffle); 78 } 79 80 @Override 81 public boolean hasNext() { 82 if (size != N) { 83 size = N; 84 shuffle = new int[N]; 85 for (int i = 0; i < N; i++) { 86 shuffle[i] = i; 87 } 88 StdRandom.shuffle(shuffle); 89 } 90 if (used == N) { 91 used = 0; 92 return false; 93 } else { 94 return true; 95 } 96 } 97 98 @Override 99 public Item next() { 100 if (!this.hasNext()) 101 throw new java.util.NoSuchElementException(); 102 Item result = item[shuffle[used]]; 103 used++; 104 return result; 105 } 106 } 107 108 @SuppressWarnings("unchecked") 109 private void resize(int n) { 110 Item[] temp = this.item; 111 item = (Item[]) new Object[n]; 112 int j = 0; 113 for (int i = 0; i < N; i++) { 114 item[j++] = temp[i]; 115 } 116 } 117 118 // unit testing 119 public static void main(String[] args) { 120 RandomizedQueue<Integer> unittest = new RandomizedQueue<Integer>(); 121 unittest.enqueue(1); 122 unittest.enqueue(2); 123 Iterator it = unittest.iterator(); 124 while(it.hasNext()) { 125 System.out.println(it.next()); 126 } 127 unittest.enqueue(3); 128 while(it.hasNext()) { 129 System.out.println(it.next()); 130 } 131 // for (int i = 0; i < 7; i++) { 132 // System.out.println(i+"th sample:" + unittest.sample() 133 // +" rand:"+unittest.dequeue()); 134 // 135 // } 136 } 137 }
import edu.princeton.cs.algs4.StdIn; public class Subset { public static void main(String[] args) { int k = Integer.parseInt(args[0]); RandomizedQueue<String> rq = new RandomizedQueue<String>(); while (!StdIn.isEmpty()) { rq.enqueue(StdIn.readString()); } for (int i = 0; i < k; i++) { System.out.println(rq.dequeue()); } } }
经过不断修改,这周还是满分通过,不过这次用的时间比上次要长。