数据结构动态数组之双端栈和队列基层实现

双端栈

定义:一个线性表的两端当做栈底分别进行入栈和出栈的操作,主要利用了栈"栈底位置不变,而栈顶位置动态变化"的特性。

我们把双端栈叫做ArrayDoubleEndStack,双端栈是线性表的一种,更是栈的一个特殊的分类,所以我们可以用动态数组和栈的思想来实现双端栈,毕竟由于其操作的特殊性,并不能借助ArrayList或者ArrayStack实现,所以这里需要从头开始实现双端栈

数据结构动态数组之双端栈和队列基层实现_第1张图片

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();
}

ArrayQueue类的实现

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;
        }
    }

}

双端队列

限定插入和删除操作在表的两端进行的线性表,是一种具有队列和栈的性质的数据结构

双端队列接口的定义

双端队列的大致思想与循环队列一样,无非在队首科添加,在队尾可删除

这是之前所学的一个结构图

数据结构动态数组之双端栈和队列基层实现_第2张图片

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);
    }
}

数据结构动态数组之双端栈和队列基层实现_第3张图片

 

 

 

     

 

 

 

 

 

 

 

你可能感兴趣的:(双端队列,循环队列,双端栈,队列,数据结构)