定义:一个线性表的两端当做栈底分别进行入栈和出栈的操作,主要利用了栈"栈底位置不变,而栈顶位置动态变化"的特性。
我们把双端栈叫做ArrayDoubleEndStack,双端栈是线性表的一种,更是栈的一个特殊的分类,所以我们可以用动态数组和栈的思想来实现双端栈,毕竟由于其操作的特殊性,并不能借助ArrayList或者ArrayStack实现,所以这里需要从头开始实现双端栈
package 动态数组;
public class ArrayDoubleEndStack {
private E[] data;//元素的容器
private int ltop;//左端栈的栈顶ltop=-1,左端栈为空ltop+1表示左端栈中元素的个数
private int rtop;//右端栈的栈顶rtop=data.length,右端栈为空length-rtop表示右端栈中元素的个数
private static int DEFAULT_SIZE=10;//双端栈的默认容量
public ArrayDoubleEndStack(){
data =(E[]) new Object[DEFAULT_SIZE];
//对两个标记进行赋值
rtop=data.length;
ltop=-1;
}
//入栈操作
public void push(E element,int stackId){
//如果栈满了,就需要扩容
if(ltop+1==rtop){
resize(data.length*2);
}
switch (stackId){
case 0://向左端栈入栈一个元素
data[++ltop]=element;
break;
case 1://向右端栈入栈一个元素
data[--rtop]=element;
break;
}
}
//出栈操作
public E pop(int stackId){
if(isEmpty(stackId)){
throw new NullPointerException("stack is null");
}
E ret =null;
switch(stackId){
case 0:
ret = data[ltop--];
break;
case 1:
ret = data[rtop++];
break;
}
//如果元素个数<=len/4 && len>DEFAULT_SIZE时,就要进行缩容
if(size(0)+size(1)==data.length/4&&data.length>DEFAULT_SIZE){
resize(data.length/2);
}
return ret;
}
private void resize(int newLength) {
E [] newData =(E [])new Object[newLength];
//先去处理左端栈的问题
for(int i=0;i<=ltop;i++){
newData[i]=data[i];
}
//再去处理右端栈
int index= rtop;
for(int i=newLength-size(1);i=rtop;i--){
sb.append(data[i]);
if(i==rtop){
sb.append(']');
sb.append('\n');
}
else{
sb.append(',');
}
}
}
return sb.toString();
}
}
队列的定义
只是允许在一端进行插入操作,而在另一端进行删除操作的线性表
我们把允许删除的一端称为队首,插入的一端称为队尾
不含任何元素的队列称为空队列
队列是一种先进先出的线性表,简称FIFO
队列的顺序存储结构本身是由ArrayList实现的
队列本身也是一个线性表,其数据元素具有线性关系,只不过它是一种特殊的线性表而已
package 数据接口的实现;
public interface Queue extends Iterable {
public int size();
public boolean isEmpty();
public void offer(E element);
public E poll();
public E element();
public void clear();
}
package 动态数组;
import 数据接口的实现.Queue;
import java.util.Iterator;
public class ArrayQueue implements Queue {
//定义一个ArrayList list的对象
private ArrayList list;
public ArrayQueue(){
this(10);
}
public ArrayQueue(int capacity){
list =new ArrayList<>(capacity);
}
@Override
//获取队列中有效元素的个数
public int size() {
return list.size();
}
@Override
//判断队列是否为空
public boolean isEmpty() {
return list.isEmpty();
}
@Override
//入队一个元素,默认是在list的表尾添加一个元素
public void offer(E element) {
list.add(element);
}
@Override
//出队一个元素
public E poll() {
return list.remove(0);
}
@Override
//获取队首的元素,相当于获取list的第一个元素
public E element() {
return list.get(0);
}
@Override
//清空队列
public void clear() {
list.clear();
}
@Override
//获取迭代器
public Iterator iterator() {
return list.iterator();
}
public String toString(){
StringBuilder sb =new StringBuilder(String.format("ArrayQueue:%d/%d[",size(),list.getCapacity()));
if(isEmpty()){
sb.append(']');
}
else{
for(int i=0;i
队列的插入操作,叫做入队
队列的删除操作,叫做出队
package 动态数组;
import java.io.File;
public class 文件夹遍历 {
public static void main(String[] args) {
File dir =new File("C:\\Users\\Administrator\\Documents\\Tencent Files\\1115963060\\FileRecv\\JavaSEDay03笔记代码\\Day03Code");
ArrayQueue queue =new ArrayQueue<>();
queue.offer(dir);
while(!queue.isEmpty()){
File subDir =queue.poll();
System.out.println("【"+subDir.getName()+"】");
File[] subFiles =subDir.listFiles();
for(File f:subFiles){
if(f.isDirectory()){
queue.offer(f);
}
else{
System.out.println(f.getName());
}
}
}
}
}
package 动态数组;
public class 栈实现队列 {
//栈实现队列
public static void main(String[] args) {
QueueImplByStack queue =new QueueImplByStack<>();
for(int i=1;i<=10;i++){
queue.offer(i);
}
System.out.println(queue);
//出队三个元素
for(int i=0;i<=2;i++){
queue.poll();
}
System.out.println(queue);
System.out.println(queue.element());
}
}
//用栈来实现那个队列
class QueueImplByStack{
//创建两个栈
private ArrayStack stackA;
private ArrayStack stackB;
public QueueImplByStack(){
stackA = new ArrayStack<>();
stackB = new ArrayStack<>();
}
//入队一个元素
public void offer(E element){
stackA.push(element);
}
//出队一个元素
public E poll(){
if(stackA.isEmpty()){
throw new NullPointerException("queue is null");
}
while(stackA.size()!=1){
stackB.push(stackA.pop());
}
//将剩余的最后一个元素弹出来
E ret =stackA.pop();
while(!stackB.isEmpty()){
stackA.push(stackB.pop());
}
return ret;
}
public E element(){
if(stackA.isEmpty()){
throw new NullPointerException("queue is null");
}
while(stackA.size()!=1){
stackB.push(stackA.pop());
}
E ret =stackA.peek();
while(!stackB.isEmpty()){
stackA.push(stackB.pop());
}
return ret;
}
@Override
public String toString() {
if(stackA.isEmpty()){
return "[]";
}
else{
return stackA.toString();
}
}
}
package 动态数组;
public class 队列实现栈 {
public static void main(String[] args) {
StackImplByQueue stack =new StackImplByQueue<>();
for(int i=1;i<=12;i++){
stack.push(i);
}
System.out.println(stack);
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);
}
}
class StackImplByQueue{
private ArrayQueue queueA;
private ArrayQueue queueB;
public StackImplByQueue(){
queueA = new ArrayQueue<>();
queueB =new ArrayQueue<>();
}
public void push(E element){
//如果两个队列都是空
if(queueB.isEmpty()&&queueA.isEmpty()){
queueA.offer(element);
}
else if(queueA.isEmpty()){
//说明B不是空,就往B里面入队一个元素
queueB.offer(element);
}
else{
queueA.offer(element);
}
}
public E pop(){
if(queueA.isEmpty()&&queueB.isEmpty()){
throw new NullPointerException("stack is null");
}
else if(!queueA.isEmpty()){
while (queueA.size()!=1){
queueB.offer(queueA.poll());
}
return queueA.poll();
}
else {
while(queueB.size()!=1){
queueA.offer(queueB.poll());
}
return queueB.poll();
}
}
public E peek(){
if(queueA.isEmpty()&&queueB.isEmpty()){
throw new NullPointerException("stack is null");
}
else if(!queueA.isEmpty()){
while (queueA.size()!=1){
queueB.offer(queueA.poll());
}
E ret =queueA.element();
queueB.offer(queueA.poll());
return ret;
}
else{
while (queueB.size()!=1){
queueA.offer(queueB.poll());
}
E ret =queueB.element();
queueA.offer(queueB.poll());
return ret;
}
}
public String toString(){
if(queueA.isEmpty()&&queueB.isEmpty()){
return "[]";
}
else if(!queueA.isEmpty()){
return queueA.toString();
}
else{
return queueB.toString();
}
}
}
队列的顺序结构本身是由ArrayList实现的
在数据元素入队的时候,相当于在ArrayList表尾添加元素
在数据元素出队的时候,相当于在ArrayList表头删除元素
很明显,入队的时间复杂度O(1),出队的时间复杂度是O(n)
但是线性表的增删数据元素时间复杂度都是O(n),但是这个是按平均算的
优化一:
能否让队头指针和队尾指针一样随着数据元素的变化而变化呢
循环队列ArrayLoopQueue(该循环队列的实现思想是动态数组),但是由于操作元素的特殊性,并不能直接由ArrayList和ArrayQueue实现,所以从头开始定义ArrayLoopQueue
优化2:
队尾或者队头指针到达尾部,如需后移可以重新指向表头
优化三:
将一个空间留出来,专门放尾部指针
package 动态数组;
import 数据接口的实现.Queue;
import java.util.Iterator;
public class ArrayLoopQueue implements Queue {
private static int DEFAULT_SIZE = 10;
//存储元素的容器
private E[] data;
//队首指针front
private int front;
//队尾指针rear
private int rear;
//front ==rear表示队列空,(rear+1)%data.length==front表示已满
//有效元素的个数
private int size;
private static int DEFAULT_CAPACITY=10;
public ArrayLoopQueue(){
this(DEFAULT_CAPACITY);
}
//因为有一个是不能用的,一个位置是专门用来放首指针的,所以要达到capacity的效果,必须加一
public ArrayLoopQueue(int capacity){
data = (E [])new Object[capacity+1];
front=0;
rear=0;
size =0;
}
//获取循环队列的有效元素的个数
@Override
public int size() {
return size;
}
//判断循环队列是否为空
@Override
public boolean isEmpty() {
return size==0&&front==rear;
}
//向循环队列中入队一个元素
@Override
public void offer(E element) {
//判断是否需要扩容
if((rear+1)%data.length==front){
resize(data.length*2-1);//减一是因为我们要达到21的效果
}
data[rear]=element;
rear =(rear+1)% data.length;//表示下一个rear的位置,因为不能写成rear+1,因为这是一个循环的队列,所以用(rear+1)%data.length
size++;
}
private void resize(int newLength) {
E [] newData = (E [])new Object[newLength];
int index =0;
for(int i=front;i!=rear;i=(i+1)%data.length){
newData[index++] = data[i];
}
front =0;
rear = index;
data = newData;
}
//循环队列出队一个元素
@Override
public E poll() {
if(isEmpty()){
throw new NullPointerException("queue is null");
}
E ret =data[front];
front = (front+1)%data.length;//表示下一个元素的位置
size--;
//判断是否缩容
if(size==(data.length-1)/4&&data.length-1>DEFAULT_SIZE){
resize(data.length/2+1);
}
return ret;
}
@Override
public E element() {
if(isEmpty()){
throw new NullPointerException("queue is null");
}
return data[front];
}
@Override
public void clear() {
data = (E [])new Object[DEFAULT_CAPACITY+1];
front=0;
rear=0;
size =0;
}
public String toString(){
StringBuilder sb =new StringBuilder(String.format("ArrayLoopQueue:%d/%d[",size,data.length-1));
if(isEmpty()){
sb.append(']');
}
else{
for(int i=front;i!=rear;i=(i+1)%data.length){
sb.append(data[i]);
if((i+1)%data.length==rear){
sb.append(']');
}
else{
sb.append(',');
}
}
}
return sb.toString();
}
@Override
public Iterator iterator() {
return new ArrayLoopQueueIterator();
}
class ArrayLoopQueueIterator implements Iterator{
//设置游标
private int cur ;
@Override
public boolean hasNext() {
return cur!=rear;
}
@Override
public Object next() {
E ret =data[cur];
cur = (cur+1)%data.length;
return ret;
}
}
}
是限定插入和删除操作在表的两端进行的线性表,是一种具有队列和栈的性质的数据结构
双端队列接口的定义
双端队列的大致思想与循环队列一样,无非在队首科添加,在队尾可删除
这是之前所学的一个结构图
package 动态数组;
import 数据接口的实现.Deque;
import 数据接口的实现.Stack;
import java.util.Iterator;
//双端队列
//没有提供可以在任意角标处操作元素的功能
public class ArrayDeque implements Deque, Stack {
private E[] data;
private int front;//队首指针 front ==rear 双端队列为空
private int rear;//队尾指针 (rear+1)%len ==front双端队列满了
private int size;//有效元素的个数
private static int DEFAULT_SIZE =10;
//由用户指定一个容量
public ArrayDeque(int capacity){
data =(E []) new Object[capacity+1];
front=0;
rear=0;
size=0;
}
//默认无参
public ArrayDeque(){
this(DEFAULT_SIZE);
}
@Override
//在Deque的队尾添加一个元素
public void addLast(E element) {
//先判断Deque是否是满的
if(isExpansion()){
resize(data.length*2-1);
}
data[rear] = element;
rear=(rear+1)%data.length;
size++;
}
@Override
//在Deque的队首添加一个元素
public void addFirst(E element) {
//先判断Deque是否是满的
if(isExpansion()){
resize(data.length*2-1);
}
front =((front-1+data.length)%data.length);
data[front]=element;//此时的front已经变了,所以不需要减减了
size++;
}
private void resize(int newLength) {
E [] newData = (E [])new Object[newLength];
int index =0;
for(int i=front;i!=rear;i=(i+1)%data.length){
newData[index++] = data[i];
}
front =0;
rear = index;
data = newData;
}
//是否扩容的函数
private boolean isExpansion(){
return (rear+1)%data.length == front;
}
@Override
//Deque的队首删除一个元素
public E removeFirst() {
if(isEmpty()){
throw new NullPointerException("元素为空");
}
E ret = data[front];
front = (front+1)%data.length;
size--;
//再判断是否要缩容
if(isShrink()){
resize(data.length/2+1);
}
return ret;
}
private boolean isShrink() {
return size ==(data.length-1)/4&& (data.length-1)>DEFAULT_SIZE;
}
@Override
public E removeLast() {
if(isEmpty()){
throw new NullPointerException("元素为空");
}
rear = (rear-1+data.length)%data.length;
E ret = data[rear];
size--;
return ret;
}
@Override
//获取队尾元素
public E getLast() {
return data[(rear-1+data.length)%data.length];
}
@Override
//获取队首元素
public E getFirst() {
return data[front];
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size==0&&front == rear;
}
@Override
public void push(E element) {
//栈,入栈————>front当做栈底,rear当做栈顶,也可以反着来
addLast(element);
}
@Override
public E pop() {
//栈,出栈————>front当做栈底,rear当做栈顶,也可以反着来
return removeLast();
}
@Override
//获取栈顶元素
public E peek() {
//栈,获取栈顶元素————>front当做栈底,rear当做栈顶,也可以反着来
return getLast();
}
@Override
//入队操作,在队尾添加元素。。front队首,rear队尾
public void offer(E element) {
addLast(element);
}
@Override
//出队操作-->removeFirst-->front队首,rear队尾
public E poll() {
return removeFirst();
}
@Override
//队列当中获取队首元素-->front队首,rear队尾
public E element() {
return getFirst();
}
@Override
public void clear() {
data =(E []) new Object[DEFAULT_SIZE+1];
front =0;
rear =0;
size=0;
}
public String toString(){
StringBuilder sb =new StringBuilder(String.format("ArrayDeque:%d/%d[",size,data.length));
if(isEmpty()){
sb.append(']');
}
else{
for(int i=front;i!=rear;i=(i+1)%data.length){
sb.append(data[i]);
if((i+1)%data.length==rear){
sb.append(']');
}
else{
sb.append(',');
}
}
}
return sb.toString();
}
@Override
public Iterator iterator() {
return new ArrayDequeIterator();
}
//默认的迭代方向是从front到rear的,也可多从rear到front
class ArrayDequeIterator implements Iterator{
private int cur =front;
@Override
public boolean hasNext() {
return cur!=rear;
}
@Override
public E next() {
E ret = data[cur];
cur = (cur+1)%data.length;
return ret;
}
}
}
测试代码
package 动态数组;
public class TestArrayDeque {
public static void main(String[] args) {
ArrayDeque deque =new ArrayDeque<>();
System.out.println(deque);
deque.addFirst(1);
deque.addFirst(2);
deque.addFirst(3);
System.out.println(deque);
deque.addLast(1);
deque.addLast(2);
deque.addLast(3);
System.out.println(deque);
}
}