Coursera Algorithms第二周编程任务

Programming Assignment 2: Deques and Randomized Queues

Write a generic data type for a deque and a randomized queue. The goal of this assignment is to implement elementary data structures using arrays and linked lists, and to introduce you to generics and iterators.

Algorithms 第二周的编程任务,主要目的是编写一个双向链表,随机队列和一个测试客户端。

课程给出的两个数据类型说明(API):

Dequeue. A double-ended queue or deque (pronounced “deck”) is a generalization of a stack and a queue that supports adding and removing items from either the front or the back of the data structure. Create a generic data type Deque that implements the following 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)          // add the item to the front
   public void addLast(Item item)           // add the item to the end
   public Item removeFirst()                // remove and return the item from the front
   public Item removeLast()                 // remove and return the item from 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 (optional)
}

Randomized queue. A randomized queue is similar to a stack or queue, except that the item removed is chosen uniformly at random from items in the data structure. Create a generic data type RandomizedQueue that implements the following API:

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

方案

Deque 类需要同时维护指向头部和尾部的两个节点,并同为在单向链表的基础上,在内部类Node上添加previous属性用于指向上一个节点。

实现代码

import java.util.Iterator;
public class Deque<Item> implements Iterable<Item> {
    private Node first = null;
    private Node last = null;
    private int capacity = 0;

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

    /**
     * Construct an empty deque
     */
    public Deque() {}

    /**
     * Check whether the deque is empty.
     *
     * @return true when the deque is empty, false otherwise.
     */
    public boolean isEmpty() {return capacity == 0;}

    /**
     * Return the number of items on the deque.
     *
     * @return the number of items on the deque.
     */
    public int size() {return capacity;}

    /**
     * Add the item to the front
     *
     * @throws java.util.NoSuchElementException when {@code item} is empty.
     */
    public void addFirst(Item item)
    {
        if (item == null) throw new IllegalArgumentException();
        Node oldFirst = first;
        first = new Node();
        first.item = item;
        if (capacity == 0)
        {
            first.next = null;
            first.previous = null;
            last = first;
        }
        else {
            first.next = oldFirst;
            oldFirst.previous = first;
        }
        ++capacity;
    }

    /**
     * Add the item to the end
     *
     * @throws java.util.NoSuchElementException when {@code item} is empty.
     */
    public void addLast(Item item)
    {
        if (item == null) throw new IllegalArgumentException();

        Node oldLast = last;
        last = new Node();
        last.item = item;
        if (capacity == 0)
        {
            last.previous = null;
            last.next = null;
            first = last;
        }
        else {
            oldLast.next = last;
            last.previous = oldLast;
        }
        ++capacity;
    }

    /**
     * Remove and return the item from the front
     *
     * @throws java.util.NoSuchElementException when {@code Deque} is empty.
     */
    public Item removeFirst()
    {
       if (isEmpty()) throw new java.util.NoSuchElementException();

       Item item = first.item;
       first = first.next;
       if (capacity == 1)
       {
           last = first;
       }
       else
       {
           first.previous = null;
       }
        capacity--;
       return item;
    }

    /**
     * Remove and return the item from the end
     *
     * @throws java.util.NoSuchElementException when {@code Deque} is empty.
     */
    public Item removeLast()
    {
        if (isEmpty()) throw new java.util.NoSuchElementException();

        Item item = last.item;
        last = last.previous;
        if (capacity == 1) {
            first = last;
        }
        else
        {
            last.next = null;
        }
        capacity--;
        return item;
    }

    /**
     * Return an iterator over items in order from front to end.
     *
     * @return an iterator over items in order from front to end.
     *
     * @throws java.util.NoSuchElementException when called {@code next()} method if
     * there is no next element.
     * @throws UnsupportedOperationException when called {@code remove()} method.
     */
    @Override
    public Iterator<Item> iterator()
    {
        return new ListIterator();
    }

    private class ListIterator implements Iterator<Item>
    {
        private Node current = first;

        @Override
        public boolean hasNext() { return current != null; }

        @Override
        public void remove() {
            throw new java.lang.UnsupportedOperationException();
        }

        @Override
        public Item next()
        {
            if (!hasNext()) throw new java.util.NoSuchElementException();
            Item item = current.item;
            current = current.next;
            return item;
        }

    }
}

RandomizedQueue 类因为要返回随机元素,所以我们采用数组的方法。dequeue 方法随机选择一个元素,并将选择节点指向最后节点,再让最后节点指向null。

实现代码

import edu.princeton.cs.algs4.StdRandom;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class RandomizedQueue<Item> implements Iterable<Item> {
    private int amount = 0;
    private Item[] queue;

    /**
     * Construct an empty randomized queue.
     */
    public RandomizedQueue() {
        queue = (Item[]) new Object[2];
    }

    /**
     * Check whether the randomized queue is empty.
     *
     * @return true when the randomized queue is empty, false otherwise.
     */
    public boolean isEmpty() {
        return amount == 0;
    }

    /**
     * Return the number of items on the randomized queue.
     *
     * @return the number of items on the randomized queue.
     */
    public int size() {
        return amount;
    }

    /**
     * Add the item.
     */
    public void enqueue(Item item) {
        if (item == null) {
            throw new java.lang.IllegalArgumentException();
        }

        if (amount == queue.length) {
            resize(queue.length * 2);
        }
        queue[amount++] = item;
    }

    /**
     * Remove and return a random item.
     *
     * @return a random item.
     */
    public Item dequeue()
    {
        if (isEmpty()) {
            throw new NoSuchElementException();
        }

        int randomIndex = StdRandom.uniform(amount);
        Item item = queue[randomIndex];
        queue[randomIndex] = queue[amount - 1];
        queue[--amount] = null;

        if (amount > 0 && amount == queue.length / 4) {
            resize(queue.length / 2);
        }
        return item;
    }

    /**
     * Return a random item (but do not remove it).
     *
     * @return a random item (but do not remove it).
     */
    public Item sample() {
        if (isEmpty()) {
            throw new NoSuchElementException();
        }

        int randomIndex = StdRandom.uniform(amount);
        return queue[randomIndex];
    }

    /**
     * Return an independent iterator over items in random order.
     *
     * @return an independent iterator over items in random order.
     *
     * @throws NoSuchElementException when called {@code next()} method if
     * there is no next element.
     * @throws UnsupportedOperationException when called {@code remove()} method.
     */
    public Iterator<Item> iterator() {
        return new ListIterator();
    }

    private void resize(int capacity) {
        Item[] temp = (Item[]) new Object[capacity];
        System.arraycopy(queue, 0, temp, 0, queue.length);
        queue = temp;
    }

    private class ListIterator implements Iterator<Item> {
        private int[] randomIndexes = StdRandom.permutation(amount);
        private int i = 0;

        @Override
        public boolean hasNext() {
            return i < amount;
        }

        @Override
        public Item next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            return queue[randomIndexes[i++]];
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

}

Permutation 类(参考某论坛的楼主):

我最开始的想法是如果知道了k和N,那么读每个数的时候就知道他进RandomizedQueue 的概率了;

但问题是input stream读完之前,我不可能知道N是多少;而input stream读完之后,每个数都按自己应有的概率enqueue了;

所以解决问题的方法就是:N是动态的;每读一个数,N++,然后对之前的概率分配进行调整;

1,2,3,4,5; k = 3, N=5 为例:

queue长度达到k之前,照单全收;所以queue长度达到k时里面的元素是:1, 2 ,3

这时候从input stream里面读到4,一共读了4个数,N变成了4;

由于queue的长度不能超过k,超过1个也不行,所以我要先从1,2,3里面随机洗出一张牌,再把4放进去;保证queue的长度一直是k

由于dequeue()是随机的,所以(1,2,4),(1,3,4),(2,3,4)都是等概的;

问题是没有(1,2,3); 所以有一定概率我不要4从而得出(1,2,3)。这个概率是多少呢?

也就是从N个数里面挑k个数,没有某一个数的概率 \(\frac{C_{N-1}^k}{C_N^k} = \frac{N-k}{N}\)

此时k = 3, N = 4(1,2,3)的概率就是\(\frac{1}{4}\)了;

下一步,读到5N = 5; 又要面临是否要把5弄进去的抉择,此时的\(\frac{N-k}{N}\)= \(\frac{2}{5}\); 也就是说不要5的概率是\(\frac{2}{5}\).

由此保证了每个(a,b,c)的permutation一定是等概的。

这个算法的思路在于:

每次决定要不要一个新数加进来的时候,我都可以保证:如果加进来,然后我可以把包含它的所有组合洗的等概;那么我只要保证不加他进来的总概率(或加他进来的总概率)是对的即可;

这个不加进来的概率就是 \(\frac{N-k}{N}\)(加进来的概率是\(\frac{k}{N}\)),最后给出实现代码。

实现代码

import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;

public class Permutation
{
    /**
     * Creates one {@code RandomizedQueue} object
     * of maximum size at most k.
     *
     * @param args the command-line arguments
     */
    public static void main(String[] args)
    {
        int k = Integer.parseInt(args[0]);
        RandomizedQueue<String> rq = new RandomizedQueue<String>();
        double n = 1.0;
        while (!StdIn.isEmpty())
        {
            String s = StdIn.readString();
            if(k == 0) {
                break;
            }
            else if (rq.size() < k)
            {
                rq.enqueue(s);
            }
            else if (StdRandom.uniform() > ((n-k)/n)) {
                rq.dequeue();
                rq.enqueue(s);
            }
                n++;
        }
        for (String s : rq) StdOut.println(s);
    }
}

原文地址:https://www.cnblogs.com/revc/p/9226716.html

时间: 2024-10-29 19:07:38

Coursera Algorithms第二周编程任务的相关文章

吴恩达第二课第二周编程实例

吴恩达第2课第2周编程习题 目标:使用mini-batch来加快学习速度:比较梯度下降,momentum,adam的效果 核心:指数加权平均值得计算及其意义,它是momentum,RMSProp,Adam算法的基石 不足:本例程没有使用学习率衰减的步骤,同时本例程只适于3层的二分法的神经网络 常记点: 1. 偏差修正时是除以,此处是-,t从1开始: 2. L=len(parameters) //2 ,这个L不等于网络层数,range(1,L+1)=range(1,len(layers_dims)

Coursera machine learning 第二周 编程作业 Linear Regression

必做: [*] warmUpExercise.m - Simple example function in Octave/MATLAB[*] plotData.m - Function to display the dataset[*] computeCost.m - Function to compute the cost of linear regression[*] gradientDescent.m - Function to run gradient descent 1.warmUpE

第二周编程总结

1.实验代码 include<stdio.h> include<process.h> int main() { FILE*fp; int j,i,k,max; int num[5]; char op; if((fp=fopen("C:\wtt.txt","r+"))==NULL){ printf("File open error!"); exit(0); } for(i=0;i<5;i++){ fscanf(fp,&

《C语言MOOC——翁恺》第二周编程练习记录

1 逆序的三位数(5分) 题目内容: 逆序的三位数: 程序每次读入一个正三位数,然后输出逆序的数字.注意,当输入的数字含有结尾的0时,输出不应带有前导的0.比如输入700,输出应该是7. 提示:用%10可以得到个位数,用/100可以得到百位数....将这样得到的三个数字合起来:百位*100+十位*10+个位,就得到了结果. 注意:除了题目要求的输出,不能输出任何其他内容,比如输入时的提示,输出时的说明等等都不能.这道题目要求输出逆序的数字,程序就只能输出这个数字,除此之外任何内容都不能输出. 输

20165214 结队编程项目-四则运算(第二周)

20165214 第一次结队编程项目--四则运算第二周 需求分析 本周的结队编程想要实现一个四则运算系统,它可以自动生成n个计算题(本周不包括分数),其中n由我们输入.每输出一道题目,运行程序的人需要输入相应的答案,直到最后一道题做完.最后,统计正确率.然后,在这个基础上可以进行相应的功能扩展,比如语言支. 设计思路 我需要在上周的基础上对程序进行补充.在题目的生成上,应该再加上括号.÷./ 本周达成: ①能够随机生成n道题目,n由我们输入,最大长度可直接在程序里面修改: ②支持真分数运算: ③

20165104孟凡斌-结对编程练习(第二周)

20165104孟凡斌-结对编程练习(第二周) 很不幸,这次的结对练习作业的难度,依然远远远超出我,我的搭档两人得能力范围.结对学习,变成了结对向同学请教学习.两个人一起问,确实效率高一点,每周保持这样的学习状态,虽说很累,但确实比原来懒散的我学习好很多. 需求分析 ------------------- 题目要求 自动生成小学四则运算题目(加.减.乘.除) 支持整数 支持多运算符(比如生成包含100个运算符的题目) 支持真分数 统计正确率 扩展要求 处理生成题目并输出到文件 完成题目后从文件读

20172312『Java程序设计』课程 结对编程练习_四则运算第二周阶段总结

20172312『Java程序设计』课程 结对编程练习_四则运算第二周阶段总结 结对伙伴 学号 :20172315 20172318 姓名 :胡智韬 陆大岳 伙伴第一周博客地址: 对结对伙伴的评价:这俩人一开始各编各的还好,到后面就开始吵,从头吵到尾,陆大胖,胡志汪,还好到最后是把代码敲出来了,不容易不容易. 小组结对编程的照片(QQ群截图) 项目中自己负责的部分 代码的综合整理,错误查找,合并以及博客的撰写. 个人贡献度划分 彭霖:胡智韬:陆大岳=3:3:4 相关过程的截图 生成题目类驱动类的

20172327 结对编程项目-四则运算 第二周 阶段总结

20172327 结对编程项目-四则运算 第二周 阶段总结 结对编程项目-四则运算 第二周 输出阶段总结博客 结对对象: 学号:20172317 姓名:蒋子行 伙伴第二周博客地址: 学号:20172320 姓名:李闻洲 伙伴第二周博客地址: 担任角色: 驾驶员:蒋子行 副驾驶:李闻洲 马瑞蕃 小组结对编程的photo: 项目中自己负责的部分: 我在项目中真的没啥贡献,项目主要由祥哥主刀,我和李闻洲就是打杂的. 个人贡献度划分: 我在项目中真的没啥贡献,项目主要由祥哥主刀,我就是打杂的,提点建议和

20172316 结对编程-四则运算 第二周 阶段总结

20172316 结对编程-四则运算 第二周 阶段总结 1.项目内容 以结对小组形式编写代码,实现以下功能: 1.自动生成题目 2.题目运算(判题) 3.支持真分数 4.生成题目添加括号 5.题目去重(扩展需求,加分项) 6.文件处理(扩展需求,自行学习,加分项) 相关过程截图 最开始的代码名称十分不规范,有的使用拼音,有的使用英文,有的不知所云,其他组员第一次看见名称时不容易理解和衔接起来: 出现了两个程序之间符号表示的差异,对于式子的值的计算有很大影响: 结对评价 唐才铭 王文彬 关键代码解