【数据结构与算法】背包、队列和栈

前言

集合类数据类型的实现

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);
        }
    }
}
posted @ 2022-07-02 15:43  Tod4  阅读(59)  评论(0)    收藏  举报