目录
1.栈(Stack)
1.1 概念
1.2 栈的实现
2.队列(Queue)
2.1 概念
2.2 队列的实现
2.3 循环队列
2.3.1 概念
2.3.2 循环队列的实现
3. 双端队列 (Deque)
1.栈(Stack)
1.1 概念
栈是一种特殊的线性表,栈只允许再固定的一端进行插入和删除元素的操作。栈中的数据遵循先进后出LIFO(last in first out)的原则。
入栈:栈的插入操作叫做入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈,出数据在栈顶。
我们常见的栈的操作有:方法调用栈的过程;浏览器的前进与后退; 编辑器中的撤回; 代码编辑器中括号的匹配; 算术运算符号的优先级。
栈的核心操作:
1、push():向栈中添加元素
2、pop():移除栈顶元素
3、peek():查看栈顶元素但不删除
1.2 栈的实现
栈的底层实现有两种:
1、基于数组的实现:顺序栈
2、基于链表的实现:链式栈
使用顺序表实现栈:
package stack_quene.stack.stack;
import java.util.Arrays;
import java.util.NoSuchElementException;
public class Stack<E> {
public boolean isEmpty;
private E[] elementdata;
private int size;
public Stack() {
elementdata = (E[]) new Object[10];
}
public Stack(int initCap) {
elementdata = (E[]) new Object[initCap];
}
//入栈操作
public void push(E value) {
//扩容
if (size == elementdata.length) {
grow();
}
elementdata[size] = value;
size++;
}
//出栈操作
public E pop() {
if (getSize() == 0) {
throw new NoSuchElementException("栈为空");
}
E oldValue = elementdata[size - 1];
size--;
elementdata[size] = null;
return oldValue;
}
//查看栈顶元素但不删除
public E peek() {
if (getSize() == 0) {
throw new NoSuchElementException("栈为空");
}
return elementdata[size - 1];
}
public int getSize() {
return size;
}
//扩容操作
public void grow() {
int oldValue = this.elementdata.length;
int newValue = oldValue << 1;
this.elementdata = Arrays.copyOf(elementdata, newValue);
}
@Override
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append("[");
for (int i = 0; i < size; i++) {
sb.append(elementdata[i]);
if(i!=size-1){
sb.append(",");
}
}
sb.append("] top");
return sb.toString();
}
public boolean isEmpty() {
return false;
}
}
2.队列(Queue)
2.1 概念
队列:只允许在一段进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列遵循先进先出(First In First Out)的原则,进行插入操作的一端称为队尾(Tail/Rear),进行删除操作的一端称为队头(Head/Front)。
队列的核心操作:
1.offer():入队操作;
2.poll():出队操作;
3.peek():查看队首元素,但不出队。
2.2 队列的实现
队列的两种底层实现:
1.基于数组的队列:循环队列
2.基于链表的队列
队列使用链表实现更加优化一些,因为如果使用数组结构,出队列时在数组头出数据,那么在删除后后面所有元素都需要向前移动,效率比较低。
package stack_quene.stack.quene.impl;
import stack_quene.stack.quene.Queue;
import java.util.NoSuchElementException;
public class LinkedQuene implements Queue {
private Node head;
private Node tail;
private int size;
private class Node{
private int data;
private Node next;
public Node(int data) {
this.data = data;
}
}
/**
* 队首出,队尾进
* @param value
*/
@Override
public void offer(int value) {
Node node=new Node(value);
if(head==null){
head=tail=node;
}else {
tail.next = node;
tail = node;
}
size++;
}
//出队列操作
@Override
public int poll() {
if(size==0){
throw new NoSuchElementException("队列为空");
}
int oldValue=head.data;
Node node=head;
head=head.next;
node.next=null;
size--;
return oldValue;
}
//查看队首元素,但不删除的操作
@Override
public int peek() {
if(size==0){
throw new NoSuchElementException("队列为空");
}
return head.data;
}
@Override
public boolean isEmpty() {
return false;
}
//打印输出队列
@Override
public String toString() {
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("front [");
Node node=head;
while(node!=null){
stringBuilder.append(node.data);
if(node.next!=null){
stringBuilder.append(",");
}
node=node.next;
}
stringBuilder.append("] top");
return stringBuilder.toString();
}
}
2.3 循环队列
2.3.1 概念
在循环队列中我们规定:front指向循环队列的第一个元素的索引; tail指向循环队列的最后一个元素的下一个元素的索引(为了方便插入元素,array[tail]=value)。
因为当tail的下标移动到数组的最后一个位置时,在数组未存满的情况下,需要继续循环移动到数组的第一个位置,继续从头开始入队。
所以front和tail的移动规则是:front=(front+1)%array.length ,tail=(tail+1)%array.length
如何判断循环队列的空or满呢?
1.判断数组是否为空: front==tail
2.判断数组是否已满:front=(tail+1)%array.length,因此我们规定在循环队列中浪费一个空间,这个空间不存储元素,为了区分空和满。
2.3.2 循环队列的实现
package stack_quene.stack.quene.impl;
import stack_quene.stack.quene.Queue;
public class LoopQueue implements Queue {
private int[] data;
//有效元素个数
private int size;
private int front;
private int tail;
public LoopQueue(int k) {
data=new int[k+1];
}
@Override
public void offer(int value) {
//判断循环队列是否已满
if(isFull()){
System.out.println("queue is full");
return;
}
data[tail]=value;
tail=(tail+1)%data.length;
size++;
}
@Override
public int poll() {
//判断循环队列是否为空
if(isEmpty()){
System.out.println("queue is empty");
return -1;
}
int value=data[front];
front=(front+1)%data.length;
size--;
return value;
}
@Override
public int peek() {
if (isEmpty()){
System.out.println("queue is empty");
}
return data[front];
}
//如何查看队尾元素
public int getTail(){
if (isEmpty()){
System.out.println("queue is empty");
}
int index=tail==0?data.length-1:tail-1;
return data[index];
}
@Override
public boolean isEmpty() {
if(front==tail){
return true;
}
return false;
}
public boolean isFull(){
if((tail+1)%data.length==front){
return true;
}
return false;
}
public int getSize(){
return size;
}
//遍历数组
public String toString(){
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("front [");
for (int i = front; i !=tail ;) {
stringBuilder.append(data[i]);
if(i!=tail-1){
stringBuilder.append(",");
}
i=(i+1)%data.length;
}
stringBuilder.append("] tail");
return stringBuilder.toString();
}
}
3. 双端队列 (Deque)
双端队列是指允许两端都可以进行入队和出队操作的队列。
这就是关于栈和队列的所有基本知识~~~