【java数据结构】队列
此篇博客希望对你有所帮助(帮助你了解队列),不懂的或有错误的也可在评论区留言,错误必改评论必回!!!持续关注,下一篇博客是树!!!整篇博客的代码都在Gitee中(代码链接放下文章结尾)。
一、队列的概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进的FIFO(First in First Out)队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头 (Head/Front)
队列:如等地铁一样,先来的先排队坐地铁,后来的后排队坐地铁。
二、队列的使用
在java中,Queue是个接口,底层是通过链表实现的。
- 当使用有容量限制的队列时,offer(E e)通常比add(E e)方法更好用,因为offer在无法插入元素时不会抛出异常,而是返回false。
- poll()与remove()方法相比,poll()方法在队列为空时不会抛出异常。
- peek()与element()方法相比,peek()方法在队列为空时不会抛出异常。
三、队列的模拟实现(双链表)
这里用单链表或双链表都可以实现队列。但是我们这里实现队列是通过双链表模拟的。
单链表实现思路:
我们需要定义一个标记尾节点的引用,下来就是尾插入,头删除,有兴趣的可以自行实现以下哦!
public void offer(int val) {
}
public int poll() {
}
public int peek() {
}
public int size() {
}
public boolean isEmpty() {
}
创建节点
public static class ListNode {
ListNode next;
ListNode prev;
int value;
ListNode(int value) {
this.value = value;
}
}
ListNode first; // 队头
ListNode last; // 队尾
int size = 0;
isEmpty()
public boolean isEmpty() {
return first==null;
}
size()
public int size() {
return size;
}
offer(int val)
public void offer(int val) {
ListNode cur=new ListNode(val);
if(first==null){
first=last=cur;
}
last.next=cur;
cur.prev=last;
cur.next=null;
last=cur;
}
poll()
public class EmptyException extends RuntimeException{
public EmptyException() {
}
public EmptyException(String message) {
super(message);
}
}
public int poll() {
ListNode cur=first;
if(first==null){
throw new EmptyException();
}else if(first==last){
size--;
return first.value;
}else {
first=first.next;
first.prev=null;
size--;
return cur.value;
}
}
peek()
public int peek() {
if(first==null){
throw new EmptyException();
}else{
return first.value;
}
}
四、循环队列
实际中我们有时还会使用一种队列叫循环队列。环形队列通常使用数组实现。
4.1 循环队列下标偏移
4.2 如何区分队列是空还是满
- 通过添加 size 属性记录,如果size等于数组长度则满,否则不满
- 保留一个位置,
- 使用标记,判断尾的下一个是不是头
4.4 设计循环队列
public class MyCircularQueue {
public int front;
public int rear;
public int[] elem;
public int useSize;
public MyCircularQueue(int k) {
this.elem = new int[k];
}
public boolean enQueue(int value) {
if (isFull()) {
return false;
}
elem[rear] = value;
rear = (rear + 1) % elem.length;
useSize++;
return true;
}
public boolean deQueue() {
if (isEmpty()) {
return false;
}
front = (front + 1) % elem.length;
useSize--;
return true;
}
public int Front() {
if (isEmpty()) {
return -1;
}
return elem[front];
}
public int Rear() {
if (isEmpty()) {
return -1;
}
int index = (front+useSize-1)% elem.length;
return elem[index];
}
public boolean isEmpty() {
return useSize == 0;
}
public boolean isFull() {
return useSize==elem.length;
}
}
五、双端队列(Deque)
双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。 那就说明元素可以从队头出队和入队,也可以从队尾出队和入队
Deque是一个接口,使用时必须创建LinkedList的对象。
Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现
六、练习
public class MyStackUseQueue {
public Queue<Integer> queue1;
public Queue<Integer> queue2;
public MyStackUseQueue() {
queue1=new LinkedList<>();
queue2=new LinkedList<>();
}
public void push(int x) {
if(!queue1.isEmpty()){
queue1.offer(x);
} else if (!queue2.isEmpty()) {
queue2.offer(x);
}else{
queue1.offer(x);
}
}
public int pop() {
if(empty()){
return -1;
}
if(!queue1.isEmpty()){
int size=queue1.size();
for(int i=0;i<size-1;i++){
queue2.offer(queue1.poll());
}
return queue1.poll();
}else{
int size=queue2.size();
for(int i=0;i<size-1;i++){
queue1.offer(queue2.poll());
}
return queue2.poll();
}
}
public int top() {
if(empty()){
return -1;
}
if(!queue1.isEmpty()){
int size=queue1.size();
int val=0;
for(int i=0;i<size;i++){
val=queue1.peek();
queue2.offer(queue1.poll());
}
return val;
}else{
int size=queue2.size();
int val=0;
for(int i=0;i<size;i++){
val=queue2.peek();
queue1.offer(queue2.poll());
}
return val;
}
}
public boolean empty() {
return queue1.isEmpty()&&queue2.isEmpty();
}
}
public class MyQueueUseStack {
public ArrayDeque<Integer> stack1;
public ArrayDeque<Integer> stack2;
public MyQueueUseStack() {
stack1 = new ArrayDeque<>();
stack2 = new ArrayDeque<>();
}
public void push(int x) {
stack1.push(x);
}
public int pop() {
if(empty()) {
return -1;
}
if(stack2.isEmpty()) {
//第一个栈里面所有的元素 放到第二个栈当中
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
public int peek() {
if(empty()) {
return -1;
}
if(stack2.isEmpty()) {
//第一个栈里面所有的元素 放到第二个栈当中
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.peek();
}
public boolean empty() {
return stack1.isEmpty() && stack2.isEmpty();
}
}