【数据结构与算法】背包、队列和栈
前言
集合类数据类型的实现
1 栈
1.1 定容栈
/**
 * 定容栈的实现
 *
 */
public class FixedCapacityStackOfStrings {
    private String[] a;
    private int N = 0;
    public FixedCapacityStackOfStrings(int capacity) {
        a = new String[capacity];
    }
    public int size() {
        return N;
    }
    public boolean isEmpty() {
        return N == 0;
    }
    public void push(String item) {
        a[N++] = item;
    }
    public String pop() {
        return a[--N];
    }
    // 测试
    public static void main(String[] args) {
        String str = "to be or not to - be - - that - - - is";
        String expect = "to be not that or be ";
        String result = "";
        FixedCapacityStackOfStrings s = new FixedCapacityStackOfStrings(100);
        for(String item : str.split(" ")) {
            if(!"-".equals(item)) {
                s.push(item);
            }
            else if(!s.isEmpty()) {
                result += s.pop() + " ";
            }
        }
        System.out.println(expect.equals(result));
    }
}
1.2 泛型定容栈
/**
 *  泛型定容栈
 */
public class FixedCapacityStack<Item> {
    private Item[] a;
    private int N = 0;
    public FixedCapacityStack(int capacity) {
        // 不能使用泛型数组
        a = (Item[]) new Object[capacity];
    }
    public boolean isEmpty() {
        return N == 0;
    }
    public Item pop() {
        return a[--N];
    }
    public void push(Item item) {
        a[N++] = item;
    }
    public int size() {
        return N;
    }
    // 测试
    public static void main(String[] args) {
        String str = "to be or not to - be - - that - - - is";
        String expect = "to be not that or be ";
        String result = "";
        FixedCapacityStack<String> s = new FixedCapacityStack<>(100);
        for(String item : str.split(" ")) {
            if(!"-".equals(item)) {
                s.push(item);
            }
            else if(!s.isEmpty()) {
                result += s.pop() + " ";
            }
        }
        System.out.println(expect.equals(result));
    }
}
需要注意的是,上面代码中泛型定容栈的初始化使用的是:
a = (Item[]) new Object[capacity];
而不是:
a = new Item[capacity];
这是由于创建泛型数组在Java中是不允许的
1.3 调整数组的大小:实现Stack API动态改变数组大小
选择用数组表示栈内容意味着用例必须预先估计栈的最大容量。在Java中,数组一旦被声明长度就不能够再进行更改了,选择大容量的用例在栈为空时会造成内存的极大浪费。同时在push的时候还需要考虑栈满的情况,综合上面两点,书中提供了一种解决方法:即使用一个大小不同的数组对栈进行转移:
    public void resize(int max) {
        Item[] temp = (Item[]) new Object[max];
        for(int i=0;i<N;i++) {
            temp[i] = a[i];
        }
        a = temp;
    }
如此一来,就可以在push操作之前和pop操作之后判断栈的容量,以动态改变栈数组的大小。
    public void push(Item item) {
        if(N == a.length)
            resize(a.length * 2);
        a[N++] = item;
    }
    public Item pop() {
        Item item = a[--N];
        // 防止对象游离
        a[N] = null;
        // N=0不必再进行长度缩减
        if(N > 0 && N <= a.length/4)
            resize(a.length / 2);
        return item;
    }
这里解决了栈满的判定问题,但是栈空pop时还需要进行判定
对象游离:
Java的垃圾回收策略是回收所有无法被访问到的对象的内存;但是像栈被pop出的元素,它是永远不可能被访问到的了,但是仍然存在于内存,但是Java的垃圾回收器没有办法知道这一点,因此需要通过引用覆盖的方式告知:
a[N] = null
1.4 实现Stack API的迭代
Java集合类型数据类型的基本操作之一就是能够通过for-each语句进行遍历元素,原因是因为Collection类实现了Iterable接口:
public interface Collection<E> extends Iterable<E>
Iterable接口维护了一个Iterator迭代器接口,接口方法用于定义迭代的规则,迭代器接口方法包括:
public interface Iterator<E> {
    boolean hasNext();
    E next();
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}
因此要实现Stack API的迭代,需要API implement Iterable<Item>和添加它的方法iterator:
    @Override
    public Iterator iterator() {
        return new ReverseArrayIterator();
    }
ReverseArrayIterator实现了Iterator接口,定义了迭代需要的hashNext和next接口方法:
    private class ReverseArrayIterator implements Iterator<Item> {
        private int i = N;
        @Override
        public boolean hasNext() {
            return i > 0;
        }
        @Override
        public Item next() {
            return a[--i];
        }
        @Override
        public void remove() {
        }
    }
ReverseArrayIterator为定义在Stack的内部类
1.5 综合:一个能够动态调整数组大小并且能够进行迭代的Stack API
package Algorithms.One;
import java.util.Collection;
import java.util.Iterator;
public class IterableStack<Item> implements Iterable<Item>{
    private Item[] a;
    //默认初始化为0
    private int N;
    public void resize(int max) {
        Item[] temp = (Item[]) new Object[max];
        for(int i=0;i<N;i++) {
            temp[i] = a[i];
        }
        a = temp;
    }
    public IterableStack(int capacity) {
        a = (Item[]) new Object[capacity];
    }
    public void push(Item item) {
        if(N == a.length)
            resize(a.length * 2);
        a[N++] = item;
    }
    public Item pop() {
        Item item = a[--N];
        // 防止对象游离
        a[N] = null;
        // N=0不必再进行长度缩减
        if(N > 0 && N <= a.length/4)
            resize(a.length / 2);
        return item;
    }
    public boolean isEmpty() {
        return N == 0;
    }
    @Override
    public Iterator iterator() {
        return new ReverseArrayIterator();
    }
    private class ReverseArrayIterator implements Iterator<Item> {
        private int i = N;
        @Override
        public boolean hasNext() {
            return i > 0;
        }
        @Override
        public Item next() {
            return a[--i];
        }
        @Override
        public void remove() {
        }
    }
    public static void main(String[] args) {
        String str = "to be or not to - be - - that - - - is";
        String expect = "to be not that or be ";
        String result = "";
        IterableStack<String> s = new IterableStack<>(100);
        for(String item : str.split(" ")) {
            if(!"-".equals(item)) {
                s.push(item);
            }
            else if(!s.isEmpty()) {
                result += s.pop() + " ";
            }
        }
        System.out.println(expect.equals(result));
        Iterator iterator = s.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        for (String str2 : s) {
            System.out.println(str2);
        }
    }
}
2 链表
链表是一种递归的线性数据结构,它或为空(null),或者是指向一个节点(node)的引用,该节点包含一个泛型的元素和一个指向另一条链表的引用。
public class LinkedList<Item> {
    private class Node {
        Item item;
        Node next;
    }
}
private Node first;
在需要使用Node类的类中将其定义为私有的内部类,因为它不是为用例准备的(书中这句话我的理解是它的数据类型随用例类LinkedList变化而不是自己能够决定,每个用例都可以这么写,不是单独为用每个例准备的)
2.1 在表头插入节点
    /**
     * 在表头插入节点
     * @param item
     */
    private void addOnHead(Item item) {
        Node temp = new Node();
        temp.item = item;
        temp.next = first.next;
        first = temp;
    }
2.2 从表头删除节点
    /**
     * 在表头删除节点
     * @return
     */
    private boolean deleteAtHead() {
        // 没有节点删除失败
        if(first == null)
            return false;
        // 有一个节点,将链表置空
        if(first.next == null)
            first = null;
        // 两个以上节点的情况
        else
            first.next = first.next.next;
        return true;
    }
2.3 在表尾插入节点
    /**
     * 尾插法
     * @param item
     */
    public void insertOnTail(Item item) {
       Node temp = new Node();
       temp.item = item;
       Node tail = first;
       if(tail==null) {
            tail = temp;
       }
       while(tail.next!=null) {
           tail = tail.next;
       }
       tail.next = temp;
    }
2.5 链栈LinkedStack的实现
链栈即为使用链表实现的栈,栈的顶部即为表头,实例变量first指向栈顶,这样当使用push的时候将元素添加到表头,pop的时候将表头元素删除即可,使用一个变量N计数元素个数,并且实现了Iterable使得链栈可以使用for-each进行遍历,链表的使用完美达到了栈的最优设计目标:
① 可以存储任意数据类型的数据
② 所需的空间总是和集合大小成正比,且大小不被限制(硬件允许的条件下)
③ 操作所需时间和集合大小无关
④ 可以进行for-each遍历
public class LinkedStack<Item> implements Iterable<Item> {
    private class Node {
        Item item;
        Node next;
    }
    private Node first;
    private int N;
    public void push(Item item) {
        Node oldFirst = first;
        first = new Node();
        first.item = item;
        first.next = oldFirst;
        N++;
    }
    public Item pop() {
        if(first == null)
            return null;
        Item item = first.item;
        first = first.next;
        N--;
        return item;
    }
    public int size() {
        return N;
    }
    public boolean isEmpty() {
        return N == 0;
    }
    @Override
    public Iterator<Item> iterator() {
        return new LinkedStackIterator();
    }
    private class LinkedStackIterator implements Iterator<Item>{
        private int i = N;
        @Override
        public boolean hasNext() {
            return i > 0;
        }
        @Override
        public Item next() {
            Node temp = first;
            for(int j=0;j<i-1;j++)
                temp = temp.next;
            i--;
            return temp.item;
        }
    }
    // 测试
    public static void main(String[] args) {
        LinkedStack<Integer> stack = new LinkedStack<>();
        for (int i=0;i<10;i++)
            stack.push(i);
        System.out.println(stack.pop());
        for(int val : stack) {
            System.out.print(val);
        }
    }
}
2.6 链式队列LinkedQueue的实现
将队列表示为一条最早插入到最近插入的链表,实例变量first指向队列的队首,tail指向队尾,入队enQueue将添加元素添加到表尾,出队deQueue操作将元素添加到表首。
package Algorithms.One;
import java.util.Iterator;
public class LinkedQueue<Item> implements Iterable<Item> {
    private class Node {
        Item item;
        Node next;
    }
    private int N;
    private Node first;
    private Node tail;
    // 表尾入队
    public void enQueue(Item item) {
        Node oldTail = tail;
        tail = new Node();
        tail.item = item;
        if(N == 0)
            first = tail;
        else
            oldTail.next = tail;
        N++;
    }
    // 表首出队
    public Item deQueue() {
        if(first == null)
            return null;
        Item item = first.item;
        first = first.next;
        if(isEmpty()) tail=null;
        N--;
        return item;
    }
    public boolean isEmpty() {
        return N == 0;
    }
    public int size() {
        return N;
    }
    private void printAll() {
        Node temp = first;
        while(first!=null) {
            System.out.println(first.item);
            first = first.next;
        }
    }
    @Override
    public Iterator<Item> iterator() {
        return new LinkedQueueIterator();
    }
    private class LinkedQueueIterator implements Iterator<Item> {
        private int i = 0;
        @Override
        public boolean hasNext() {
            return i<N;
        }
        @Override
        public Item next() {
            Node temp = first;
            for(int j=0;j<i;j++)
                temp = temp.next;
            i++;
            return temp.item;
        }
    }
    public static void main(String[] args) {
        LinkedQueue<Integer> queue = new LinkedQueue<>();
        for(int i=0;i<10;i++)
            queue.enQueue(i);
        for(int var:queue) {
            System.out.print(var);
        }
    }
}
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号