目录
双端队列Deque(是一个接口,它的一个重要子类LinkedList)
1.栈(Stack)
定义: 一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO
(
Last In First Out
)的原则。
压栈:栈的插入操作叫做进栈
/
压栈
/
入栈,
入数据在栈顶
。
出栈:栈的删除操作叫做出栈。
出数据在栈顶
核心操作:
pop():移除栈顶元素
peek():查看栈顶元素但不删除
push():向栈中添加元素
栈的底层实现由两种:
基于数组的实现-顺序栈
基于链表的实现-链式栈
一般来说,顺序栈的实现较为简单。
实现代码如下:
package stack_queue.stack;
import java.util.NoSuchElementException;
public class Stack<E> {
private E[] elementData;
//当前栈中元素个数
private int size;
public Stack(){
elementData = (E[]) new Object[10];
size = 0;
}
public Stack(int initCap){
elementData = (E[]) new Object[initCap];
}
//入栈操作
public void push(E value){
elementData[size ++] = value;
}
//出栈操作,返回原先的栈顶元素
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;
}
@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();
}
}
package stack_queue.stack;
public class StackTest {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(3);
stack.push(5);
//[1,3,5]top
System.out.println(stack);
//5
System.out.println(stack.peek());
stack.pop();
//[1,3]top
System.out.println(stack);
}
}
2.队列(Queue)
定义:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头 (Head/Front)
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低(即每次出队后都需要进行数组元素的搬移操作,非常耗时)。
基于链表的实现:
代码如下:
package stack_queue.queue;
public class QueueTest {
public static void main(String[] args) {
Queue queue = new LinkedQueue();
queue.offer(1);
queue.offer(3);
queue.offer(5);
//front[1,3,5] tail
System.out.println(queue);
queue.poll();
System.out.println(queue);
}
}
package stack_queue.queue;
import java.util.NoSuchElementException;
//给予链表的队列
//队首进队尾出
public class LinkedQueue 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 tmpHead = head;
head = head.next;
tmpHead.next = null;
size--;
return oldValue;
}
@Override
public int peek() {
if (size == 0){
throw new NoSuchElementException("队列为空!");
}
return head.data;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("front [");
Node node = head;
while (node != null){
sb.append(node.data);
if (node.next != null){
sb.append(",");
}
node = node.next;
}
sb.append("] tail");
return sb.toString();
}
}
循环队列:
条件:①front指向循环队列的第一个元素索引
②tail永远指向循环队列的最后一个元素的下一个位置arr[tail] = x;
③当front == tail时循环队列为空
④当tail + 1 == front时,循环队列为满(设定在循环队列中浪费一个空间,这个空间不能存储元素,这是判断满和空的区别)
⑤每次front和tail添加或者删除元素后向后移动,(front + 1) % data.length; (tail + 1) % data.length
注意:驱魔的核心是当走到数组末尾时,据需从头开始入队和出队。
循环队列代码的实现:
package stack_queue.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.err.println("queue is full!");
return;
}
data[tail] = value;
tail = (tail + 1) % data.length;
size++;
}
@Override
public int poll() {
//判断队列是否为空
if (isEmpty()){
System.err.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.err.println("queue is empty!");
return -1;
}
return data[front];
}
public int getTail(){
if (isEmpty()) {
System.err.println("queue is empty!");
return -1;
}
//最后一个元素的下标
int index = tail == 0 ? data.length - 1 : tail - 1;
return data[index];
}
public boolean isFull(){
if ((tail + 1) % data.length == front)
return true;
return false;
}
public boolean isEmpty(){
return tail == front;
}
public int getSize() {
return size;
}
//遍历循环队列
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("front [");
//取得最后一个元素的索引
int lastIndex = tail == 0 ? data.length - 1: tail - 1;
//遍历循环队列
for (int i = front; i != tail;) {
sb.append(data[i]);
if (i != lastIndex){
sb.append(",");
}
i = (i + 1) % data.length;
}
sb.append("] tail");
return sb.toString();
}
}
package stack_queue.queue;
import stack_queue.queue.LinkedQueue;
public class QueueTest {
public static void main(String[] args) {
LoopQueue loopQueue = new LoopQueue(3);
loopQueue.offer(1);
loopQueue.offer(3);
loopQueue.offer(5);
//front[1,3,5]tail
System.out.println(loopQueue);
loopQueue.poll();
//[3,5]
System.out.println(loopQueue);
loopQueue.offer(7);
System.out.println(loopQueue);
}
}
双端队列Deque(是一个接口,它的一个重要子类LinkedList)
普通队列只能从一端插入另一端删除。而双端队列两端可以同时进行插入和删除。
注意:栈和队列经常作为高阶数据结构的辅助。