本专栏文章主要用于帮助Java使用者快速上手数据结构,刷算法题!
前言
自古以来数据结构界就分为九重天,据说冲破这九重天之后就可以去进攻算法界最终修炼最后成佬,受万人敬仰。
但是这谈何容易,因为每一重天都有神兽把守,想要冲破每一重天都必须收服守护的神兽才行。
守护九重天的神兽分别是:数组、字符串、栈、队列、链表、树、散列表、堆、图。可见他们的战斗力也是逐层增强的。想只凭靠自身的能力拿下他们谈何容易。
不过大家不必惊慌,我这里有一本上古秘籍《Java小子怒闯数据结构九重天》,里面有每一重天神兽的攻略。只要修炼者仔细钻研里面的每一篇,对九重天了如指掌之后,冲破这九重天也是易如反掌的。
今天为大家带来第四重天的攻略!
1.🌀队列的基础知识
队列是一种线性表,它的特性是先进先出,插入在一端,删除在另一端。也被称为FIFO(先入先出,First- In-First-Out)结构。队列结构就像排队一样,刚来的人入队要排在队尾,每次出队的都是队首的人。
类比于排队我们就可以知道对于队列这种数据结构我们有两个操作分别是:入队(push()
)和出队(pop()
)。
同样根据这个图我们就可以看出来队列先进先出的特点了。
2.🌀Java中实例化队列
队列与栈十分相似,都是特殊的线性结构,队列与栈不同的是在Java中并没有队列(Queue)类,也当然没有它自己的方法之类了。
那么有人可能就要说了,我明明在写Java的时候看到过Queue这个关键字的,确实你没有看错的确有这个关键字。在Java中虽然没有Queue类但是却有Queue接口。
如下图:
那么我们又该怎么样实例化一个队列呢?
我们实例化的格式如下:
Queue<T> queue = new ArrayDeque<T>();
Queue<T> queue = new LinkedList<T>();
看到这里大家可能会有些迷,什么Queue,Deque,ArrayDeque,LinkedList之类的,直接乱掉了。
接下来我来帮大家理一理关系:
首先Queue
、Deque
都是接口,其次Deque
是Queue
的子接口,我们看源码:
其中Queue
就是个标准的队列接口,只允许一端入队一端出队,里面定义了队列的入队出队方法。
而Deque
是接口Queue
的扩充,它是一个双端队列,允许在两端进行入队出队操作,定义的方法在Queue
的基础上增添了其他的一些。
对于ArrayDeque
、LinkedList
都是接口Deque
的实现类,也就是Queue
的实现类。他们对所有Deque
和Queue
中定义的方法进行了实现。
所以说我们在实例化栈的时候就是使用双端队列Deque
这个接口,然后只在一端进行操作,就是一个栈了。如下:
Deque<E> stack = new ArrayDeque<E>();
Deque<E> stack = new LinkedList<E>();
在实例化标准队列的时候肯定就是使用Queue
这个接口在一端入队,另一端出队,如下:
Queue<T> queue = new ArrayDeque<T>();
Queue<T> queue = new LinkedList<T>();
这里说的是标准队列,如果需要使用到双端队列那么我们肯定也会使用Deque这个接口的。
相信看到这里你应该就懂了!有不理解的评论区见,或者关注下列公众号可以联系到我。
3.🌀Java中队列常用API
上面介绍到我们实例化队列主要是使用ArrayDeque、LinkedList类,但是他们最终也只是实现了Queue接口的子方法,只不过实现形式不同而已。
所以我们就围绕Queue接口来讲解一下队列常用的API即可。
3.1 使用add()
添加元素到队列,如果队列满了就抛异常。
queue.add(value);
3.2 使用remove()
移除并且返回队列头部元素,如果队列为null,就抛异常。
queue.remove();
3.3 使用element()
返回队列头部元素,但不会移除元素 如果队列为null,就抛异常。
queue.element();
3.4 使用offer()
添加元素到队列,如果队列满了就返回false,不会阻塞.
queue.offer();
3.5 使用poll()
移除并且返回队列头部元素,如果队列为null,就返回null,不会阻塞。
queue.poll();
3.6 使用peek()
返回队列头部元素,不会移除元素,如果队列为null,就返回null,不会阻塞。
queue.peek();
4.🌀Java实现队列
与栈类似,队列底层实现有数组与链表我们也分别使用数组和链表来实现一下队列。
用数组实现队列
public class MyQueue<T>{
//底层使用数组来实现
T[] elements;
//构造函数
public MyQueue(){
elements = new T[0];
}
//向队列中推入元素
public void push(T element){
//先初始化一个新的数组,因为要向队列中推入元素,所以需要个数比之前的数组个数要多一个,所以这里加1
T[] newArr = new T[elements.length + 1];
//然后把原先数组中的元素挪到新的数组中
for(int i = 0; i < elements.length; i++){
newArr[i] = elements[i];
}
//把新加入的元素放到新的数组的最后面
newArr[elements.length] = element;
//然后将原来的数组替换成新的数组
elements = newArr;
}
//判断队列是否为空
public boolean isEmpty(){
if(elements.length == 0){
return true;
}else{
return false;
}
}
//出队
public T pull(){
//判断队列是否为空
if(isEmpty()){
//如果为空,则抛出异常
System.out.println("对列中元素为空");
throw new NullPointerException();
}
//先初始化一个新的数组用来盛放新的队列,因为要取出数据,所以队列的大小是原先队列大小减1
T[] newArr = new int[elements.length - 1];
//将数据放入新的数组中
for(int i = 1; i < elements.length; i++){
newArr[i-1] = elements[i];
}
//将第一个元素取出来
T element = elements[0];
//将数组改成新的数组
elements = newArr;
return element;
}
//查看队列中的元素
public void getQueue(){
//判断队列是否为空
if(isEmpty()){
//如果为空,则抛出异常
System.out.println("对列中元素为空");
throw new NullPointerException();
}
for(T element : elements){
System.out.println(element);
}
}
}
用链表实现一个队列
//定义每个链表节点
class Node<T>{
public T val;
public Node next;
public Node(T val){
this.val=val;
}
}
public class MyQueue<T>{
//定义头尾指针
private Node<T> front,rear;
//定义队列长度
private int length;
//构造一个空的队列
public MyQueue() {
length = 0;
front = rear = new Node<T>(null);
}
//在队列的队尾插入一个新元素
public void push(T obj) {
rear = rear.next = new Node<T>(obj, null);
length ++;
}
//删除队列队头元素
public T pull( ) {
if (isEmpty()){
throw new RuntimeException("队列为空");
return null;
}
Node<T> p = front.next;
T x = p.data;
front.next = p.next;
length--;
if (front.next == null){
rear = front;
}
return x;
}
//取队列队头元素
public T getHead( ){
if (isEmpty()){
throw new RuntimeException("队列为空");
return null;
}
return front.next.data;
}
//求出队列中数据元素的个数
public int size( ){
return length;
}
//判断当前队列是否为空
public boolean isEmpty(){
return length == 0;
}
}
5.🌀队列进阶练习
Leetcode 225. 用队列实现栈
大家先思考尝试后再看题解哦!
题解:
这个题目没什么困难的地方主要考察的是你对对列和栈的了解以及自带API运用的是否熟练。
代码如下:
class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
queue1 = new LinkedList<Integer>();
queue2 = new LinkedList<Integer>();
}
public void push(int x) {
queue2.offer(x);
while (!queue1.isEmpty()) {
queue2.offer(queue1.poll());
}
Queue<Integer> temp = queue1;
queue1 = queue2;
queue2 = temp;
}
public int pop() {
return queue1.poll();
}
public int top() {
return queue1.peek();
}
public boolean empty() {
return queue1.isEmpty();
}
}
结语
恭喜你修炼到这里,你已经基本有了收服神兽队列的能力。神兽队列是我们到进攻算法界最重要的能力之一。大家不可懈怠。
感兴趣的修炼者可以关注下面公众号,会持续推送最新更新的!
持续更新中…