[learn data structure from today 02] stack and queue

Keywords: Java jvm Linux

Catalog

Let's talk about stack and queue today.

Review the previous chapter [data structure 01] array As long as the subscript of the data is known in the array, the data can be queried quickly through sequential search, and can be searched freely according to different subscripts. However, today's "stack" and queue data structure access is limited, and only one end is allowed to read, insert and delete the data. At this time, there is a great doubt about its significance. Because I will feel that compared with array and linked list, stack brings me only restrictions, and has no advantage. Then I'll just use arrays or linked lists? Why use this "operation Limited" stack? In fact, in terms of function, arrays or linked lists can really replace stacks, but you need to know that specific data structures are abstractions of specific scenes. Moreover, arrays or linked lists expose too many operation interfaces, which are flexible and free in operation, but they are relatively uncontrollable when used, and are naturally more prone to errors.

@

1. Understand stack and queue

First of all, how to understand "stack"? With a practical and popular example, when we usually put plates, we put them from the bottom to the top; when we take them, we take them from the top to the bottom, one by one. We can't take them out at will from the middle, first in, then out. This is a typical "stack" structure. When a data set only involves inserting and deleting data at one end, and meets the requirements of last in, first out, first in, and then out We should choose stack as the data structure.

Second, how to understand the queue? In the same way, with a practical and popular example, the dining hall usually queues for meals at school, and can't jump in line, first in, first out, which is a typical queue structure

2. Talk about stack with code

In fact, stacks can be implemented either by arrays or by linked lists. The stack implemented by array, we call it sequence stack, the stack implemented by linked list, we call it chain stack. Whether it's a sequential stack or a chained stack, we only need an array of size n to store data. In the process of stack in and stack out, only one or two temporary variables are needed to store space, so the space complexity is O(1).

Note that storing data here requires an array of size n, which does not mean that the spatial complexity is O(n). Because, these n spaces are necessary and cannot be omitted. So when we talk about space complexity, it means that in addition to the original data storage space, the algorithm needs additional storage space to run.

Is spatial complexity analysis very simple? Time complexity is not difficult. Whether sequential stack or chained stack, stack in and stack out only involve the operation of individual data at the top of the stack, so the time complexity is O(1).

Also, there is a concept of "stack" in JVM memory management. Stack memory is used to store local variables and method calls, and heap memory is used to store objects in Java. Is the stack in the JVM the same as the stack we are talking about here? If not, why is it called "stack"? You know, Daniel, please meet in the comments area~

2.1. Stack implemented by array: sequence stack

public class MyStack {
    
    //At the bottom of the stack, we use arrays to store data
    int[] elements;

    public MyStack() {
        elements = new int[0];
    }
    
    //Indentation element
    public void push(int element) {
        // Create a new array
        int[] newArr = new int[elements.length + 1];
        // Copy elements from the original array to the new array
        for (int i = 0; i < elements.length; i++) {
            newArr[i] = elements[i];
        }
        // Put the added element into the new array
        newArr[elements.length] = element;
        // Replace old array with new array
        elements = newArr;
    }
    
    //Take out the top element of the stack
    public int pop() {
        //There are no elements in the stack
        if(elements.length==0) {
            throw new RuntimeException("stack is empty");
        }
        //Take out the last element of the array
        int element = elements[elements.length-1];
        //Create a new array
        int[] newArr = new int[elements.length-1];
        //All elements in the original array except the last one are put into the new array
        for(int i=0;i<elements.length-1;i++) {
            newArr[i]=elements[i];
        }
        //Replace array
        elements=newArr;
        //Back to top of stack element
        return element;
    }
    
    //View top of stack elements
    public int peek() {
        //There are no elements in the stack
        if(elements.length==0) {
            throw new RuntimeException("stack is empty");
        }
        return elements[elements.length-1];
    }
    
    //Judge whether the stack is empty
    public boolean isEmpty() {
        return elements.length==0;
    }
    
}

2.2 stack of test array implementation

import demo2.MyStack;

public class TestMyStack {

    public static void main(String[] args) {
        //Create a stack
        MyStack ms = new MyStack();
        //Indentation array
        ms.push(9);
        ms.push(8);
        ms.push(7);
        //Top of stack element
        System.out.println(ms.pop());
        System.out.println(ms.pop());
        System.out.println(ms.pop());
        //View top of stack elements
//      System.out.println(ms.peek());
        System.out.println(ms.isEmpty());
    }

}

2.3. Stack based on linked list: linked stack

package stack;
/**
 * Stack based on linked list.
 */
public class StackBasedOnLinkedList {
  private Node top = null;

  public void push(int value) {
    Node newNode = new Node(value, null);
    // Judge whether the stack is empty
    if (top == null) {
      top = newNode;
    } else {
      newNode.next = top;
      top = newNode;
    }
  }

  /**
   * I use - 1 to indicate that there is no data in the stack.
   */
  public int pop() {
    if (top == null) return -1;
    int value = top.data;
    top = top.next;
    return value;
  }

  public void printAll() {
    Node p = top;
    while (p != null) {
      System.out.print(p.data + " ");
      p = p.next;
    }
    System.out.println();
  }

  private static class Node {
    private int data;
    private Node next;

    public Node(int data, Node next) {
      this.data = data;
      this.next = next;
    }

    public int getData() {
      return data;
    }
  }
}

3. Talk about queues with code

The stack only supports two basic operations: push() and pop(). The queue is very similar to the stack, and the supported operations are very limited. There are two basic operations: enqueue() to put a data to the end of the queue, dequeue() to take an element from the head of the queue. Therefore, like stack, queue is a linear table data structure with limited operation. The concept of queue is well understood and the basic operation is easy to master. As a very basic data structure, queues are widely used, especially some queues with some additional characteristics, such as cyclic queues, blocking queues, concurrent queues. They play a key role in the development of many sub bottom systems, frameworks and middleware. For example, high-performance queue Disruptor and Linux ring cache all use circular concurrent queue; Java concurrent uses ArrayBlockingQueue to implement fair lock.

Just like stack, queue can be realized by array or linked list. The stack implemented by array is called sequence stack, and the stack implemented by linked list is called chain stack. Similarly, queues implemented by arrays are called sequential queues, and queues implemented by linked lists are called linked queues

3.1. Array implementation queue: sequential queue

package queue;

// Queues implemented by arrays
public class ArrayQueue {
  // Array: items, array size: n
  private String[] items;
  private int n = 0;
  // Head is the team's head and tail is the team's tail
  private int head = 0;
  private int tail = 0;

  // Request an array of size capacity
  public ArrayQueue(int capacity) {
    items = new String[capacity];
    n = capacity;
  }

  // Join the team
  public boolean enqueue(String item) {
    // If tail == n, the queue is full
    if (tail == n) return false;
    items[tail] = item;
    ++tail;
    return true;
  }

  // Team out
  public String dequeue() {
    // If head == tail, the queue is empty
    if (head == tail) return null;
    // In order to make the students in other languages see it more clearly, put the operation on a separate line.
    String ret = items[head];
    ++head;
    return ret;
  }

  public void printAll() {
    for (int i = head; i < tail; ++i) {
      System.out.print(items[i] + " ");
    }
    System.out.println();
  }
}

3.2. Queue implemented by linked list: linked queue

package queue;

/**
 * Queue based on linked list
 */
public class QueueBasedOnLinkedList {

  // The head and tail of the queue
  private Node head = null;
  private Node tail = null;

  // Join the team
  public void enqueue(String value) {
    if (tail == null) {
      Node newNode = new Node(value, null);
      head = newNode;
      tail = newNode;
    } else {
      tail.next = new Node(value, null);
      tail = tail.next;
    }
  }

  // Team out
  public String dequeue() {
    if (head == null) return null;

    String value = head.data;
    head = head.next;
    if (head == null) {
      tail = null;
    }
    return value;
  }

  public void printAll() {
    Node p = head;
    while (p != null) {
      System.out.print(p.data + " ");
      p = p.next;
    }
    System.out.println();
  }

  private static class Node {
    private String data;
    private Node next;

    public Node(String data, Node next) {
      this.data = data;
      this.next = next;
    }

    public String getData() {
      return data;
    }
  }

}

3.2 cyclic queue

When using arrays to implement queues, there will be data movement operations, so that the performance of the queuing operations will be affected. Is there any way to avoid data movement? Let's look at the solution of circular queue.

Loop queue, as the name suggests, looks like a loop. The original array has a head and a tail, which is a straight line. Now we've connected the ends and made a ring.

package queue;
public class CircularQueue {
  // Array: items, array size: n
  private String[] items;
  private int n = 0;
  // Head is the team's head and tail is the team's tail
  private int head = 0;
  private int tail = 0;

  // Request an array of size capacity
  public CircularQueue(int capacity) {
    items = new String[capacity];
    n = capacity;
  }

  // Join the team
  public boolean enqueue(String item) {
    // The queue is full.
    if ((tail + 1) % n == head) return false;
    items[tail] = item;
    tail = (tail + 1) % n;
    return true;
  }

  // Team out
  public String dequeue() {
    // If head == tail, the queue is empty
    if (head == tail) return null;
    String ret = items[head];
    head = (head + 1) % n;
    return ret;
  }

  public void printAll() {
    if (0 == n) return;
    for (int i = head; i % n != tail; ++i) {
      System.out.print(items[i] + " ");
    }
    System.out.println();
  }
}

OK, so let's summarize the queue. The biggest characteristic of the queue is first in, first out. The main two operations are entering and leaving the queue. Just like stack, it can be implemented either by array or by linked list. The implementation of array is called sequence queue, and the implementation of linked list is called chain queue. Especially a circular queue that looks like a ring. When an array implements a queue, there will be data movement operations. To solve the problem of data movement, we need a circular queue like a ring.

If this article is helpful to you, even a little bit, please give me a compliment. Thank you~

You are welcome to pay attention to my public address, explore technology, yearn for technology and pursue technology.

Posted by jimmyhumbled on Mon, 18 Nov 2019 04:01:51 -0800