目录
队列的实现方式:
一、栈
栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。
压栈:向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;
出栈:从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
后进先出!
public static void main(String[] args) {
Stack<Integer> s = new Stack();
s.push(1);
s.push(2);
s.push(3);
s.push(4);
System.out.println(s.size()); // 获取栈中有效元素个数---> 4
System.out.println(s.peek()); // 获取栈顶元素---> 4
s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3
System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3
if(s.empty()){
System.out.println("栈空");
}else{
System.out.println(s.size());
二、队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
先进先出!
在Java中,Queue是个接口,底层是通过链表实现的。在实例化时必须实例化 LinkedList 的对象,因为 LinkedList 实现了 Queue 接口。
public static void main(String[] args) {
Queue<Integer> q=new LinkedList<>();//linkedlist是双向链表
q.offer(1);
q.offer(2);
q.offer(3);
q.offer(4);
q.offer(5); // 从队尾入队列
System.out.println(q.size());
System.out.println(q.peek()); // 获取队头元素
q.poll();
System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
if(q.isEmpty()){
System.out.println("队列空");
}else{
System.out.println(q.size());
}
}
下面是一些java中队列的实现方式:
1.顺序队列
顺序队列是队列的一种,只是说队列的存在形式是队列中的元素是连续的,就像数组
顺序队列的实现方式
(1)队头不动,队尾动(很像数组)
在元素出队列时,队头不动,队尾动,每出一个元素,就把队列中剩余的元素往前搬移,从而队尾会跟着改变。
缺点:在出队列时,需要大量搬移元素,时间复杂度为O(N)。
(2)队头动,队尾不动
在元素出队列时,队头往后移动一个位置,队尾一直保持不动。
优点:在出队列时,只需把队头往后移动一个位置就好,所以时间复杂度为O(1)。
缺点:因为在元素在出队列时只是移动队头,并没有把元素在内存上删除,所以就会出现元素把整个内存耗尽,并且此内存中的存储都是不合法的元素,从而造成假溢出。
假溢出:看似内存中再存储不了元素了,但是此内存在理论上是可以使用的,内存被一些不合法的元素使用完了,从而造成一种溢出现象,但是此溢出是假溢出。
2.循环队列
循环队列是首尾相接的顺序存储队列,顾名思义循环队列就是队列的内存可以循环使用
在顺序队列中,出现了假溢出问题,那循环队列就是为了假溢出问题的
循环队列的实现:
给定两个指针,分别标记队头和队尾,标记队头的指针是front,标记队尾的是rear。开始时,front是队头指针,rear是队尾指针,空队列时front和rear指向同一位置
class MyCircularQueue {
int[] array;
int front;//定义头结点
int rear;//定义尾结点
int count;//定义有效节点
int N;//定义数组长度
//定义数组
public MyCircularQueue(int k) {
array = new int[k];
N = k;
}
//入队列
public boolean enQueue(int value) {
if(isFull()){
return false;
}
array[rear] = value;
rear++;
if(rear == N){
rear = 0;
}
count++;
return true;
}
//出队列
public boolean deQueue() {
if(isEmpty()){
return false;
}
front++;
front %= N;
count--;
return true;
}
//返回队列头
public int Front() {
if(isEmpty()){
return -1;
}
return array[front];
}
//返回队列尾
public int Rear() {
if(isEmpty()){
return -1;
}
return array[(rear + N - 1)% N];
}
//检查是否满了
public boolean isEmpty() {
return 0 == count;
}
//检查是否为空
public boolean isFull() {
return count == array.length;
}
}
3.链式队列
链式队列跟线性表的单链表一样,只是说只能从头出从尾进而已
/*
* 链式队列
* front:指向的是链表的头节点
* rear:永远指向的是末尾节点
* @param <T>
*/
class LinkQueue<T>{
// 构造函数 offer poll peek empty size
private Entry<T> front; // 队头
private Entry<T> rear;
private int count; // 记录链表节点的个数
public LinkQueue(){
front= new Entry<>(null, null);
}
public void offer(T val) {
Entry<T> node = front;
while(node.next != null){
node = node.next;
}
node.next = new Entry<>(val, null);
count++;
}
public T poll() {
if(empty())
return null;
T val = front.next.data;
front.next = front.next.next;
count--;
return val;
}
public T peek() {
if(empty())
return null;
return front.next.data;
}
public boolean empty(){
return front.next == null;
}
public int size(){
return this.count;
}
static class Entry<T>{
T data;
Entry<T> next;
public Entry(T data, Entry<T> next) {
this.data = data;
this.next = next;
}
}
}
public class LinkQueueTest {
public static void main(String[] args) {
LinkQueue<Integer> ls = new LinkQueue<>();
ls.offer(1);
ls.offer(2);
ls.offer(3);
ls.offer(4);
System.out.println(ls.peek());
System.out.println(ls.size());
while(!ls.empty()){
System.out.print(ls.poll() + " ");
}
System.out.println();
}
}
4.双端队列
双端队列又名double ended queue,简称deque,双端队列没有队列和栈这样的限制级,它允许两端进行入队和出队操作,也就是说元素可以从队头出队和入队,也可以从队尾出队和入队。
import java.util.List;
// 模拟实现队列---底层使用双向链表---在集合框架中Queue是一个接口---底层使用的是LinkedList
public class Queue<E> {
// 双向链表节点
public static class ListNode<E>{
ListNode<E> next;
ListNode<E> prev;
E value;
ListNode(E value){
this.value = value;
}
}
ListNode<E> first; // 队头
ListNode<E> last; // 队尾
int size = 0;
// 入队列---向双向链表位置插入新节点
public void offer(E e){
ListNode<E> newNode = new ListNode<>(e);
if(first == null){
first = newNode;
// last = newNode;
}else{
last.next = newNode;
newNode.prev = last;
// last = newNode;
}
last = newNode;
size++;
}
// 出队列---将双向链表第一个节点删除掉
public E poll(){
// 1. 队列为空
// 2. 队列中只有一个元素----链表中只有一个节点---直接删除
// 3. 队列中有多个元素---链表中有多个节点----将第一个节点删除
E value = null;
if(first == null){
return null;
}else if(first == last){
last = null;
first = null;
}else{
value = first.value;
first = first.next;
first.prev.next = null;
first.prev = null;
}
--size;
return value;
}
// 获取队头元素---获取链表中第一个节点的值域
public E peek(){
if(first == null){
return null;
}
return first.value;
}
public int size() {
return size;
}
public boolean isEmpty(){
return first == null;
}