CS61B Proj1: LinkListedDeque

93 阅读4分钟

CS61B Proj1: Dqeue Data Structures

介绍

Proj1 主要分为两个部分:

  1. 数据结构部分

创建LinkedListDeque.java 和 ArrayDeque.java 文件. 对应两种不同的实现.

并且使用Lab3中学到的测试方法验证其正确性.

当本部分完成之后, 可以使用Proj0 CheckPoint 检查其正确性.

  1. 应用程序部分

创建MaxArrayDeque.java, 并实现能够播放Guitar Hero的声音合成器.

Packages

概念:

包是Java类的集合, 用于协同实现一个目标. 诸如, org.Junit是一个包. Assert是一个类, 其中的assert方法是里面的方法.

方法:

  1. 创建包时, 我们只需要在文件顶部制定代码是包的一部分即可.
package deque;
  1. 使用包时, 只需要引用包即可. 包本质上知识计算机中的某个文件夹.
import deque;

The Deque API

概念:

Deqeue: doubled-ended queue. 双端队列.

双端队列是一个具有动态大小的容器, 可以在其两端收缩或者扩展.

我们需要实现的方法:

  1. public void addFirst(T item)

  2. public void addLast(T item)

  3. public boolean isEmpty()

  4. public int size()

  5. public void printDeque()

  6. public T removeFirst()

  7. public T removeLast()

  8. public T get(int index)

  9. public Iterator iterator()

  10. public boolean equals(Object o)

要求:

  1. 我们不应该让Deque接口实现Iterable, 而应该让其子类实现

  2. 我们需要编写两种不同的子类:

    1. 链表
    2. 可以调整大小的数组
  3. 我们编写的子类, 应该接受任何泛型类型.

Project Tasks

Linked List Deque 链表双端队列

要求:

  1. 添加操作和删除操作不能使用递归, 单个操作花费的时间是恒定的.

  2. get使用迭代, 而不能使用递归

  3. size花费的时间也是恒定时间

  4. for-each循环 花费时间和数量成正比

  5. 不要维护不存在于双端队列中的items的结点的引用, 即使用的内存量和项目数成正比.

  6. 不能使用导入数据结构

额外需要实现的方法:

  1. public LinkedListDeque()
  2. public T getRecursive(int index)

参考内容:

  1. 双向链表

方法实现:

首先, 建立一个链表.

回忆Lecture中学到的内容:

  1. 引用最后一个结点, addLast时间恒定
  2. 对于每一个结点, 引用前一个结点, removeLast时间恒定.

所以我们需要建立的是一个DDList(双向链表) + 且记录头尾结点. 同时使用的是循环链表(即prev指向最后的元素)

又因为size的花费时间也要求恒定, 所以我们建立一个size变量, 用于记录链表的长度.

public class LinkedListDeque<T> {
    private int size;
    private Node sentinel;

    private static class Node<T> {
        private Node prev;

        private Node next;

        private T item;
        public Node(Node p, T i, Node n) {
            prev = p;
            item = i;
            next = n;
        }
    }
}

1. LinkedListDeque()

首先我们准备完成的任务是构造函数.

public LinkedListDeque() {
    sentinel = new Node(null, null, null);
    sentinel.prev = sentinel;
    sentinel.next = sentinel;
    size = 0;
}

2. size()

其次则是size(), 用于返回链表的长度. 该方法直接返回记录了数据长度的size即可.

同时因为数据抽象, 我们将size设置为private私有属性.

public int size() {
    return size;
}

3. isEmpty()

第三个是isEmpty(), 直接使用size判断就可以了.

public boolean isEmpty() {
    return size == 0;
}

4, addFirst()

第四个实现的是增加第一个元素

public  void addFirst(T item) {
    // 后指的是第一个元素
    Node add_node = new Node(sentinel, item, sentinel.next);
    sentinel.next = add_node;
    add_node.next.prev = add_node;
    size += 1;
}

步骤:

  1. 建立新结点, 存储数据. 并建立和链表的关系
    1. 前面是sentinel
    2. 后面是第一个结点
  2. 重新设定链表结点间的关系
    1. sentinel后面的是该结点
    2. 原本第一个结点前面的是新建立的结点
  3. 链表的长度+1

5. addLast()

第五个方法是增加后一个元素

public void addLast(T item) {
    // 前指的是第一个元素
    Node new_node = new Node(sentinel.prev, item, sentinel);
    sentinel.prev = new_node;
    new_node.prev.next = new_node;
    size += 1;
}

6. removeFirst()

第六个实现的是删除第一个元素

public T removeFirst() {
    if (size == 0) {
        return null;
    }
    Node removed_node = sentinel.next;
    removed_node.next.prev = sentinel;
    sentinel.next = removed_node.next;
    size -= 1;
    return (T) removed_node.item;
}

7. removeLast()

第7个实现的是删除最后一个元素

public T removeLast() {
    if (size == 0) {
        return null;
    }
    Node removed_node = sentinel.prev;
    removed_node.prev.next = removed_node.next;
    removed_node.next.prev = removed_node.prev;
    size -= 1;
    return (T) removed_node.item;
}

8. get

public T get(int i) {
    if (i < 0 || i >= size) {
        return null;
    }
    Node node = this.sentinel;
    for (int j = 0; j <= i; j += 1) {
        node = node.next;
    }
    return (T) node.item;
}

9. getRecursion()

public T getRecursive(int index) {
    if (index < 0 || index >= size) {
        return null;
    }
    return getRecursive_helper(index, sentinel.next);
}

public T getRecursive_helper(int index, Node node) {
    if (index  == 0) {
        return (T) node.item;
    }
    return getRecursive_helper(index - 1, (Node) node.next);
}

10. iterator()

public Iterator<T> iterator() {
    return new LinkedListDequeIterator<>();
}

public class LinkedListDequeIterator<T> implements Iterator<T> {
    private int index;
    private Node node;

    public LinkedListDequeIterator() {
        index = 0;
        node = sentinel.next;
    }
    public boolean hasNext() {
        return index < size;
    }

    public T next() {
        T item = (T) node.item;
        node = node.next;
        index += 1;
        return item;
    }
}

11. printDeque()

public void printDeque() {
    if (size == 0) {
        return;
    }
    StringBuilder res = new StringBuilder("");
    for (T e: this) {
        res.append(e + " ");
    }
    System.out.println(res);
    System.out.println("");
}

12. equals

public boolean equals(Object o) {
    if (o.getClass() != this.getClass()) {
        return false;
    }
    if (o == this) {
        return true;
    }

    // 一个新的问题
    LinkedListDeque linko = (LinkedListDeque) o;
    if (linko.size() != size) {
        return false;
    }

    for(int i = 0; i < size; i += 1) {
        if (!get(i).equals(linko.get(i))) {
            return false;
        }
    }
    return true;
}