Coursera Algorithm Ⅰ week2 编程作业 Deques and Randomized Queues
程序员文章站
2022-03-09 15:28:28
...
代码:https://github.com/RedemptionC/CourseraAlgorithms/tree/master/rQueue-deque
这是学习完栈和队列之后的编程作业,需要提交3个类:c.java
,Deque.java
和 Permutation.java
主要是使用数组和链表实现deque和RandomizedQueue的泛型数据类型
Deque
deque支持在头尾进行添加或删除元素:
import edu.princeton.cs.algs4.StdOut;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class Deque<Item> implements Iterable<Item> {
private node first, last;
private int size;
public Iterator<Item> iterator() {
return new dequeIterator();
}
private class node {
Item item;
node next, prev;
}
private class dequeIterator implements Iterator<Item> {
private node current = first;
public boolean hasNext() {
return current != null;
}
public Item next() {
if (!hasNext()) {
throw new NoSuchElementException("no more items");
}
Item t = current.item;
current = current.next;
return t;
}
public void remove() {
throw new UnsupportedOperationException("no remove operation");
}
}
public Deque() {
first = last = null;
size = 0;
}
public boolean isEmpty() {
return first == null;
}
public int size() {
return size;
}
public void addFirst(Item item) {
if (item == null) {
throw new IllegalArgumentException("arg can not be null");
}
node n = new node();
n.item = item;
n.next = first;
n.prev = null;
if (size == 0) {
// 如果添加之前为空,需要设置last,first为新增元素
last = n;
first = n;
} else {
// 并不是循环队列
first.prev = n;
first = n;
}
size++;
}
public void addLast(Item item) {
if (item == null) {
throw new IllegalArgumentException("arg can not be null");
}
node n = new node();
n.item = item;
n.next = null;
n.prev = last;
if (size == 0) {
last = n;
first = n;
} else {
last.next = n;
last = n;
}
size++;
}
public Item removeFirst() {
// 注意要判断非空
if (isEmpty()) {
throw new NoSuchElementException("can not remove when deque is empty");
}
node t = first;
Item i = t.item;
if (size == 1) {
last = first = null;
size--;
return i;
}
first = first.next;
first.prev = null;
size--;
t = null;
return i;
}
public Item removeLast() {
if (isEmpty()) {
throw new NoSuchElementException("can not remove when deque is empty");
}
node t = last;
Item i = t.item;
if (size == 1) {
last = first = null;
size--;
return i;
}
last = last.prev;
t = null;
last.next = null;
size--;
return i;
}
public static void main(String[] args) {
Deque<String> deque = new Deque<>();
for (int i = 0; i < 3; i++) {
deque.addLast(i + "");
}
while (!deque.isEmpty()) {
StdOut.println(deque.removeFirst());
}
Deque<Integer> ideque = new Deque<>();
for (int i = 0; i < 20; i++) {
ideque.addFirst(i);
}
Iterator<Integer> iterator = ideque.iterator();
while (iterator.hasNext()) {
StdOut.println(iterator.next());
}
while (!ideque.isEmpty()) {
StdOut.println(ideque.removeLast());
}
System.out.println("************************");
for (int i = 0; i < 3; i++) {
if (i % 2 == 0) {
ideque.addFirst(i);
} else {
StdOut.println(ideque.removeLast());
}
}
}
}
头尾插入/删除 是核心代码,小心一点,还比较简单
另外要注意的两个点是泛型和迭代器,迭代器需要实现两个方法,hasNext和Next
RandomizedQueue
特点是从queue删除元素时,必须是随机的。既然要访问随机位置的元素,这里使用的是数组实现
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import java.util.Iterator;
import java.util.NoSuchElementException;
/*
* 因为要在O(1)内获取随机位置,所以必须使用数组保存
* resizing array
* 数组满时double
* 数组只有四分之一满时,half,half时注意要新建一个数组然后把
* 原来数组里的元素逐一复制到新数组里去,然后指向新数组
* */
public class RandomizedQueue<Item> implements Iterable<Item> {
private Item[] rqueue;
private int size, capacity;
public RandomizedQueue() {
rqueue = (Item[]) new Object[1];
size = 0;
capacity = 1;
}
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
private void resize(int newcap) {
Item[] newqueue = (Item[]) new Object[newcap];
// 注意,resize也可能变小,所以这里的循环控制条件不是size,而是size和newcap
for (int i = 0; i < size && i < newcap; i++) {
newqueue[i] = rqueue[i];
}
rqueue = newqueue;
capacity = newcap;
}
public void enqueue(Item item) {
if (item == null) {
throw new IllegalArgumentException("arg can not be null");
}
// 如果恰好只有一个空位,也要resize,因为size指向的是下一个?
if ((capacity - size) <= 1) {
resize(capacity * 2);
}
rqueue[size++] = item;
}
public Item dequeue() {
if (isEmpty()) {
throw new NoSuchElementException("can not remove when deque is empty");
}
if (size == capacity / 4) {
resize(capacity / 2);
}
int index = StdRandom.uniform(size);
Item rs = rqueue[index];
// 把最后一个移到出队的元素处,并size--
rqueue[index] = rqueue[--size];
return rs;
}
public Item sample() {
if (isEmpty()) {
throw new NoSuchElementException("empty");
}
int index = StdRandom.uniform(size);
return rqueue[index];
}
private class rqueueIterator implements Iterator<Item> {
//用一个元素记录已遍历的个数
// private int cur;
private Item[] copy;
private int copySize;
public rqueueIterator() {
//创建一份拷贝,每输出一个就deque掉
copy = (Item[]) new Object[size];
for (int i = 0; i < size; i++) {
copy[i] = rqueue[i];
}
copySize = size;
}
public boolean hasNext() {
return copySize > 0;
}
public Item next() {
if (!hasNext()) {
throw new NoSuchElementException("no more items");
}
int index = StdRandom.uniform(copySize);
// 因为每次都是随机选一个,所以不能从前到后记录遍历位置,而是把已经遍历的元素直接出队
// 这样就不会选到已经遍历的元素
Item rs = copy[index];
copy[index] = copy[--copySize];
return rs;
}
public void remove() {
throw new UnsupportedOperationException("no remove operation");
}
}
public Iterator<Item> iterator() {
return new rqueueIterator();
}
public static void main(String[] args) {
RandomizedQueue<String> randomizedQueue = new RandomizedQueue<>();
for (int i = 0; i < 1000; i++) {
randomizedQueue.enqueue(i + "");
// randomizedQueue.dequeue();
}
StdOut.println(randomizedQueue.size());
for (String i : randomizedQueue) {
StdOut.println(i);
}
StdOut.println("*****************************");
for (int i = 0; i < 3; i++) {
StdOut.println(randomizedQueue.sample());
}
StdOut.println("*****************************");
StdOut.println("test iterator independence");
for (String i : randomizedQueue) {
for (String j : randomizedQueue) {
StdOut.println(i + "-" + j);
}
}
StdOut.println("*****************************");
while (!randomizedQueue.isEmpty()) {
StdOut.println(randomizedQueue.dequeue());
}
StdOut.println(randomizedQueue.isEmpty());
}
}
Permutation
就是对随机队列的应用