栈和队列。这两者都属于逻辑结构,它们的物理实现既可以利用数组,也可以利用链表来完成。
栈
栈(stack)是一种线性数据结构,它就像一个上图所示的放入乒乓球的圆筒容器,栈中的元素只能先入后出(First In Last Out,简称FILO)。
最早进入的元素存放的位置叫作栈底(bottom),最后进入的元素存放的位置叫作栈顶
(top)。
栈的数组实现
栈的链表实现
入栈
入栈操作(push)就是把新元素放入栈中,只允许从栈顶一侧放入元素,新元素的位置将会成为新的栈顶。
出栈
出栈操作(pop)就是把元素从栈中弹出,只有栈顶元素才允许出栈,出栈元素的前一个元素将会成为新的栈顶。
栈的JAVA实现
package dataStructure.myStack;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.EmptyStackException;
public class myStack <E>{
private Object[]arr;
//数组默认长度
private int stackLength=4;
private int size;
//指针
private int index = -1;
//判断栈是否为空
public boolean empty(){
return this.size==0;
}
//获取栈顶元素
public E pop(){
if(this.index==-1){
throw new IndexOutOfBoundsException("空栈,无法取元素");
}
this.size--;
return (E) this.arr[index--];
}
/**
* 数组初始化或者以 1.5 倍容量对数组扩容
*/
private void capacity(){
if(this.arr==null){
this.arr = new Object[this.stackLength];
}
if(this.size-(this.stackLength-1)>=0){
this.stackLength = this.stackLength+this.stackLength/2;
this.arr = Arrays.copyOf(this.arr,this.stackLength);
}
}
//向栈容器添加元素
public E push(E item){
this.capacity();
this.arr[++index]=item;
this.size++;
return item;
}
public static void main(String[] args) {
myStack<String> myStack = new myStack<>();
myStack.push("a");
myStack.push("b");
myStack.push("c");
System.out.println(myStack.pop());
System.out.println(myStack.empty());
System.out.println(myStack.pop());
System.out.println(myStack.pop());
System.out.println(myStack.empty());
System.out.println(myStack.pop());
}
}
队列
队列(queue)是一种线性数据结构,它的特征和行驶车辆的单行隧道很相似。不同于栈的先入后出,队列中的元素只能先入先出(First In First Out,简称(FIFO)。队列的出口端叫作队头(front),队列的入口端叫作队尾(rear)。
队列这种数据结构既可以用数组来实现,也可以用链表来实现。
队列数组实现
队列链表实现
入队
入队(enqueue)就是把新元素放入队列中,只允许在队尾的位置放入元素,新元素的下一个位置将会成为新的队尾。
出队
出队操作(dequeue)就是把元素移出队列,只允许在队头一侧移出元素,出队元素的后一个元素将会成为新的队头。
循环队列
数组实现的队列可以采用循环队列的方式来维持队列容量的恒定。
以利用已出队元素留下的空间,让队尾指针重新指回数组的首位。
在物理存储上,队尾的位置也可以在队头之前。当再有元素入队时,将其放入数组的首位,队尾指针继续后移即可。
队列满的条件:(队尾下标+1)%数组长度 = 队头下标
循环队列JAVA实现
package dataStructure.myQueue;
public class circleQueue {
private int front;//队头指针
private int rear;//对尾指针
private int capacity;//队列容量
private int[] array;
public circleQueue(int capacity) {
this.capacity = capacity;
this.array = new int[this.capacity];
}
public circleQueue() {
}
/**
* 出队
* @return
* @throws Exception
*/
public int deQueue() throws Exception{
if(rear==front){
throw new Exception("队列已空");
}
//队头出队
int deQueueElement = array[front];
front = (front+1)%array.length;
return deQueueElement;
}
/**
* 入队
* @param element
* @throws Exception
*/
public void enQueue(int element) throws Exception{
if((rear+1)%array.length==front){
throw new Exception("队列已满!");
}
array[rear]=element;
rear = (rear+1)%array.length;
}
/**
* 输出队列
*/
public void output(){
for (int i = front;i!=rear;i=(i+1)%array.length){
System.out.println(array[i]);
}
}
public static void main(String[] args) throws Exception {
circleQueue circleQueue = new circleQueue(6);
circleQueue.enQueue(2);
circleQueue.enQueue(4);
circleQueue.enQueue(7);
circleQueue.enQueue(100);
System.out.println("队列元素为");
circleQueue.output();
System.out.println("--------");
circleQueue.deQueue();
circleQueue.deQueue();
circleQueue.deQueue();
System.out.println("队列元素为");
circleQueue.output();
System.out.println("--------");
circleQueue.enQueue(12);
circleQueue.enQueue(14);
circleQueue.enQueue(16);
circleQueue.enQueue(120);
System.out.println("队列元素为");
circleQueue.output();
System.out.println("--------");
}
}
栈和队列的应用
栈:栈通常用于对“历史”的回溯,也就是逆流而上追溯“历史”。
- 实现递归逻辑
- 导航
队列:是按照“历史”顺序,把“历史”重演一遍。
- 多线程中,争夺公平锁的等待队列,就是按照访问顺序来决定线程在队列中的次序的。
- 网络爬虫实现网站抓取时,也是把待抓取的网站URL存入队列中,再按照存入队列的顺序来依次抓取和解析的。
双端队列
双端队列:综合了栈和队列的优点,对双端队列来说,从队头一端可以入队或出队,从队尾一端也可以入队或出队。
优先队列
谁的优先级最高,谁先出队。
它是基于二叉堆来实现的。