Estructura de datos_3: Cola (cola de matriz + cola circular)

Cola

Escribe al principio

  • Estructura de datos lineal primero en entrar, primero en salir (FIFO)
  • Solo permita operaciones de eliminación en la parte delantera de la tabla (parte delantera) e inserte operaciones en la parte posterior de la tabla (cola). El final que realiza la operación de inserción se denomina final de la cola, y el final que realiza la operación de eliminación se denomina cabecera de la cola.
  • Cuando no hay elementos en la cola, se denomina cola vacía.

La realización de una cola de matriz, combinada con una matriz dinámica, construye ArrayQueue <E> a modo de interfaz

  • Interfaz: cola

      public interface Queue<E> {
    
          /**
           * 获取队列容量大小
           * @return
           */
          int getSize();
      
          /**
           * 队列空判断
           * @return
           */
          boolean isEmpty();
      
          /**
           * 入队
           * @param e
           */
          void enqueue(E e);
      
          /**
           * 出队
           * @return
           */
          E dequeue();
      
          /**
           * 获取队首元素
           * @return
           */
          E getFront();
      }
    
  • Clase de implementación de interfaz: ArrayQueue <E>

      public class ArrayQueue<E> implements Queue<E>{
    
          private Array<E> array;
      
          public ArrayQueue() {
              array = new Array<>();
          }
      
          public ArrayQueue(int capacity) {
              array = new Array<>(capacity);
          }
      
          @Override
          public int getSize() {
              return array.getSize();
          }
      
          @Override
          public boolean isEmpty() {
              return array.isEmpty();
          }
      
          @Override
          public void enqueue(E e) {
              array.addLast(e);
          }
      
          @Override
          public E dequeue() {
              return array.removeFirst();
          }
      
          @Override
          public E getFront() {
              return array.getFirst();
          }
      
          @Override
          public String toString() {
              return "ArrayQueue{" +
                      "array=" + array +
                      '}';
          }
      }
    

Implementación de cola circular

  • Limitaciones de la cola del arreglo: enfóquese en la operación de quitar la cola, la complejidad del tiempo es el nivel O (n)
    • ¿Por qué se dice que la operación de sacar de cola es para el primer elemento del equipo, y la matriz subyacente moverá los elementos restantes hacia adelante después de eliminar el elemento con índice 0, lo que implica operaciones transversales, por lo que la complejidad del tiempo aumenta a O (n)? .
    • Cola circular, abandonar la operación de avance de otros elementos después de que el elemento se quita de la cola, construir el frente del puntero de la cabeza, la cola del puntero de la cola (esencialmente el tamaño de la matriz dinámica) y simplificar el movimiento hacia adelante del elemento quitado de la cola a la operación de movimiento del puntero de la cabeza (frente ++).
  • Dos puntos a tener en cuenta:
    • La cola circular está vacía: front == tail [estado inicial]
    • Se considera que la cola circular está llena: (tail + 1)% C == front [C es la longitud de la cola, desperdiciando un espacio de matriz para que el puntero de cola apunte]
  • ¿Sobre la expansión de la matriz dinámica subyacente?
    • En el artículo de la matriz dinámica, se menciona que la esencia de la expansión es abrir un nuevo espacio de memoria y copiar el contenido de la matriz original en la nueva matriz. Hay un problema en este lugar. La matriz circular hace un uso completo de la matriz espacio de la matriz, por lo que se utiliza como una cola circular. Cuando está llena, la posición donde el índice de la matriz es 0 no es necesariamente el primer elemento de la cola circular.
    • Como se muestra en la siguiente figura, la posición donde el índice de la matriz es 0 es el último elemento agregado en la cola circular. En este momento, se activa la operación de expansión de la matriz. Al copiar la matriz, debe prestar atención: ya que la cola También es una estructura lineal, los elementos deben colocarse en orden, por lo que el método de cambio de tamaño de la matriz dinámica Es necesario realizar algunos cambios.
      Inserte la descripción de la imagen aquí
  • Transforme ArrayQueue <E> y reescriba el método en combinación con la interfaz Queue
    • Cree LoopQueue <E>, complete la construcción del atributo de miembro básico

        public class LoopQueue<E> implements Queue<E> {
      
            private E[] data;
            private int front, tail;
            private int size; // 队列实际容量标识
        
            public LoopQueue(int capacity) {
                // capacity + 1 适应循环队列满载机制
                // (tail + 1) % c == front
                data = (E[]) new Object[capacity + 1];
                front = 0;
                tail = 0;
                size = 0;
            }
        
            public LoopQueue() {
                this(10);
            }
        
        	// 获取队列最大容量
            public int getCapacity() {
                return data.length - 1;
            }
        }	
      
    • getSize () Obtiene la capacidad real de la cola

        @Override
        public int getSize() {
            return size;
        }
      
    • isEmpty () Cola de juicio vacía

        @Override
        public boolean isEmpty() {
            // 队列判空条件
            return front == tail;
        }
      
    • getFront () Obtiene el elemento principal

        @Override
        public E getFront() {
            if (isEmpty()) {
                throw new IllegalArgumentException("Queue is empty");
            }
            return data[front];
        }
      
    • Reescribe resize (), regulariza la cola circular

        /**
         * 容量重置
         * @param capacity
         */
        private void resize(int capacity) {
            E[] newData = (E[]) new Object[capacity + 1];
            for (int i = 0; i < size; i++) {
                // 新数组中的元素索引相较原数组中索引存在front的偏移量
                newData[i] = data[(front + i) % data.length];
            }
            // 数组地址指向、头指针变更为默认值、尾指针指向变更
            data = newData;
            front = 0;
            tail = size;
        }
      
    • poner en cola (E e) poner en cola

        @Override
        public void enqueue(E e) {
            if ((tail + 1) % data.length == front) {
                resize(getCapacity() * 2);
            }
            data[tail] = e;
            tail = (tail + 1) % data.length;
            size ++;
        }
      
    • dequeue () dequeue

        @Override
        public E dequeue() {
            if (isEmpty()) {
                throw new IllegalArgumentException("Queue is empty");
            }
            E res = data[front];
            data[front] = null;
            front  = (front + 1) % data.length;
            size --;
        	// 四等分点进行数组缩容,避免复杂度震荡
            if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
                resize(getCapacity() / 2);
            }
            return res;
        }
      

Cola de matriz de comparación y cola circular (consideradas desde la perspectiva de poner y quitar de cola respectivamente)

  • método de prueba

      private static double testQueue(Queue<Integer> q, int opCount) {
          long startTime = System.nanoTime();
          Random random = new Random();
          for (int i = 0; i < opCount; i++) {
              q.enqueue(random.nextInt(Integer.MAX_VALUE));
          }
      	// 出队测试时使用
          for (int i = 0; i < opCount; i++) {
              q.dequeue();
          }
          long endTime = System.nanoTime();
          return (endTime - startTime) / 1000000000.0;
      }
    
  • Pruebe el método Main, defina el número de operaciones, cree objetos de cola de matriz y cola circular respectivamente

      public static void main(String[] args) {
          int opCount = 100000;
          Queue<Integer> arrayQueue = new ArrayQueue<>();
          Queue<Integer> loopQueue = new LoopQueue<>();
          System.out.println("arrayQueue:" + testQueue(arrayQueue, opCount) + " s");
          System.out.println("loopQueue:" + testQueue(loopQueue, opCount) + " s");
      }
    
  • Prueba que requiere mucho tiempo para unirse al equipo:
    Inserte la descripción de la imagen aquí

  • Prueba de entrada + salida de cola que consume mucho tiempo: la principal diferencia entre las dos colas es sacar de cola. La complejidad de la cola de matriz aumenta debido a la operación de eliminación de cola.
    Inserte la descripción de la imagen aquí
    Resumen: el resultado es obvio. El método de cola circular hace un uso razonable del espacio de la matriz y quita la cola de la operación. La complejidad del tiempo vuelve al nivel O (1), que tiene un mejor rendimiento que la cola del arreglo.

Supongo que te gusta

Origin blog.csdn.net/Nerver_77/article/details/89787019
Recomendado
Clasificación