四、栈:一种受限的线性结构(只能在一端进行插入、删除)。
1、特点:先进后出
2、栈的实现包含:顺序栈和链栈。主要操作:入栈和出栈
1、基于数组的顺讯栈
/*
* 基于数组实现的顺序栈
*/
public class ArrayStack {
private int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 栈的最大容量
private int DEFAULT_CAPACITY = 10;// 默认栈容量
private int top = -1;// 栈顶指针
private int size = 0;// 记录栈的大小【top和size保留一个就行了】
private Object element[];// 存放栈内元素
public ArrayStack() {
element = new Object[DEFAULT_CAPACITY];
}
// 创建指定大小的栈
public ArrayStack(int capacity) {
element = new Object[capacity];
}
// 入栈
public boolean push(Object data) {
// 先检查栈容量是否足够【再进入一个元素时,数组长度是否够?】
try {
checkCapacity(size + 1);
element[++top] = data;
size++;
return true;
} catch (Exception e) {
return false;
}
}
// 出栈
public Object pop() {
if (size <= 0) {
System.out.println("Stack is null");
return null;
}
size--;
return element[top--];
}
private void checkCapacity(int minCapacity) {
if (element.length - minCapacity < 0) {
throw new RuntimeException("栈容量不够");
}
}
// 获得栈的大小
public int size() {
return size;
}
public static void main(String[] args) {
ArrayStack stack = new ArrayStack();
for (int i = 0; i < 13; i++) {
boolean push = stack.push(i);
System.out.println("入栈元素" + i + " ,入栈结果为:" + push);
}
for (int i = 0; i < 11; i++) {
System.out.println(stack.pop());
}
System.out.println("现在栈大小为:" + stack.size);
}
}
2、基于链表的链式栈【单链表/双链表都可以】
/*
* 基于双链表实现的链式栈
*/
class Node {
Object data;
Node prev;// 前驱
Node next;// 后继
public Node(Node prev, Object data, Node next) {
this.data = data;
this.prev = prev;
this.next = next;
}
}
public class LinkedListStack {
private int size = 0;
private Node tail;// 一端进行插入、删除
public LinkedListStack() {
this.tail = null;
}
// 入栈
public void push(Object data) {
Node newNode = new Node(tail, data, null);
if (size > 0) {
tail.next = newNode;
}
tail = newNode;
size++;
}
// 出栈
public Object pop() {
if (size < 1) {
return null;
}
Object data = tail.data;
tail = tail.prev;// 删除前驱
if (tail != null) {
tail.next = null;// 删除后继
}
size--;
return data;
}
public int size() {
return size;
}
public static void printStack(LinkedListStack stack) {
if (stack.size() > 0) {
Node tail = stack.tail;
while (tail != null) {
System.out.print(tail.data + " ");
tail = tail.prev;
}
}
}
public static void main(String[] args) {
LinkedListStack stack = new LinkedListStack();
for (int i = 0; i < 10; i++) {
stack.push(i);
}
printStack(stack);
}
}
3、针对栈,Java提供了java.util.Stack类封装了各种操作细节。
1、Stack类常用操作:
初始化 Stack stack = new Stack();
入栈 push(E e)
出栈 pop()
获得栈顶元素 peek()
栈大小 size()
2、源码分析:
1)、Stack是用数组实现的顺序栈,底层支持动态扩容,扩容方法grow。
2)、Stack底层存储数据还是用到父类Vector,所以数组初始大小10,增量因子capacityIncrement=0,两倍扩容。
public class Stack<E> extends Vector<E> {
public Stack() {
}
//入栈
public E push(E item) {
addElement(item);//调用父类Vector的方法
return item;
}
public synchronized void addElement(E obj) {//线程安全
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
private void ensureCapacityHelper(int minCapacity) {
if (minCapacity - elementData.length > 0)
grow(minCapacity);//扩容
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//两倍扩容
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
五、队列:一种受限的线性结构,只能在一端插入,另一端删除。
1、特点:先进先出
2、主要操作:入队列、出队列。常见队列:循环队列、阻塞队列、并发队列。
1、基于数组实现的顺序队列:
public class ArrayQueue {
// 定义队列的结构
private Object elements[];// 存放队列元素
private int size = 0;// 记录队列大小
private int DEFAULT_SIZE = 10;// 默认初始容量
private int front;// 队头
private int rear;// 队尾
private int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 最大容量【借鉴ArrayList源码】
// 定义在结构上的操作
public ArrayQueue() {
elements = new Object[DEFAULT_SIZE];
front = -1;
rear = -1;
}
public ArrayQueue(int capacity) {
elements = new Object[capacity];
front = -1;
rear = -1;
}
// 入队列【扩容】
public boolean enqueue(Object data) {
// 先检查队列容量是否足够
try {
ensureSize();
elements[++rear] = data;
size++;
return true;
} catch (Exception e) {
return false;
}
}
// 出队列
public Object dequeue() {
if (front == rear) {
return null;
}
Object data = elements[++front];
size--;
return data;
}
// 打印队列元素
public void printQueue() {
for (int i = 0; i <= rear; i++) {
System.out.print(elements[i] + " ");
}
}
// 入队列前检查容量是否足够
private void ensureSize() {
if (rear == elements.length - 1) {// 不能再继续入队列了
if (front == -1) {// 队列从未删除元素
grow(size);// 直接扩容
} else {// 队列删除过元素,将所有元素搬到从下标0开始处【解决了假溢出!!!】
for (int i = front + 1; i <= rear; i++) {
elements[i - front - 1] = elements[i];
}
rear = rear - front - 1;
front = -1;
}
}
}
// 动态扩容
public void grow(int oldSize) {
int newSize = oldSize + (oldSize >> 1);// 1.5倍扩容
if (newSize - oldSize < 0) {
newSize = DEFAULT_SIZE;
}
elements = Arrays.copyOf(elements, newSize);
}
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue();
// for (int i = 0; i < 12; i++) {
// queue.enqueue(i + 1);
// }
for (int i = 0; i < 9; i++) {
queue.enqueue(i + 1);
}
queue.dequeue();// 1出队列
queue.enqueue(10);
queue.enqueue(20);
queue.printQueue();
}
}
2、基于链表实现的队列:
/*
* 基于单链表实现队列(双链表也可以)
*/
public class LinkedListQueue {
// 定义链表的结构
static class Node {
Object data;
Node next;
public Node(Object data) {
this.data = data;
}
}
// 定义队列的结构
private int size;// 队列中元素个数
private Node front;// 队头结点
private Node rear;// 队尾结点
public LinkedListQueue() {// 还可以添加setter/getter方法
front = null;
rear = null;
size = 0;
}
// 定义队列的操作【还可以添加获得队头、队尾元素、打印队列等操作加以完善】
// 入队【链表不需要扩容】
public void enqueue(Object element) {
Node node = new Node(element);
if (rear == null) {// 队尾为空,则第一个插入结点为队尾、队头结点
rear = node;
front = node;
size++;
return;
}
rear.next = node;
rear = node;
size++;
}
// 出队
public Object dequeue() {
if (front != null && front.next != null) {
Object element = front.data;
front = front.next;
size--;
return element;
}
if (front != null && front.next == null) {
Object element = front.data;
front = null;
size = 0;
System.out.println("这是最后一个元素了,不要在出队列了");
return element;
} else {
return null;
}
}
public static void main(String[] args) {
LinkedListQueue queue = new LinkedListQueue();
for (int i = 0; i < 10; i++) {
System.out.println("Enqueue--->" + (i + 1));
queue.enqueue(i + 1);
}
for (int i = 0; i < 10; i++) {
queue.dequeue();
}
System.out.println(queue.size);
}
}
3、队列的主要应用场景:用队列来存放等待处理的元素,这种场景一般用于缓冲、并发访问、实时消息通信、分布式消息队列等。
总结:对于在很短时间内可以处理的消息,不需要队列,直接阻塞即可。但是如果在消息处理时费时间,就可以引入队列,避免阻塞。
4、针对队列,Java提供了java.util.Queue接口:
Queue接口方法:(它继承自Collection接口)
add(E e) 插入一个元素到队列中,失败时返回IllegalStateException (队列容量不够)
offer(E e) 插入一个元素到队列中,失败时返回false
element() 返回队列头部的元素
peek() 返回队列头部的元素,队列为空时返回null
poll() 返回并移除队列的头部元素,队列为空时返回null
remove() 返回并移除队列的头部元素
再有Queue接口的两个子接口Deque、BlockingQueue。
`
六、题目:
1、用两个栈模拟一个队列
2、用两个队列模拟一个栈