Programming Assignment 2: Randomized Queues and Deques

實現一個泛型的雙端隊列和隨機化隊列,用數組和鏈表的方式實現基本數據結構,主要介紹了泛型和迭代器。java

 

Dequeue. 實現一個雙端隊列,它是棧和隊列的升級版,支持首尾兩端的插入和刪除。Deque的API以下數組

public class Deque<Item> implements Iterable<Item> {
   public Deque()                           // construct an empty deque
   public boolean isEmpty()                 // is the deque empty?
   public int size()                        // return the number of items on the deque
   public void addFirst(Item item)          // insert the item at the front
   public void addLast(Item item)           // insert the item at the end
   public Item removeFirst()                // delete and return the item at the front
   public Item removeLast()                 // delete and return the item at the end
   public Iterator<Item> iterator()         // return an iterator over items in order from front to end
   public static void main(String[] args)   // unit testing
}

deque的操做實現必須是常數時間,使用空間是當前元素個數,迭代器的實現next()和hasNext()操做也是常數時間和常數空間,具體的要求以下。數據結構

1

因此Deque採用Linked-list實現方式。dom

結點定義成:spa

private class Node {
    private Item item;
    private Node prev;
    private Node next;
}

雙端隊列使用Linked-List實現,那麼使用一個哨兵sentinel來輔助實現Deque,這在Programming Tricks and Common Pitfalls中有講到,每一個Assignment中checklist的內容對於理解和實現有很大幫助,那麼咱們能夠這樣設計:設計

1

使用哨兵指向deque的隊首元素,而隊尾元素指向哨兵,如今咱們來分析每一個方法實現。指針

Deque(): 初始Deque沒有元素,元素個數爲0,那麼哨兵的prev和next也都指向自身。code

addFirst(): 隊首添加元素時候,就是簡單的鏈表操做在sentinel和第一個Node之間插入一個新的Node,並記得把元素個數加1.orm

addLast(): 同理blog

removeFirst(): 首先要判斷,deque是否爲空,可否支持刪除操做,能夠的話,刪除首元素,更新第二個元素和sentinel之間的關係,而後元素個數減1

removeLast(): 同理

isEmpty()和size(): 用一直維護元素個數變量來進行操做

迭代器Iterator的操做也十分簡單了, 咱們只須要獲取sentinel哨兵,而後遍歷就能夠實現。hasNext()直到下一個元素又走回了sentinel哨兵,那麼咱們就已經遍歷完了全部元素。

 

Randomized queue. 隨機化隊列也和棧和隊列十分類似,區別在於它的remove操做是隨機刪除隊列中的一個元素。API以下:

public class RandomizedQueue<Item> implements Iterable<Item> {
   public RandomizedQueue()                 // construct an empty randomized queue
   public boolean isEmpty()                 // is the queue empty?
   public int size()                        // return the number of items on the queue
   public void enqueue(Item item)           // add the item
   public Item dequeue()                    // delete and return a random item
   public Item sample()                     // return (but do not delete) a random item
   public Iterator<Item> iterator()         // return an independent iterator over items in random order
   public static void main(String[] args)   // unit testing
}

時間和空間複雜度的要求看上面那個表格,咱們使用Array數組的實現方式。注意一下java不能建立泛型數組,用cast強制轉換來實現,但會致使warning

Item[] a = (Item[]) new Object[1];

Randomized queue和通常的queue基本操做都是同樣,因爲隨機出隊,那入隊元素也不必定按照正常的隊列來使用,咱們只須要把隊列的元素維護在連續起始開始的一段就能夠了。

那麼咱們只須要使用一個tail尾指針,當插入元素的時候,把元素直接插入在隊尾:

public void enqueue(Item item) {
    if (item == null)
        throw new java.lang.NullPointerException("can't add a null item");
    if (N == q.length) resize(2*q.length);
    q[tail++] = item;
    N++;
}

當出隊的時候,隨即一個1-元素個數之間的下標,刪除這個元素,那麼這個位置空出來怎麼辦?把隊尾元素填充上去,而後隊尾置爲空。

在入隊和出隊的時候,要進行resize操做維護空間大小,resize看PPT上面有講解。

public Item dequeue() {
    if (isEmpty())
        throw new java.util.NoSuchElementException("underflow");
    
    int index = StdRandom.uniform(N);
    Item item = q[index];
    //because random, just simply put q[tail-1] to q[index]
    q[index] = q[--tail];
    q[tail] = null;
    N--;
    if (N > 0 && N == q.length/4) resize(q.length/2);
    
    return item;
}

迭代器的操做,不能需改原來的元素,須要從新申請空間,隨機化的出隊思考起來也很簡單,咱們使用Elementary Sort中介紹的Shuffle的方法來對元素從新組合一下

for (int i = 0; i < N; i++) {
    int r = StdRandom.uniform(i+1);
    Item tmp = array[i];
    array[i] = array[r];
    array[r] = tmp;
}

 

Subset client. 從n個string中隨機輸出k個,n中的全部元素每一個最多隻能輸出一次。

要求使用Deque或者RandomizedQueue空間複雜度最多爲N,時間複雜度爲N線性。

使用RandomizedQueue實現的話,初始把全部N個元素都加入到隊列中,而後dequeue()其中n-k個,而後輸出剩下的k個元素就是結果了。

使用Deque隨機的把N個元素加入隊首和隊尾,接下來隨機的在隊首和隊尾刪除n-k元素,而後輸出剩下的k個元素就能夠獲得結果了。

相關文章
相關標籤/搜索