Deque和RandomizedQueue實現時碰到的一些問題
這是Algorithms第二周的作業。這次比上周的作業順利多了,也有可能自己以前有鏈表的基礎,因此完成地快些。
Deque就像python中地deque一樣,支持兩端的添加和刪除 constant time cost. 無疑是要用鏈表實現的。對兩端操作一定需要用雙向鏈表。
最後作業提交,Deque出現的問題更多。首先,在調試的時候,根據以前刷題的印象,一個鏈表不需要一個額外的頭,每個node都一樣。這樣表示非常易懂,但是初始化第一個node的時候會出現問題,一不小心就把head和tail弄成了兩條鏈表。因為第一個node可能是通過addFirst或者addLast添加的,所以這兩個函數實現的時候都要考慮head和tail指針。很多時候需要考慮head或tail為空的情況,也就是沒有元素或者在操作最後一個元素的時候。但凡用到head或者tail的地方都要註意,小心。在寫iterator的時候不用null而是直接用head.prev初始化current值,真是蠢到家了!!
···
import java.util.Iterator;
import java.util.NoSuchElementException;
public class Deque
private Node<Item> head, tail; private int size; public Deque() { tail = head = null; size = 0; } public boolean isEmpty() { return size() == 0; } public int size() { return size; } public void addFirst(Item item) { if(item == null) throw new IllegalArgumentException(); Node<Item> node = new Node<>(); node.item = item; node.next = head; if(head != null) head.prev = node; else tail = node; // "node" is the only node head = node; size++; } public void addLast(Item item) { if(item == null) throw new IllegalArgumentException(); Node<Item> node = new Node<>(); node.item = item; node.prev = tail; if(tail != null) tail.next = node; else head = node; // "node" is the only node tail = node; size++; } public Item removeFirst() { if(isEmpty()) throw new NoSuchElementException(); Node<Item> first = head; head = head.next; if(head != null) head.prev = null; else tail = null; size--; return first.item; } public Item removeLast() { if(isEmpty()) throw new NoSuchElementException(); Node<Item> last = tail; tail = tail.prev; if(tail != null) tail.next = null; else head = null; size--; return last.item; } @Override public Iterator<Item> iterator() { return new DequeIterator(); } private class DequeIterator implements Iterator<Item> { private Node<Item> current; @Override public boolean hasNext() { return current.next != null; } @Override public Item next() { current = current.next; if(current == null) { throw new NoSuchElementException(); } return current.item; } public DequeIterator() { current = new Node<>(); current.next = head; } } private class Node<Item> { public Node<Item> next, prev; public Item item; } @Override public String toString() { String ret = ""; for(Item item : this) { ret += item+", "; } ret += "size="+size(); return ret; } public static void main(String[] args) { Deque<Integer> deque = new Deque<>(); deque.addLast(0); deque.removeFirst();// ==> 0 deque.isEmpty();// ==> true deque.addLast(3); deque.removeFirst(); }
}
···
RandomizedQueue實現總結
本來這次作業可以一次性寫完提交的。但看到題目上有memery的限制,於是只能補完第一周的算法分析那一節。之後回來看題目要求的48n+192個字節能做什麽。最初對時間復雜度沒有看清要求,以為dequeue可以cn,但是提交反饋裏說要constant time。我用數組的方式實現了這個隊列,鏈表顯然沒法做到隨機訪問。用上了之前的可擴展的數組實現,第一次測試的時候iterator出了點兒小問題,改完就通過講了簡單測試。然後就提交了代碼。當然,dequeue由於用了刪除單個元素並移動之後所有元素的方式實現,沒有做到constant time,使得最後提交沒有拿到90分以上。最後再看了看代碼,發現既然對元素順序沒有要求,那只需要把數組的最後一個元素移到需要刪除元素的位置上並刪掉最後一個元素就ok了。一個小小的改動從88分跳到了95分。
···
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class RandomizedQueue
private Item[] list;
private int head, tail;
public RandomizedQueue() {
list = (Item[]) new Object[2];
head = tail = 0;
}
public boolean isEmpty() {
return size() == 0;
}
public int size() {
return tail - head;
}
public void enqueue(Item item) {
if(item == null)
throw new IllegalArgumentException();
if(tail >= list.length)
resize(size()*2);
list[idx(tail++)] = item;
}
public Item dequeue() {
if(isEmpty())
throw new NoSuchElementException();
int deq = StdRandom.uniform(head, tail);
Item item = list[idx(deq)];
if(deq == tail-1)
list[idx(deq)] = null;
else
list[idx(deq)] = list[idx(tail-1)];
tail--;
if(size() < list.length/4)
resize(list.length/2);
return item;
}
public Item sample() {
if(isEmpty())
throw new NoSuchElementException();
return list[idx(StdRandom.uniform(head, tail))];
}
private void resize(int newsize) {
Item[] newlist = (Item[]) new Object[newsize];
for(int i=head,j=0; i<tail; i++, j++) {
newlist[j] = list[idx(i)];
}
head = 0;
tail = size();
list = newlist;
}
private int idx(int index) {
return index % list.length;
}
@Override
public Iterator<Item> iterator() {
return new QueueIterator();
}
private class QueueIterator implements Iterator<Item> {
private Item[] queue;
private int current;
public QueueIterator() {
queue = (Item[]) new Object[size()];
for(int i=0; i<size(); i++) {
queue[i] = list[idx(i+head)];
}
StdRandom.shuffle(queue);
current = 0;
}
@Override
public boolean hasNext() {
return current < queue.length;
}
@Override
public Item next() {
if(current >= queue.length)
throw new NoSuchElementException();
return queue[current++];
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
public String toString() {
String ret = "";
for(Item item : this) {
ret += item+", ";
}
ret += "size="+size();
return ret;
}
public static void main(String[] args){
RandomizedQueue<Integer> queue = new RandomizedQueue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.enqueue(4);
queue.enqueue(5);
StdOut.println(queue);
StdOut.println(queue.dequeue());
StdOut.println(queue.dequeue());
StdOut.println(queue);
}
}
···
Deque和RandomizedQueue實現時碰到的一些問題