数据结构:“包”的学习与数组实现和链式实现,java语言描述。

前言

包是一个抽象数据类型,许多java数据结构书籍都拿其当做入门数据结构(如:《算法(第四版)》,《数据结构与抽象(JAVA语言描述)》等)。阅读此篇文章需要java基础知识(简单语法,泛型及基本类库的使用)

定义:

包这一抽象概念与生活中的包极其类似,允许其中放入重复的事物,而且我们不关心其中的次序。

// 简单说,就是可重复,不关心顺序。

注:常用的 foreach 中就隐含着包的思想,遍历时:不关心遍历的顺序,也不在乎其中的内容,只需要遍历即可。

标准接口

先有接口后有实现,包自然有属于自己的接口

//一个包应该实现这个借口(没有使用块状注释,节省版面。)
public interface BagInterface<T> {
    // 给包内新增一个元素
    public boolean add(T newEntry);
    // 得到当前包内的东西的个数
    public int getCurrentSize();
    // 包内是否为空
    public boolean isEmpty();
    // 随机移除一个东西
    public T remove();
    // 移除一个指定元素(只移除一个即可)
    public boolean remove(T anEntry);
    // 清楚包内事物
    public void clear();
    // 得到某个物品在包内的个数
    public int getFrequencyOf(T anEntry);
    // 包内是否拥有这个物品
    public boolean contains(T anEntry);
    // 将包内的物品们转化为数组并返回
    public T[] toArray();
}

实现

静态数组实现

静态数组实现的bag中,字段应该含有一个泛型数组存储事物,一个number记载物品个数方便操作。

而且,静态数组一定有容量上限,所以我们应该提供两种构造方法,一种是默认的无参构造方法,容量为默认规定的常量。所以这就需要我们定义一个int常量为默认。其次,提供一种可以由调用者决定容量的有参构造方法。

所以ArrayBag的字段和构造方法如下

public class ArrayBag<T> implements BagInterface<T> {
    // 当前包内物品的个数
    private int numberOfEntries;
    // 存储内容的数组
    private final T[] bag;
    // 在程序设计时,不可以出现魔法值,所以需要命名一个常量
    private static final int DEFAULT_CAPACITY = 25;
    /**
     * 写构造方法,一个默认的,一个手动输入容量
     */
    @SuppressWarnings("unchecked")   // 压制警告:泛型创建数组必然有警告
    public ArrayBagTest(int capacity) {
        super();
        this.numberOfEntries = 0;
        this.bag = (T[]) new Object[capacity];
    }
    public ArrayBagTest() {
        this(DEFAULT_SIZE);    // 使用this调用其他构造方法
    }
}

 接下来实现add()方法,因为其他方法都是建立在数据上实现的。

    @Override
    public boolean add(T newEntry) {
        boolean result = true;
        if (isArrayFull()) { //使用一个方法增强自注释能力
            result = false;
        } else {
            bag[numberOfEntries] = newEntry;    // 添加结点
            numberOfEntries++;            // number增加
        }
        return result;
    }  
    private boolean isArrayFull() {// 判断是否已经不能再添加了
        return numberOfEntries >= bag.length;
    }

 接下来实现getCurrentSize()和isEmpty()

    @Override
    public int getCurrentSize() {
        return numberOfEntries;
    }
    @Override
    public boolean isEmpty() {
        return numberOfEntries == 0;
    }

然后实现清理clear()和各种remove方法


    @Override
    public T remove() {
        return removeEntry(numberOfEntries);
    }
    @Override
    public boolean remove(T anEntry) {
        int index = getIndexof(anEntry);
        T result = removeEntry(index);
        return anEntry.equals(result);
    }
    private T removeEntry(int index) {
        T result = null;
        if (!isEmpty() && (index >= 0)) {
            result = bag[index];
            bag[index] = bag[numberOfEntries - 1];

            bag[numberOfEntries - 1] = null;
            numberOfEntries--;
        }
        return result;
    }
    private int getIndexof(T anEntry) {
        int where = -1;
        boolean found = false;
        int index = 0;
        while (!found && (index < numberOfEntries)) {
            if (anEntry.equals(bag[index])) {
                found = true;
            }
            index++;
        }
        return where;
    }
    @Override
    public void clear() {
        while (!isEmpty()) {
            remove();
        }
    }

之后再将其他不太重要的方法补全即可。

import java.util.Arrays;

/**
 * Description: 使用数组实现bag
 * @ClassName: ArrayBag
 * @author 过道
 * @date 2018年8月7日 上午9:17:41
 * @version V1.0
 * 
 */
public class ArrayBag<T> implements BagInterface<T> {

    // 当前包内物品的个数
    private int numberOfEntries;

    private final T[] bag;
    // 在程序设计时,不可以出现魔法值:即未定义的常量如:{“这是一个魔法值”,234,87981.2123},以上常量都是魔法值
    private static final int DEFAULT_CAPACITY = 25;

    public ArrayBag(int capacity) {
        this.numberOfEntries = capacity;
        @SuppressWarnings("unchecked")
        T[] tempBag = (T[]) new Object[capacity];
        bag = tempBag;
        numberOfEntries = 0;
    }

    public ArrayBag() {
        this(DEFAULT_CAPACITY);
    }

    @Override
    public boolean add(T newEntry) {
        boolean result = true;
        if (isArrayFull()) {
            result = false;
        } else {
            bag[numberOfEntries] = newEntry;
            numberOfEntries++;
        }
        return result;
    }
    private boolean isArrayFull() {
        return numberOfEntries >= bag.length;
    }

    @Override
    public int getCurrentSize() {
        return numberOfEntries;
    }

    @Override
    public boolean isEmpty() {
        return numberOfEntries == 0;
    }
    @Override
    public T remove() {
        return removeEntry(numberOfEntries);
    }
    @Override
    public boolean remove(T anEntry) {
        int index = getIndexof(anEntry);
        T result = removeEntry(index);
        return anEntry.equals(result);
    }
    private T removeEntry(int index) {
        T result = null;
        if (!isEmpty() && (index >= 0)) {
            result = bag[index];
            bag[index] = bag[numberOfEntries - 1];

            bag[numberOfEntries - 1] = null;
            numberOfEntries--;
        }
        return result;
    }

    private int getIndexof(T anEntry) {
        int where = -1;
        boolean found = false;
        int index = 0;
        while (!found && (index < numberOfEntries)) {
            if (anEntry.equals(bag[index])) {
                found = true;
            }
            index++;
        }
        return where;
    }

    @Override
    public void clear() {
        while (!isEmpty()) {
            remove();
        }
    }

    @Override
    public int getFrequencyOf(T anEntry) {
        int counter = 0;
        for (int i = 0; i < numberOfEntries; i++) {
            if (anEntry.equals(bag[i])) {
                counter++;
            }
        }
        return counter;
    }

    @Override
    public boolean contains(T anEntry) {
        return getIndexof(anEntry) > -1;
    }

    @Override
    public T[] toArray() {
        return Arrays.copyOf(bag, numberOfEntries);
    }
}

动态数组实现

所谓动态,即没有容量限制,但是数组一定有容量限制,所以我们使用扩容数组。

当新添加时,发现现在正在使用的数组已经满了,那么就先进行扩容操作,之后再进行添加,以防万一,我们添加一个最大容量,一旦扩容超过我们规定的最大容量,就抛出异常。

//// 以下建立在静态数组实现的基础上进行改动的部分
 // 在程序设计时,不可以出现魔法值:即未定义的常量如:{“这是一个魔法值”,234,87981.2123},以上常量都是魔法值
    private static final int DEFAULT_CAPACITY = 25;

    private static final int MAX_CAPACITY = 1600;

    @Override
    public boolean add(T newEntry) {
        boolean result = true;
        if (isArrayFull()) {
            doubleCapacity();
        }
        bag[numberOfEntries] = newEntry;
        numberOfEntries++;
        return result;
    }
    private void doubleCapacity() {
        int newLength = 2 * bag.length;
        checkCapacity(newLength);
        bag = Arrays.copyOf(bag, newLength);
    }

    private void checkCapacity(int capacity) {
           if(capacity > MAX_CAPACITY) {
               throw new IllegalStateException();
           }
    }
    private boolean isArrayFull() {
        return numberOfEntries >= bag.length;
    }

链表实现

 链表实现,主要是注意以下链表如何定义。

    /**
     * 
     * Description: 两个域,一个指向数据,另一个指向下一个结点的位置。
     * 
     * @ClassName: Node
     * @author 过道
     * @date 2018年8月10日 下午8:53:48
     * @version V1.0
     *
     */
    private class Node<T> {
        private T data;
        private Node next;
        // 空链表时,向链表中加入数据。
        public Node(T dataPortion) {
            this(dataPortion, null);
        }
        // 链表中有值,加入数据
        public Node(T data, Node next) {
            super();
            this.data = data;
            this.next = next;
        }
    }

 

package unit02_bag;

public final class LinkedBag<T> implements BagInterface<T> {
    private Node firstNode;
    private int numberOfEntries;

    public LinkedBag() {
        this.firstNode = null;
        this.numberOfEntries = 0;
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean add(T newEntry) {
        Node newNode = new Node(newEntry);
        newNode.next = firstNode;

        firstNode = newNode;
        numberOfEntries++;
        // 对于链式存储,包不会满。
        return true;
    }

    @Override
    public int getCurrentSize() {
        return numberOfEntries;
    }

    @Override
    public boolean isEmpty() {
        return numberOfEntries == 0;
    }

    @Override
    public T remove() {
        T result = null;
        if (firstNode != null) {
            result = (T) firstNode.data;
            firstNode = firstNode.next;
            numberOfEntries--;
        }
        return result;
    }

    @Override
    public boolean remove(T anEntry) {
        boolean result = false;
        Node currentNode = firstNode;
        while (!result && currentNode != null) {
            if (currentNode.data.equals(anEntry)) {
                // 此处利用包的不关心顺序的性质,将第一位的数据放到待删除结点的位置,然后删掉第一位结点即可。
                currentNode.data = firstNode.data;
                remove();
                result = true;
            }
        }
        return result;
    }

    @Override
    public void clear() {
        while (!isEmpty()) {
            remove();
        }
    }

    @Override
    public int getFrequencyOf(T anEntry) {
        int frequency = 0; // 记录次数
        int loopCounter = 0; // 哨兵,进行记录,此处淡出为了保留逻辑完整性
        Node currentNode = firstNode;
        while ((loopCounter < numberOfEntries) && (currentNode != null)) {
            if (anEntry.equals(currentNode.data)) {
                frequency++;
            }
            loopCounter++;
            currentNode = currentNode.next;
        }
        return 0;
    }

    @Override
    public boolean contains(T anEntry) {
        boolean found = false;
        Node currentNode = firstNode;
        while (!found && currentNode != null) {
            if (currentNode.data.equals(anEntry)) {
                found = true;
            } else {
                currentNode = currentNode.next;
            }
        }
        return found;
    }

    @SuppressWarnings("unchecked")
    @Override
    public T[] toArray() {
        T[] result = (T[]) new Object[numberOfEntries];

        int index = 0;
        Node currentNode = firstNode;
        while ((index < numberOfEntries) && (currentNode != null)) {
            result[index] = (T) currentNode.data;
            index++;
            currentNode = currentNode.next;
        }
        return result;
    }

    private class Node<T> {
        private T data;
        private Node next;

        public Node(T dataPortion) {
            this(dataPortion, null);
        }

        public Node(T data, Node next) {
            super();
            this.data = data;
            this.next = next;
        }
    }
}

 

 

 

 

posted @ 2018-08-08 19:46  过道  阅读(244)  评论(0编辑  收藏  举报