Fun 2- stacks and queues data structure

1. Stack Stack

1.1 stack features
  • Stack is a linear structure
  • From one end only additive element, the element can only be removed from the same side (top of the stack)
  • LIFO (Last In First Out, LIFO)
1.2 application stack
  • Ubiquitous undo operation (the Undo)
  • System stack calling program
  • Matching brackets ( "{} [] () [()]")

      import java.util.Stack;
    
      class Solution {
          public boolean isValid(String s) {
              Stack<Character>  stack = new Stack<>();
              if(s == null) 
                  return false;
              for(int i = 0;i<s.length();i++) {
                  char c = s.charAt(i);
                  if(c == '(' || c == '[' || c=='{') {
                      stack.push(c);
                  }
                  else {
                      if(stack.isEmpty()) {
                          return false;
                      }
                      if(c == ')' && stack.pop() != '(') 
                          return false;
                      if(c == ']' && stack.pop() != '[')
                          return false;
                      if(c=='}' && stack.pop() != '{')
                          return false;
                  }
              }
    
              return stack.isEmpty();
          }
    
          public static void main(String[] args) {
              System.out.println(new Solution().isValid("[]{}()"));
              System.out.println(new Solution().isValid("[()]{}()"));
          }
      }
1.3 stack implementation
  • Stack should have a function of:
    1) Drawing, void Push (E, E)
    2) out of the stack, E POP ()
    . 3) return to the top element, PEEK E ()
    . 4) determines whether the stack is empty, boolean isEmpty ()
    5) returns the number of elements in the stack, int getSize ()

  • Generic interface to support the definition of the stack

      public interface Stack<E> {
    
          int getSize();
          boolean isEmpty();
    
          E peek();
          E pop();
          void push(E e);
  • In the definition of dynamic array memory stack elements:

      public class DynamicArray<E> {
          private E[] data;
          private int size;
    
          // 构造函数,传入数组的容量
          public DynamicArray(int capacity) {
              data = (E[])new Object[capacity];
              size = 0;
          }
    
          // 空构造,默认数组容量capacity=10
          public DynamicArray() {
              this(10);
          }
    
          // 获取数组的容量
          public int getCapacity(){
              return data.length;
          }
    
          // 获取数组中元素个数
          public int getSize(){
              return size;
          }
    
          // 返回数组是否为空
          public boolean isEmpty() {
              return size == 0;
          }
    
          // 向所有元素后添加一个新元素
          public void addLast(E e) {
      //      if(size == data.length) {
      //          throw new IllegalArgumentException("AddLast failed. Array is full.");
      //      }
      //      
      //      data[size] = e;
      //      size++;
    
              add(size,e);
          }
    
          // 向所有元素前添加一个新元素
          public void addFirst(E e) {
              add(0,e);
          }
    
          // 向指定位置插入一个新元素e
          public void add(int index,E e) {
    
              if(index<0 || index > size) {
                  throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");            
              }
    
              // 如果存储数据长度已超过数组容量,则扩容
              if(size == data.length) 
                  resize(2*data.length);
    
              for(int i = size - 1;i>=index;i--) {
                  data[i+1] = data[i];
              }
    
              data[index] = e;
              size ++;
          }
    
          private void resize(int newCapacity) {
              E newData[] = (E[])new Object[newCapacity];
              for(int i = 0;i<size;i++) {
                  newData[i] = data[i];
              }
              data = newData;
          }
    
          // 获取index索引位置的元素
          public E get(int index) {
              if(index <0 || index >=size) {
                  throw new IllegalArgumentException("Get failed. Index is illegal.");
              }
              return data[index];
          }
    
          // 修改index索引位置的元素为e
          public void set(int index,E e) {
              if(index <0 || index >=size) {
                  throw new IllegalArgumentException("Set failed. Index is illegal.");
              }
              data[index] = e;
          }
    
          // 查找数组中是否包含元素e
          public boolean contains(E e) {
              for(int i = 0;i<size;i++) {
                  if (data[i].equals(e))
                      return true;
              }
              return false;
          }
    
          // 查找数组中元素e所在的索引,如果不存在元素e,则返回-1
          public int find(E e) {
              for(int i = 0;i<size;i++) {
                  if (data[i].equals(e))
                      return i;
              }
              return -1;      
          }
    
          // 从数组中删除index索引位置的元素,返回删除的元素
          public E remove(int index) {
              if(index<0 || index > size) {
                  throw new IllegalArgumentException("Remove failed. Index is illegal.");
              }
              E ret = data[index];
              for(int i = index + 1;i<size;i++) {
                  data [i - 1] = data[i]; 
              }
              size--;
              data[size] = null;
              // 如果存储数据的个数已小于容量的一半,则缩减容量
              // 惰性,如果存储数据的个数已小于容量的1/4,则缩减一半容量
              if(size==data.length/4) {
                  resize(data.length/2);
              }
              return ret;
          }
    
          // 从数组中删除第一个元素,返回删除的元素
          public E removeFirst() {
              return remove(0);
          }
    
          // 从数组中删除最后元素,返回删除的元素
          public E removeLast() {
              return remove(size-1);
          }
    
          // 从数组删除元素e
          public void removeElement(E e) {
              int index = find(e);
              if(index != -1)
                  remove(index);
          }
    
          @Override
          public String toString() {
              StringBuilder res = new StringBuilder();
              res.append(String.format("Array: size = %d, capacity = %d \n", size,data.length));
              res.append('[');
              for(int i = 0;i<size;i++) {
                  res.append(data[i]);
                  if(i!=size -1)
                      res.append(",");
              }
              res.append(']');
              return res.toString();
          }
    
          public E getLast() {
              return get(size-1);
          }
          public E getFirst() {
              return get(0);
          }
    
      }
  • Based on dynamic array, the array realization stack

      public class ArrayStack<E> implements Stack<E> {
          DynamicArray<E> Array;
    
          public ArrayStack(int capacity) {
              Array = new DynamicArray<>(capacity);
          }
    
          public ArrayStack() {
              Array = new DynamicArray<>();
          }
    
    
          @Override
          public int getSize() {
              return Array.getSize();
          }
    
          @Override
          public boolean isEmpty() {
              return Array.isEmpty();
          }
    
          @Override
          public void push(E e) {
              Array.addLast(e);       
          }
    
          @Override
          public E peek() {
              return Array.getLast();
          }
    
          @Override
          public E pop() {
              return Array.removeLast();
          }
    
          @Override
          public String toString() {
              StringBuilder res = new StringBuilder();
              res.append("Stack: ");
              res.append("[");
              for(int i = 0;i<Array.getSize();i++) {
                  res.append(Array.get(i));
                  if(i != Array.getSize() - 1)
                      res.append(",");
              }
              res.append("] top");
              return res.toString();
          }
    
      }
  • test

      public class Main {
    
          public static void main(String[] args) {
              ArrayStack<Integer> stack = new ArrayStack<>(); 
              for(int i = 0;i<5;i++) {
                  stack.push(i);
                  System.out.println(stack);
                  // Stack: [0] top
                  // Stack: [0,1] top
                  // Stack: [0,1,2] top
                  // Stack: [0,1,2,3] top
                  // Stack: [0,1,2,3,4] top
              }
              stack.pop();
              System.out.println(stack);
              // Stack: [0,1,2,3] top
          }
      }
1.4 Analysis of the complexity of the stack
  • void push (E) // O (1), amortized complexity (the trigger may resize operation)
  • E pop () // O (1), amortized complexity (the trigger may resize operation)
  • E peek () // O (1), returns to the top element
  • int getSize() // O(1)
  • boolean isEmpty() // O(1)

2. Queue Queue

2.1 Queue Features
  • FIFO data structure
  • Only additive element from one end (the tail), remove the element from the other end (the first team)
  • First In First Out (FIFO)
  • In real life, widely used (queue)
2.2 Array achieve queue
  • basic skills

    1) into the team, void the enqueue (E E)
    2) dequeued, E dequeue ()
    . 3) Remove the head of the queue elements, E getFront ()
    . 4) returns the queue size, int getSize ()
    . 5) determines whether the queue is empty, boolean isEmpty ()

  • Defined support generic queue interface

      public interface Queue<E> {
          int getSize();
          boolean isEmpty();
          void enqueue(E e);
          E getFront();
          E dequeue();
      }
  • Using a dynamic array defined by an array queue

      public class ArrayQueue<E> implements Queue<E> {
    
          private DynamicArray<E> array; 
          public ArrayQueue(int capacity) {
              array = new DynamicArray<>(capacity);
          }
          public ArrayQueue() {
              array = new DynamicArray<>();
          }
    
          @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() {
              StringBuilder res = new StringBuilder();
              res.append("Queue: ");
              res.append("front "+"[");
              for(int i = 0;i<array.getSize();i++) {
                  res.append(array.get(i));
                  if(i != array.getSize() - 1)
                      res.append(",");
              }
              res.append("] tail");
              return res.toString();
          }
    
          public static void main(String[] args) {
              ArrayQueue<Integer> queue = new ArrayQueue<>();
              for(int i = 0;i<10;i++) {
                  queue.enqueue(i);
                  System.out.println(queue);
                  if(i%3==2) {
                      queue.dequeue();
                      System.out.println(queue);
                  }
              }
          }
      }
2.3 Complexity analysis queue array
  • void enqueue (E) // O (1), amortized complexity (the trigger may resize operation)
  • And dequeue () // O (n)
  • E getFront () // O (1), the first team to return to the elements
  • int getSize() // O(1)
  • boolean isEmpty() // O(1)
Cyclic queue 2.4

Array queue when a team always needs to traverse the queue, the queue shifts all the elements to move forward, high complexity, so a solution is a circular queue:

  • Key 1: define front, tail position of the recording head of the queue and the tail where, when enqueued tail = (tail + 1)% length, front when dequeuing = (front + 1)% length
  • Key 2: the queue is empty when the front == tail
  • Key 3: indicates that the queue is full, additional capacity is needed when the front == (tail + 1)% length

Implementation process:

  • Generic Interface

      public interface Queue<E> {
          int getSize();
          boolean isEmpty();
          void enqueue(E e);
          E getFront();
          E dequeue();
      }
  • Static member variable definition contains an array, generic interface to achieve a dynamic circular queue

      public class LoopQueue<E> implements Queue<E> {
          private E[] data;
          private int size;
          private int front,tail;
    
          public LoopQueue(int capacity) {
              data = (E[])new Object[capacity + 1];//循环队列要预留一个空间
              front = 0;
              tail = 0;
              size = 0;
          }
          // 缺省构造
          public LoopQueue() {
              this(10);
          }
    
          @Override
          public boolean isEmpty() {
              return front == tail;
          }
    
          @Override
          public int getSize() {
              return size;
          }
    
          public int getCapacity() {
              return data.length - 1;
          }
    
          @Override
          public void enqueue(E e) {
              // 如果队列已满,扩容
              if(front == (tail + 1)%data.length) {
                  resize(getCapacity()*2);
              }
              // 将元素添加到队尾
              data[tail] = e;
              // tail 向后移一位,循环移位
              tail = (tail + 1) % data.length;
              size++;
    
          }
          private void resize(int newCapacity) {
              E[] newData = (E[])new Object[newCapacity + 1];
              for(int i =0 ; i<size;i++) {
                  // 新队列的首位存储旧队列的front
                  newData[i] = data[(i+front)%data.length];
              }
              data = newData;
              front = 0;
              tail = size;
          }
    
          @Override
          public E dequeue() {
              if(isEmpty()) {
                  throw new IllegalArgumentException("Dequeue failed. Queue is empty.");
              }
              // 取出队首元素,并将队首置空
              E res = data[front];
              data[front] = null;
              // front 向后移一位,循环
              front = (front + 1)%data.length;
              size--;
    
              // 当队列元素等于容量的1/4时,缩小一半队列容量
              if(size == getCapacity() / 4 && getCapacity() / 2 != 0){
                  resize(getCapacity() / 2);
              }
              return res;
          }
    
          @Override
          public E getFront() {
              if(isEmpty()) {
                  throw new IllegalArgumentException("Queue is empty.");
              }
              return data[front];
          }
    
          @Override
          public String toString() {
              StringBuilder res = new StringBuilder();
              res.append(String.format("LoopQueue: size = %d , capacity = %d\n", size, getCapacity()));
              res.append("front "+"[");
              // 从队首开始循环,直到队尾结束
              for(int i = front; i != tail ;i = (i+1)%data.length) {
                  res.append(data[i]);
                  // 未达到队尾时,添加间隔符
                  if((i+1)%data.length != tail)
                      res.append(",");
              }
              res.append("] tail");
              return res.toString();
          }
      }
  • test

      public static void main(String[] args) {
          LoopQueue<Integer> queue = new LoopQueue<>();
          for(int i = 0;i<10;i++) {
              queue.enqueue(i);
              System.out.println(queue);
              if(i%3==2) {
                  queue.dequeue();
                  System.out.println(queue);
              }
          }
          /*
              LoopQueue: size = 1 , capacity = 10
              front [0] tail
              LoopQueue: size = 2 , capacity = 10
              front [0,1] tail
              LoopQueue: size = 3 , capacity = 10
              front [0,1,2] tail
              LoopQueue: size = 2 , capacity = 5
              front [1,2] tail
              LoopQueue: size = 3 , capacity = 5
              front [1,2,3] tail
              LoopQueue: size = 4 , capacity = 5
              front [1,2,3,4] tail
              LoopQueue: size = 5 , capacity = 5
              front [1,2,3,4,5] tail
              LoopQueue: size = 4 , capacity = 5
              front [2,3,4,5] tail
          */
      }
2.5 Analysis of the time complexity of the circular queue
  • void enqueue (E) // O (1), amortized complexity (the trigger may resize operation)
  • E dequeue () // O (1), amortized complexity (the trigger may resize operation)
  • E getFront () // O (1), the first team to return to the elements
  • int getSize() // O(1)
  • boolean isEmpty() // O(1)

3. The array of circular queues and queues Comparison

import java.util.Random;

public class Main {
    public static double costTime(Queue<Integer> queue,int nCount) {
        
        Random random = new Random();
        long startTime = System.nanoTime();
        for(int i = 0;i<nCount;i++) {
            queue.enqueue(random.nextInt(Integer.MAX_VALUE));
        }
        for(int i = 0;i<nCount;i++) {
            queue.dequeue();
        }
        
        long endTime = System.nanoTime();
        return (endTime-startTime) / 1000000000.0 ;
        
    }

    public static void main(String[] args) {
        int nCount = 100000;
        ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
        LoopQueue<Integer> loopQueue = new LoopQueue<>();
        System.out.println("ArrayQueue:"+costTime(arrayQueue,nCount)); //ArrayQueue:5.000418012
        System.out.println("LoopQueue:"+costTime(loopQueue,nCount)); // LoopQueue:0.022053394
    }
}

4. Summary

This is the main lesson learned the two most common data structures, stacks and queues. Is a linear structure stack LIFO, the practical applications include: undo operations, function call, matching brackets, it is easy to implement by means of a dynamic array stack related functions; FIFO queue is a linear structure based on dynamic array can be achieved related functions of the queue, but the queue array complexity of the team is relatively high, therefore, with the concept of recycling, it is possible to achieve a more efficient circular queue structure, the final test also confirmed the efficiency of the circular queue.

Guess you like

Origin www.cnblogs.com/SweetZxl/p/11390674.html