目录
Stack:FILO
栈是一个操作受限的线性表,仅能在一端进行添加和删除数据,先进后出FILO
通过链表和数组实现
代码实现
栈的应用场景
- 实际工作不常用,可能会存在于某些jdk或者第三方插件的源码中
- 函数调用栈函数调用栈
- 反序字符串
- 浏览器的前进后退功能
-
编译器利用栈实现表达式求值
-
括号匹配问题
-
利用栈实现 DFS: depth-first-search 深度优先遍历
使用数组代码实现栈
import java.util.HashMap;
/**
* 实现集合类
* 模拟的数据结构是栈
* 底层结构是数组
*/
public class MyArrayStack<T> {
private final int MAX_CAPACITY = Integer.MAX_VALUE - 8;
private final int INIT_CAPACITY = 10;// 数组的默认容量
private Object[] objs;// 这个集合类底层持有的数组
private int size;// 这个集合类存储的元素数量
public MyArrayStack(){
objs = new Object[INIT_CAPACITY];
}
public MyArrayStack(int initCapacity){
if (initCapacity < 1 || initCapacity > MAX_CAPACITY)throw new IllegalArgumentException("parame is Illegal");
objs = new Object[initCapacity];
}
public boolean push(T value){
// 在添加的时候, 要先判断底层数组是否满了
if (size == objs.length){
// 数组满了--> 扩容
int newLen = getLen();// 获取扩容的长度
grow(newLen);// 根据要扩容的长度, 扩容这个数组
}
// 到这一步, 意味着, 数组一定有位置可供添加数据
// 添加元素, 栈顶在size-1位置, 栈底在下标为0的位置
objs[size] = value;
size++;
return true;
}
// 根据一个新长度扩容底层数组
private void grow(int newLen) {
Object[] newObjs = new Object[newLen];
// 把旧数组的数据转移到新数组
for (int i = 0; i < objs.length; i++) {
newObjs[i] = objs[i];
}
objs = newObjs;
}
// 获取一个数组的新长度
private int getLen() {
// 旧数组长度
int oldLen = objs.length;
// 新长度扩为原来的二倍
int newLen = oldLen * 2;
if (newLen >= MAX_CAPACITY || newLen < 0){
newLen = MAX_CAPACITY;
}
if (newLen == oldLen)throw new RuntimeException("stack can not add");
return newLen;
}
// 栈的出栈方法
public T pop(){
if (isEmpty()) throw new RuntimeException("stack is empty");
T value = (T)objs[size - 1];
size--;
return value;
}
// 栈的查看栈顶元素方法
public T peek(){
if (isEmpty()) throw new RuntimeException("stack is empty");
return (T) objs[size - 1];
}
public boolean isEmpty(){
return size == 0;
}
public int size(){
return size;
}
}
public class Demo2 {
public static void main(String[] args) {
MyArrayStack<String> stack = new MyArrayStack<>(2);
stack.push("zs");
stack.push("ls");
stack.push("wu");
stack.push("zl");
// zs - ls - wu -zl
// 栈底 栈顶
// 0 1 2 3
// zs - ls - wu -zl
// 栈底 栈顶
// 0 1 2
String pop = stack.pop();
System.out.println(pop);
System.out.println(stack);
// zs - ls - wu - aa
// 栈底 栈顶
// 0 1 2 3
stack.push("aa");
System.out.println(stack);
}
}
使用链表代码实现栈
/**
* 实现集合类
* 模拟的数据结构是栈
* 底层结构是链表: 单链表
*/
public class MyLinkedStack<T> {
private Node top;// 保存栈的栈顶
private int size;// 这个栈中保存了多少个元素
// 对于栈我们应该提供的三个方法: 入栈, 出栈, 查看栈顶元素
// push pop peek
/**
* 给栈实现一个添加方法
* @param value : 要添加的元素
* @return : 添加是否成功
*/
public boolean push(T value){
top = new Node(value, top);
size++;
return true;
}
/**
* 栈的删除操作
* @return : 被删除的栈顶元素
*/
public T pop(){
// 判断链表为空
if (isEmpty()) throw new RuntimeException("stack is empty");
T value = top.value;// 保存原本栈顶值
top = top.next; // 栈顶向栈底移动一个元素
size--;
return value;
}
/**
* 栈的查看栈顶元素的方法
* @return: 栈顶元素
*/
public T peek(){
// 判断链表为空
if (isEmpty()) throw new RuntimeException("stack is empty");
return top.value;
}
public boolean isEmpty(){
return size == 0;
}
public int size(){
return size;
}
class Node{
T value;
Node next;
public Node(T value, Node next) {
this.value = value;
this.next = next;
}
}
}
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Stack;
public class Demo {
public static void main(String[] args) {
// Stack<String> stack = new Stack<>();
// stack.push("zs");
// LinkedList<String> stack = new LinkedList<>();
// stack.push("zs");
MyLinkedStack<String> stack = new MyLinkedStack<>();
stack.push("zs");
stack.push("ls");
stack.push("wu");
stack.push("zl");
// zs ls wu zl
// 栈底 栈顶
String pop1 = stack.pop();
String pop2 = stack.pop();
System.out.println(pop1);
System.out.println(pop2);
System.out.println(stack.peek());
}
}
Queue:FIFO
队列是一种操作受限的线性表,一端插入数据,另一端删除数据。可以通过链表和数组实现
队列应用场景
不常用,可能会存在于某些jdk或者第三方插件的源码中,并且用到的不是普通队列,一般在工程中用到的是阻塞队列。
- 利用队列实现 BFS:breadth first search 广度优先搜索/ 遍历
阻塞队列:队列满的时候,添加程序等待,队列空的时候,删除程序等待。常用于生产者-消费者模型,队列满时入队列就阻塞,队列空的时候,出队列就阻塞。
线程池:创建线程池,会指定一个阻塞队列来存储任务
面包——任务
消费者——线程
生产者——提交任务的程序
桌子——阻塞队列
缓存、利用队列实现BFS:breadth first search 广度优先搜索/遍历
分布式:服务分布在不同的地方,这些分布在不同位置的服务又可以相互关联通过某些途径。
使用数组代码实现队列
public class MyArrayQueue<T> {
private final int MAX_CAPACITY = Integer.MAX_VALUE - 8;
private final int INIT_CAPACITY = 10;
private Object[] arr;// 底层循环数组
private int size;// 队列中存储了多少元素
private int head;// 队头的下标
private int end;// 队尾的下标
public MyArrayQueue() {
this.arr = new Object[INIT_CAPACITY];
}
public MyArrayQueue(int initCapacity) {
if (initCapacity < 1 || initCapacity > MAX_CAPACITY)throw new IllegalArgumentException("initCapacity is Illegal");
this.arr = new Object[initCapacity];
}
// offer poll peek
public boolean offer(T value){
if (size == arr.length){
// 底层数组满了, 需要扩容
int newLen = getLen();
grow(newLen);
}
// 走到这, 意味着数组中有位置可以添加
if (size == 0){
// 原本数组没有存储元素
arr[head] = value;
end = head;
}else {
// 原本数组已经存储元素
end = (end + 1) % arr.length;
arr[end] = value;
}
size++;
return true;
}
private void grow(int newLen) {
Object[] newArr = new Object[newLen];
// 把旧数组的数组转移到新数组
for (int i = 0; i < arr.length; i++) {
int index = (head + i) % arr.length;
newArr[i] = arr[index];
}
// 重新定义指向
arr = newArr;
head = 0;
end = size -1;
}
private int getLen() {
// 旧数组长度
int oldLen = arr.length;
// 新长度扩为原来的二倍
int newLen = oldLen * 2;
if (newLen >= MAX_CAPACITY || newLen < 0){
newLen = MAX_CAPACITY;
}
if (newLen == oldLen)throw new RuntimeException("stack can not add");
return newLen;
}
public T poll(){
if (isEmpty()) throw new RuntimeException("queue is empty");
T vlaue = (T)arr[head];
if (size == 1){// 原本队列中只剩一个元素
head = 0;
end = 0;
}else {
// 队列中有多个元素
head = (head + 1) % arr.length;
}
size--;
return vlaue;
}
public T peek() {
if (isEmpty()) throw new RuntimeException("queue is empty");
return (T)arr[head];
}
public boolean isEmpty(){
return size == 0;
}
}
public class Demo2 {
public static void main(String[] args) {
MyArrayQueue<String> queue = new MyArrayQueue<>(4);
queue.offer("zs");
queue.offer("ls");
queue.offer("wu");
queue.offer("zl");
// zs ls wu zl
// 0 1 2 3
// head end
// zs ls wu zl
// 0 1 2 3
// head end
String poll = queue.poll();
// aa ls wu zl
// 0 1 2 3
// end head
queue.offer("aa");
// ls wu zl aa bb
// 0 1 2 3 4 5 6 7
// head end
queue.offer("bb");
System.out.println(queue);
}
}
使用链表代码实现队列
/**
* 实现一个集合类
* 模拟的数据结构是队列
* 底层结构是: 单链表
*/
public class MyLinkedQueue<T> {
private Node head;// 队列的队头
private Node end;// 队列的队尾
private int size;// 队列存储了多少元素
// 作为队列, 它应该存在哪些操作?
// 入队列-> 添加 出队列 -> 删除 查看队头元素 --> 查找
// offer poll peek
public boolean offer(T value){
if (isEmpty()){
// 原队列为空, 这个新添加的元素, 既是队头又是队尾
head = new Node(value, null );
end = head;
size++;
return true;
}else {
// 原队列不空, 这个新添加的元素, 放在队尾
Node newNode = new Node(value, null);
end.next = newNode;
end = newNode;
size++;
return true;
}
}
public T poll(){
if (isEmpty())throw new RuntimeException("queue is empty");
// 链表不空
T value = head.value;
if (size == 1){
// 队列中只剩一个元素
head = null;
end = null;
}else {
// 队列中多于一个元素
head = head.next;
}
size--;
return value;
}
public T peek() {
if (isEmpty()) throw new RuntimeException("queue is empty");
return head.value;
}
public boolean isEmpty(){
return size == 0;
}
public int size(){
return size;
}
class Node{
T value;
Node next;
public Node(T value, Node next) {
this.value = value;
this.next = next;
}
}
}
import java.util.ArrayDeque;
public class Demo1 {
public static void main(String[] args) {
// ArrayDeque<String> queue = new ArrayDeque<>();
// queue.offer("zs");
// queue.poll();
// queue.peek();
MyLinkedQueue<String> queue = new MyLinkedQueue<>();
queue.offer("zs");
queue.offer("ls");
queue.offer("wu");
queue.offer("zl");
//zs -- ls -- wu -- zl
//队头 队尾
String poll = queue.poll();
System.out.println(queue.peek());
System.out.println(queue);
}
}