栈和队列
- 站和队列都是线性表,都是基于List基础上实现的
- 线性表就是元素按照一条"直线排列起来",线性表这个结构中,一次添加单个元素,常见的线性表有:数组,链表,字符串,栈和队列
- 常见的非线性结构有:树,图
- 站和队列是一个再使用上更加严格的线性表,规定了添加元素和输出元素的规则
- 动态数组,链表,可以在任意位置进行插入和删除
栈
栈的简介
-
栈是一个先进后出,后进先出的线性表,支持三个核心的操作:入栈(push),出栈(pop),返回栈顶元素(peek)
-
向栈中依次添加元素:123,输出的顺序为321
栈的常见应用
- 无处不在的撤销操作
- 浏览器的前进后退操作
- 二叉树的四种遍历方式的底层就是使用的是栈和队列
自己实现一个栈
- 栈是一个线性表,底层可以使用数组,也可以使用链表
- 基于数组实现的栈叫做顺序栈添加或者删除元素实际上就是在数组的尾部进行的,时间复杂度为o(1),栈顶实际上就是数组的末尾
- 基于链表实现的栈叫做链栈
package com.zb.day5;
import java.util.ArrayList;
import java.util.List;
/**
* 基于数组实现的栈
*/
public class myStack<E> {
//当前栈的元素个数
private int size;
//实际上我们的这个栈基于动态数组进行实现
private List<E> list = new ArrayList<>();
/**
* 给栈中添加元素
* @param val
*/
public void push(E val)
{
list.add(val);
size++;
}
/**
* 将栈顶元素出栈
*/
public void pop()
{
list.remove(size-1);
size--;
}
/**
* 获得栈顶元素
* @return
*/
public E peek()
{
return list.get(size-1);
}
/**
* 判断栈是否为空
* @return
*/
public boolean isEmpty()
{
return list.isEmpty();
}
/**
* 判断栈中是否包含指定的元素
* @param val
* @return
*/
public boolean contains(E val)
{
return list.contains(val);
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
@Override
public String toString() {
String ret = "[";
for (int i = 0;i<size;i++)
{
if (i!=size-1)
{
ret +=list.get(i)+"->";
}
}
ret+=list.get(size-1)+"] top";
return ret;
}
}
-
测试一下
package com.zb.day5; public class myStackTest { public static void main(String[] args) { myStack<Integer> stack = new myStack<>(); stack.push(1); stack.push(2); stack.push(3); System.out.println(stack); System.out.println(stack.isEmpty()); System.out.println(stack.contains(3)); System.out.println(stack.contains(4)); int len = stack.getSize(); for (int i = 0 ;i<len;i++) { System.out.println(stack.peek()); stack.pop(); } System.out.println(stack); } }
队列
- 队列FIFO在队首出队在队尾入队,遵循先进先出,后进后出的原则
- 在现实生活中比如食堂排队打饭
队列的两种实现方式
- 基于数组实现的队列->顺序队列,但是由于队列是队尾入队(数组尾部添加元素),队首出队(数组头部删除元素),数组在尾部添加元素十分的方便,但是数组想要删除头部的元素确是十分的困难.
- 基于链表实现的队列->链式队列,链表头插尾插都相对更加容易一些
基于链表实现的队列
package com.zb.day5;
import java.util.LinkedList;
import java.util.List;
public class myQueue<E> {
//当前队列当中的元素个数
private int size=0;
List<E> list = new LinkedList<>();
/**
* 元素入队操作,从队尾进行入队
* @param val
*/
public void offer(E val)
{
list.add(val);
size++;
}
/**
* 元素出队操作,在队首进行出队
*/
public void poll()
{
list.remove(0);
size--;
}
/**
* 返回队首元素
* @return
*/
public E peek()
{
return list.get(0);
}
/**
* 判断队列是否是一个空队列
* @return
*/
public boolean isEmpty()
{
return list.isEmpty();
}
/**
* 判断队列是否存在指定元素
* @param val
* @return
*/
public boolean contains(E val)
{
return list.contains(val);
}
@Override
public String toString() {
int len = size-1;
String ret = "[";
while(len>=0)
{
if(len!=0)
{
ret+=list.get(len)+"->";
}else{
ret+=list.get(len);
}
len--;
}
return ret+="] top";
}
public int getSize() {
return size;
}
}
-
测试一下
package com.zb.day5; public class myQueueTest { public static void main(String[] args) { myQueue<Integer> queue = new myQueue<>(); queue.offer(1); queue.offer(2); queue.offer(3); System.out.println(queue); System.out.println(queue.isEmpty()); System.out.println(queue.contains(3)); System.out.println(queue.contains(10)); int len = queue.getSize(); for (int i = 0;i<len;i++) { System.out.println(queue.peek()); queue.poll(); } } }
循环队列
循环队列队列是一个环,逻辑上成环,物理上还是线性表
head- 指向当前队列的第一个元素下标
tail-指向当前队列的最后一个元素的下一个位置
1、当tail走到数组末尾的时候,若数组头部还有空闲位置,tail返回数组头部继续存放元素(比之前的普通队列节省空间)
2、出队时直接将head引用向后移动即可,不需要再搬移元素 eg:出队的时候,head=head+1即可
当tail走到数组末尾的时,下一步再次返回数组头部(0)
循环队列的判空条件
当head == tail的时候,环形队列为空
循环队列的判满条件
如图,当循环队列满的时候,head就是队首元素的 tail是当前队列最后一元素的下一个索引,此时我们发现 head在第一个位置,tail就是最后一个元素5的下一个位置,也是队首元素,此时head == tail 此时满的时候和空的时候的情况就一样了,我们就无法区分环形队列到底是满还是空了。因此我们需要对当前这个队列进行改造。
改造循环队列
为了能够区分环形队列到底是满还是空,我们的做法是浪费一个空间,用来区分数组是否已满
- 浪费图中的红色阴影部分,如图此时tail已经走到了数组的末尾,如何让他的数组头部
- data.length=5
- tail=4 -> 当tail达到数组的末尾的时候,再次移动头部
- 取模-对数组的长度进行取模 tail=(tail+1)%data.length;
- 环形队列的判满条件:(tail+1)%data.length==head;
3、此时head和tail引用向后移动时,不能简单+1,要++后对数组的长度取模(可以返回数组头部继续向后移动)
head=(head+1)%data.length;
tail=(tail+1)%data.length;
最后一个元素的下标问题
此时最后一个元素索引是tail-1,tai就是当前最后一个元素的下一个位置,我们发现在这个循环队列当中还有一个空位 就是1前面那个,我们将4入队
此时tail恰好在数组的第一个位置,此时队列的最后一个元素的下标就不能是简单的tail-1了,此时最后一个元素的小标为data.length-1
所以通过上述两个情况
数组的最后一个元素的索引LastIndex
LastIndex = tail == 0?data.length-1:tail-1;