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);
}
}
···