队列
1.数组实现
import java.util.Iterator;
import java.util.NoSuchElementException;
public class HeapSort<Item> implements Iterable<Item> {
private Item[] arr; // queue elements
private int N; // number of elements on queue
private int first; // 队列第一个元素的索引
private int last; // 下一个可用插槽的索引
/**
* Initializes an empty queue.
*/
public HeapSort() {
arr = (Item[]) new Object[1];
N = 0;
first = 0;
last = 0;
}
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
private void resize(int capacity) {
Item[] temp = (Item[]) new Object[capacity];
for (int i = 0; i < N; i++) { //若此时N=1,那么仅for循环一次
temp[i] = arr[(first + i) % arr.length];//这边用first是因为,firt永远指向
// 下一个要先被删除的元素,即意味着first指向的元素以及该元素后面的元素都十分重要,很可能还没被取出。
// 而firt+i,能够保证原始数组中的下一个要被删除(取出)的元素放在新数组的第一个,根据i的变化,
// first所指向的位置后面的元素依次往后存放于新数组中
}
arr = temp;
first = 0;//由于旧数组的first很可能不是指向的0,因此要置为0 ,因为创建了新数组,下一个要被取出的元素在新数组的索引为0处
last = N;//last=N,很关键,last永远指向下一个元素被存放的地方,因此,他要找一个空位,即索引为N处,即有几个元素,就存放在
// 索引为几处,如旧数组将两个元素转移到了新数组中(arr[0],arr[1]),那么first指向0,last指向2处——arr[2]的位置
}
public void enqueue(Item item) {
if (N == arr.length) resize(2*arr.length);
arr[last++] = item;
if (last == arr.length) last = 0; //一旦发现元素已填充满数组,立马将last置为0
N++;//注意,不是栈了,栈中是arr[N++],因此,这里别忘了N++,上面也只能使用last==arr.length
}
public Item dequeue() {
if (isEmpty()) throw new NoSuchElementException("Queue underflow");
Item item = arr[first];
arr[first] = null; // to avoid loitering
N--; //注意N--得在 if(N > 0 && N == arr.length/4)语句的前面
first++;
if (first == arr.length) first = 0; //表明,数组元素已经被删完了一轮了,此时,first重置为0,因为
// 在索引0处又存放了新进来的一些元素(出现的情况(画图)),比如数组长度为4,先添加4个元素,然后删掉两个,
// 此时,last=0,first=2,再添加两个,此时last=2,first=3,再删除两个,此时first=0,last=2)
// shrink size of array if necessary
if (N > 0 && N == arr.length/4) resize(arr.length/2); //这两个if的语句顺序也不能对调,注意一下,因为会影响到arr.length
return item;
}
public Item peek() {
if (isEmpty()) throw new NoSuchElementException("Queue underflow");
return arr[first];
}
/**
* Returns an iterator that iterates over the items in this queue in FIFO order.
* @return an iterator that iterates over the items in this queue in FIFO order
*/
public Iterator<Item> iterator() {
return new ArrayIterator();
}
// an iterator, doesn't implement remove() since it's optional
private class ArrayIterator implements Iterator<Item> {
private int i = 0;
public boolean hasNext() { return i < N; }
//return i < N 等价于 N > 0
public void remove() { throw new UnsupportedOperationException(); }
public Item next() {
if (!hasNext()) throw new NoSuchElementException();
Item item = arr[(i + first) % arr.length];//和上面的for循环一样,为啥要%arr.length呢?
//因为假设数组长度为4,first指向3,所以,item[0]=arr[3];
//然后i++,此时item[1]=arr[4%4]=arr[0];若没有%arr.length,arr[4]就数组角标越界了
//因此,%arr.length是为了能够循环遍历arr数组
i++;
return item;
}
}
}
原理图解:
2.链表实现
import java.util.Iterator;
import java.util.NoSuchElementException;
public class Queue<Item> implements Iterable<Item> {
private Node<Item> first; // beginning of queue
private Node<Item> last; // end of queue
private int N; // number of elements on queue
private static class Node<Item> {
private Item item;
private Node<Item> next;
}
public Queue() {
first = null;
last = null;
N = 0;
}
public boolean isEmpty() {
return first == null;
}
public int size() {
return N;
}
public Item peek() {
if (isEmpty()) throw new NoSuchElementException("Queue underflow");
return first.item;
}
//表头删除元素
public void enqueue(Item item) {
Node<Item> oldlast = last;
last = new Node<Item>();
last.item = item;
last.next = null;
if (isEmpty()) first = last; //如果刚开始添加元素,显然,last为null
// 因此oldlast也为null。但last=new Node<Item>(),所以if语句上面的代码都OK,不会空引用异常
// 但oldlast.next会报空引用以长,因为oldlast=null。与此同时first也为null,因此first=last,
// 让这两个指针同时指向新加入的元素
else oldlast.next = last;
N++;
}
//表尾添加元素
public Item dequeue() {
if (isEmpty()) throw new NoSuchElementException("Queue underflow");
Item item = first.item;
first = first.next; //如果正好把最后一个元素删了,那么first.next=null,所以fisrt=null.
N--;
if (isEmpty()) last = null; // to avoid loitering,尽管first=null,但last还指向最后一个被删掉的元素
// 因此要手动置为null,这里可以画一下图,一眼明了。
return item;
}
/**
* Returns a string representation of this queue.
*
* @return the sequence of items in FIFO order, separated by spaces
*/
public String toString() {
StringBuilder s = new StringBuilder();
for (Item item : this) {
s.append(item);
s.append(' ');
}
return s.toString();
}
/**
* Returns an iterator that iterates over the items in this queue in FIFO order.
*
* @return an iterator that iterates over the items in this queue in FIFO order
*/
public Iterator<Item> iterator() {
return new ListIterator(first);
}
// an iterator, doesn't implement remove() since it's optional
private class ListIterator implements Iterator<Item> { //从头开始遍历,和栈一模一样
private Node<Item> current;
public ListIterator(Node<Item> first) {
current = first;
}
public boolean hasNext() { return current != null; }
public void remove() { throw new UnsupportedOperationException(); }
public Item next() {
if (!hasNext()) throw new NoSuchElementException();
Item item = current.item;
current = current.next; //这样我current.next不会影响到first指针的移动
return item;
}
}
}
栈
1.数组实现
字符串定容栈
import java.util.Iterator;
public class FixedCapacityStackOfStrings implements Iterable<String> {
private String[] a; // holds the items
private int N; // number of items in stack
public FixedCapacityStackOfStrings(int capacity) {
a = new String[capacity];
N = 0;
}
public boolean isEmpty() { return N == 0; }
public boolean isFull() { return N == a.length; }
//比如添加第一个元素,该元素位置显然在a[0]处,此时N也应该等于1.
public void push(String item) { a[N++] = item; }
//先--N,再返回是因为a[N--]这个元素是不存在的,比如我要删除a[4],此时N肯定为5,那么a[--N]完美契合。
public String pop() { return a[--N]; }
public String peek() { return a[N-1]; }
//对于自定义可迭代的数据类型,类要实现接口Iterable,还要实现该接口的方法Iterator<Item> iterator();
public Iterator<String> iterator() { return new ReverseArrayIterator(); }
private class ReverseArrayIterator implements Iterator<String> {
private int i = N;//这边新建变量i是为了避免在遍历时,a[--N]改变N的值,这是不允许发生的。
public boolean hasNext() { return i > 0; }
public String next() { return a[--i]; }
public void remove() {
throw new UnsupportedOperationException();
}
}
}
注:在构造方法里面对数组长度进行定义是十分普遍的做法,这样在创建一个栈时就可以指定它的大小。
下面是一张很经典的栈应用实例图:
从图中不难看出,对象会游离,即pop的对象其实还存在在数组中,但因为N的限制,导致访问不到游离的对象,必须对游离的对象置为null。
泛型定容字符串
import java.util.Iterator;
public class FixedCapacityStack<Item> implements Iterable<Item> {
private Item[] a; // holds the items
private int N; // number of items in stack
public FixedCapacityStack(int capacity) {
a = (Item[]) new Object[capacity];//注意,泛型数组的创建方式
N = 0;
}
public boolean isEmpty() { return N == 0; }
public void push(Item item) { a[N++] = item; }
public Item pop() { return a[--N]; }
public Iterator<Item> iterator() { return new ReverseArrayIterator(); }
private class ReverseArrayIterator implements Iterator<Item> {
private int i = N;
public boolean hasNext() { return i > 0; }
public Item next() { return a[--i]; }
public void remove() {
throw new UnsupportedOperationException();
}
}
}
下压(LIFO)栈(长度可调)
最终版本:
import java.util.Iterator;
public class ResizingArrayStack<Item> implements Iterable<Item> {
private int N=0;
private Item[] a=(Item[])new Object[1];//这边两个赋值可以放到构造函数中,除此之外,要注意这边不是(Item)new Object[1];
public boolean isEmpty() {
return N==0;
}
public int size() {
return N;
}
public void push(Item item) {
if(N==a.length) //在添加新元素前判断数组长度是否不够。
resize(a.length*2);
a[N++]=item;
}
public Item pop() {
if (isEmpty()) throw new NoSuchElementException("Stack underflow");
Item item= a[--N]; //比如N=5,我删掉了a[4],那么a[4]就游离了,此时N=4,这边a[--N]等价于a[N-1]
a[N]=null; //防止对象游离,如果上面是a[N-1],那么这边a[N-1]=null,且在后面得加上N--;
//特别注意,如果Item类型是int的话,这边不能置为null,int型的元素无法被置为null,他们默认为0。
if(N>0 && N==a.length/4) //删完了之后发现N浪费太多了,此时再缩小数组长度
resize(a.length/2);//一个4,一个2,顺序不要搞反了
return item; //返回的item就是被置为null的值。
}
public void resize(int len) {
Item[] temp=(Item[])new Object[len];
for(int i=0;i<N;i++) {//这边i<N,而不是len!!!
// 如果i<len,数组无论变大还是变小,都会多遍历一半,浪费时间
temp[i]=a[i];
}
a=temp; //把加长版新数组再次赋给a!!!
}
public Item peek() {
if (isEmpty()) throw new NoSuchElementException("Stack underflow");
return a[N-1];
}
public Iterator<Item> iterator(){
return new ReverseArrayIterator();//Itertor是一个接口,因此,我返回的对象一定要继承该接口
}
private class ReverseArrayIterator implements Iterator<Item>{
private int i=N;//也可以放到构造函数中进行赋值
/*public ReverseArrayIterator(){
i=N;*/
public boolean hasNext() {
return i>0;
}
public Item next() {
if (!hasNext()) throw new NoSuchElementException();
return a[--i];
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}
注:
- 为什么N=a.length/4,因为如果a.length/2==N时resize数组长度,那么我push一下,又要resize,pop一下,又要resize,这样会频繁的调整数组大小(中间会频繁的创建数组,浪费时间)
自从数组长度可调以及避免了对象游离之后,示意图如下:
为什么要逆序迭代遍历数组(ReverseArrayIterator)
因为上述代码是堆栈,先进后出,后进先出,符合栈的元素存取顺序。
2.链表实现
下压栈
原理图,遵循FILO(LIFO)原则。这张图的思想就是链表实现栈的思想,十分重要!
栈的元素添加和删除操作都在链表的表头执行,先进先出。
代码:
/*
* 链表实现
*/
import java.util.Iterator;
import java.util.NoSuchElementException;
public class Stack<Item> implements Iterable<Item> {
private Node<Item> first; // top of stack
private int N; // size of the stack
// helper linked list class
private static class Node<Item> {
private Item item;
private Node<Item> next;
}
public Stack() {
first = null;
N = 0;
}
public boolean isEmpty() {
return first == null;
}
public int size() {
return N;
}
//表头添加
public void push(Item item) {
Node<Item> oldfirst = first;
first = new Node<Item>();
first.item = item;
first.next = oldfirst;
N++;
}
/**
* Removes and returns the item most recently added to this stack.
*/
//表头删除
public Item pop() {
if (isEmpty()) throw new NoSuchElementException("Stack underflow");
Item item = first.item; // save item to return
first = first.next; // delete first node
N--;
return item; // return the saved item
}
/**
* Returns (but does not remove) the item most recently added to this stack.
*/
public Item peek() {
if (isEmpty()) throw new NoSuchElementException("Stack underflow");
return first.item;
}
/**
* Returns a string representation of this stack.
*
* @return the sequence of items in this stack in LIFO order, separated by spaces
*/
public String toString() {
StringBuilder s = new StringBuilder();
for (Item item : this) {
s.append(item);
s.append(' ');
}
return s.toString();
}
/**
* Returns an iterator to this stack that iterates through the items in LIFO order.
*
* @return an iterator to this stack that iterates through the items in LIFO order
*/
public Iterator<Item> iterator() {
return new ListIterator(first);
}
// an iterator, doesn't implement remove() since it's optional
private class ListIterator implements Iterator<Item> {
private Node<Item> current;
public ListIterator(Node<Item> first) {
current = first;
}
public boolean hasNext() {
return current != null;
}
public void remove() {
throw new UnsupportedOperationException();
}
public Item next() {
if (!hasNext()) throw new NoSuchElementException();
Item item = current.item;
current = current.next;
return item;
}
}
}
背包
1.数组实现
import java.util.Iterator;
public class BagArray<Item> implements Iterable<Item> {
private int N;
private Item[] a;
public BagArray() {
N = 0;
a = (Item[]) new Object[1];
}
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
public void push(Item item) {
if (N == a.length) // 在添加新元素前判断数组长度是否不够。
resize(a.length * 2);
a[N++] = item;
}
public void resize(int len) {
Item[] temp = (Item[]) new Object[len];
for (int i = 0; i < len; i++) {
temp[i] = a[i];
}
a = temp; // 把加长版新数组再次赋给a!!!
}
public Iterator<Item> iterator() {
return new ArrayIterator(); //不用再逆序遍历了。
}
private class ArrayIterator implements Iterator<Item> {
private int i = 0;
public boolean hasNext() {
return N > i;//等价于N > 0;
}
public Item next() {
return a[i++];
}
public void remove() {
}
}
}
2.链表实现
import java.util.Iterator;
import java.util.NoSuchElementException;
public class Bag<Item> implements Iterable<Item> {
private Node<Item> first; // beginning of bag
private int N; // number of elements in bag
private static class Node<Item> {
private Item item;
private Node<Item> next;
}
public Bag() {
first = null;
N = 0;
}
public boolean isEmpty() {
return first == null;
}
public int size() {
return N;
}
public void add(Item item) {
Node<Item> oldfirst = first;
first = new Node<Item>();
first.item = item;
first.next = oldfirst;
N++;
}
public Iterator<Item> iterator() {
return new ListIterator(first);
}
// an iterator, doesn't implement remove() since it's optional
private class ListIterator implements Iterator<Item> {
private Node<Item> current;
public ListIterator(Node<Item> first) {
current = first;
}
public boolean hasNext() {
return current != null;
}
public void remove() {
throw new UnsupportedOperationException();
}
public Item next() {
if (!hasNext())
throw new NoSuchElementException();
Item item = current.item;
current = current.next;
return item;
}
}
}