一、栈
1、描叙
栈和队列是计算机中基本的两个数据结构,栈可以达到后进先出,队列可以先进先出。在实际应用上,我们可以使用栈进行逆序遍历链表,非递归中序遍历二叉树,括号匹配,函数调用等等;可以使用队列对二叉树进行层次遍历,打印机的打印服务,通信中的消息队列等等。
栈的储存规则:
1,栈只能从表的一端存取数据,另一端是封闭的
2,在栈中,无论是存数据还是取数据,都必须遵循"先进后出"的原则,即最先进栈的元素最后出栈。
栈的使用案例1:
浏览器 “回退” 功能的实现,底层使用的就是栈存储结构。
当你关闭页面 A 时,浏览器会将页面 A 入栈;同样,当你关闭页面 B 时,浏览器也会将 B入栈。因此,当你执行回退操作时,才会首先看到的是页面 B,然后是页面 A,这是栈中数据依次出栈的效果。
栈的使用案例2:
栈存储结构还可以帮我们检测代码中的括号匹配问题。
多数编程语言都会用到括号(小括号、中括号和大括号),括号的错误使用(通常是丢右括号)会导致程序编译错误,而很多开发工具中都有检测代码是否有编辑错误的功能,其中就包含检测代码中的括号匹配问题,此功能的底层实现使用的就是栈结构。
2、图文示例
先进后出
3、代码示例
package stackAndQueue;
/**
* TODO 栈,先进后出
*/
public class Stack {
// 底层基于数组
private int[] arr;
// 数据长度, 最后一个数据的索引=数据长度-1
private int size;
// 容量设置
public Stack() {
arr = new int[16];
}
public Stack(int length) {
arr = new int[length];
}
/**
* 添加
*/
public void plus(int val) {
arr[size++] = val;
}
/**
* 获取并弹出元素, 最后一个数据的索引=size-1
*/
public int pop() {
return arr[--size];
}
/**
* 判断是否为空
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 容量是否满了,添加可先判断容量,进行扩容操作
*/
public boolean isFull() {
return size == arr.length;
}
}
测试代码
// 测试
class Test {
public static void main(String[] args) {
Stack stack = new Stack(5);
stack.plus(1);
stack.plus(2);
stack.plus(3);
stack.plus(4);
stack.plus(5);
System.out.println("容量是否满了-->" + stack.isFull());
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}
// 输出
容量是否满了-->true
5
4
3
2
1
二、队列
1、描叙
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。 [1]
队列的实现
队列存储结构的实现有以下两种方式:
顺序队列:在顺序表的基础上实现的队列结构;
链队列:在链表的基础上实现的队列结构;
两者的区别仅是顺序表和链表的区别,即在实际的物理空间中,数据集中存储的队列是顺序队列,分散存储的队列是链队列。
队列的实际应用
实际生活中,队列的应用随处可见,比如排队买 XXX、医院的挂号系统等,采用的都是队列的结构。
拿排队买票来说,所有的人排成一队,先到者排的就靠前,后到者只能从队尾排队等待,队中的每个人都必须等到自己前面的所有人全部买票成功并从队头出队后,才轮到自己买票。这就不是典型的队列结构吗?
2、图文示例
普通队列
先进先出
循环队列
先进先出
3、代码示例
package stackAndQueue;
/**
* TODO 队列,先进先出
*/
public class Queue {
// 底层基于数组
private int[] arr;
// 数据长度
private int size;
// 开头元素索引 (每获取一次数据 front+1)
private int front;
// 结尾元素索引(每添加一次数据rear+1,每获取一次数据rear-1 )--> 数据长度=rear+1
private int rear;
// 容量设置
public Queue() {
arr = new int[16];
front = -1;
rear = -1;
}
public Queue(int length) {
arr = new int[length];
front = -1;
rear = -1;
}
/**
* 获取当前数据长度
*/
public int size() {
return this.size;
}
/**
* 插入元素
*/
public void plus(int val) {
// 判断数组是否满了
if (isFull()) {
System.out.println("插入["+val+"]失败,数组容量已满");
} else {
// 循环插入,--元素尾添加
if (rear == arr.length - 1) {
rear = -1;
}
size++;
arr[++rear] = val;
}
}
/**
* 获取并弹出元素
*/
public int pop() {
// 循环读取--元素头开始读取
if (front == arr.length - 1) {
front = -1;
}
size--;
return arr[++front];
}
/**
* 判断元素是否为空
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 容量是否满了,添加可先判断容量,进行扩容操作
*/
public boolean isFull() {
return size == arr.length;
}
}
测试代码
// 测试
class Test1 {
public static void main(String[] args) {
Queue queue = new Queue(5);
queue.plus(1);
queue.plus(2);
queue.plus(3);
queue.plus(4);
queue.plus(5);
System.out.println("容量是否满了-->" + queue.isFull() + " 当前数据长度:" + queue.size());
//消费--> size=0
while (!queue.isEmpty()) {
System.out.println(queue.pop());
}
// 添加数据,size=0
queue.plus(6);
queue.plus(7);
queue.plus(8);
queue.plus(9);
queue.plus(10);
System.out.println("弹出--> " + queue.pop());
queue.plus(11);
//
queue.plus(12);
queue.plus(13);
queue.plus(14);
while (!queue.isEmpty()) {
System.out.println(queue.pop());
}
}
}
// 打印
容量是否满了-->true 当前数据长度:5
1
2
3
4
5
弹出--> 6
插入[12]失败,数组容量已满
插入[13]失败,数组容量已满
插入[14]失败,数组容量已满
7
8
9
10
11
本文到此结束,如果觉得有用,劳烦各位点赞关注一下呗,将不定时持续更新更多的内容…,感谢大家的观看!