【数据结构与算法】背包、队列和栈
前言
集合类数据类型的实现
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号