Then next, let me be reasonable!
Introduction
Stack, queue and linear table are all linear data structures. There is a linear relationship between each element, but they are different from the general linear structure.
The operations implemented on the stack are limited to the end of the table. The main operations are entering the stack, leaving the stack and taking the elements at the top of the stack
The operations on the queue are limited to the header and footer. The main operations are queue entry at the footer, queue exit at the header, and get the right header element at the header
1, Stack
1.1 concept
Stack operation has the characteristics of first in and last out
In life, such examples are everywhere. When using dishes, they always take them from top to bottom, and when washing dishes, they always stack them together from bottom to top. A pile of thick books. If you want to get the books pressed below, the safer way is to take the books above one by one, get the books you want, and put the books on it one by one.
Rule summary: no matter taking plates and books or putting plates and books, they are only carried out at the top, following the principle of putting in later and taking out first
This is how the stack is used. First in, last out, last in, first out. It is only allowed to insert elements (i.e. into the stack) and delete elements (out of the stack) in a fixed section. This end is called the top of the stack, and the other end is the bottom of the stack. The top of the stack will always change with insertion and deletion, and the bottom of the stack will always be fixed.
1.2 examples
1.2.1 impossible stack order
Title:
Given that the stacking sequence of a stack is mnxyz, what is the impossible stacking sequence?
A.mnxyz B.xnyzm C.nymxz D.nmyzx
Solution:
The focus of this topic is that you can also directly exit the stack after entering the stack
Option A: obviously, the five elements are put on the stack and then directly out of the stack
Option B: mnx in turn, x out of the stack, n out of the stack, y out of the stack after entering the stack, z out of the stack after entering the stack, m finally out of the stack, established
Option C: mn in turn, n out of the stack, xy in turn, y out of the stack. If the next one is out of the stack, it must be x instead of m. error
Option D: mn in turn, n out of the stack, m out of the stack, xy in turn, y out of the stack, z out of the stack after entering the stack, and x out of the stack at last. True
Answer: C
1.2.2 infix expression to suffix expression
There are three different notation of expressions, namely prefix, infix and suffix expression. The expression commonly used in our daily life is infix expression. The operator is in the middle of the operand, the operator of prefix expression is in front of the operand, and the operator of suffix expression is behind the operand.
For example:
Infix expression: (((2 + 3) * 4) - (6 / 2))
Prefix expression: - * + 2 3 4 / 6 2
Suffix expression: 2 3 + 4 * 6 2 /-
For people, the form of infix expression is easier to calculate. There is no way to see what is written at one time
For the computer, the calculation of prefix and suffix expressions is simpler. Therefore, when calculating expressions, the computer will convert infix expressions into prefix or suffix expressions
Take infix expression to suffix expression as an example to show the simplest conversion method:
- Add () according to the priority of the operator
- Move the operator after the corresponding bracket in turn (before the bracket if it is converted to a prefix expression)
- Remove the parentheses to get the suffix expression
Take the expression ((2 + 3) * 4) as an example
Illustration:
How suffix expressions are evaluated:
- If the suffix expression is a number, it is put on the stack in turn
- If you encounter an operator, pop up a number from the stack and put it on the right side of the operator, and then pop up a number and put it on the left side of the operator
- After calculation, put the calculation results on the stack
- Cycle back and forth until there are no numbers in the stack, and the whole calculation process is completed
1.3 self realization
The stack can be realized in the form of sequential table or linked list. Relatively speaking, the implementation of sequential table is more convenient and simple. Here, the stack is realized in the form of sequential table.
The sequential storage structure of the stack refers to the use of a group of storage units with continuous addresses to store data elements from the bottom of the stack to the top of the stack in turn. The stack stored in the sequential storage mode is called the sequential stack. Here, the class MyStack that implements this stack is called the sequential stack class
Here, a one-dimensional array int[] elem is used to store the data elements in the stack and set the appropriate initial length; Use the variable usedSized to record the number of data elements in the stack. If you want to delete an element (out of the stack), reduce it by one. If you want to enter the stack, increase it by one. If you want to empty the stack, you can make it 0
Implementation method:
-
public MyStack()
Initializes the length of the array
-
public boolean isFull()
When it is found that the number of elements in the stack is equal to the length of the array, it means that the stack is full, return true, otherwise return false
-
public void push(int val)
Push the data element val into the stack. If the stack is full, expand the capacity of the array that implements the stack, and then place the data element where the array subscript is usedSized, and increase the size of the array by one
-
public boolean empty()
Judge whether the stack is empty. When the usedSized size is found to be 0, it returns true; otherwise, it returns false
-
public int peek()
If the stack is not empty, the data element at the top of the stack is returned; If it is empty, an exception will be reported, indicating that the stack is empty
-
public int pop()
If the stack is not empty and the usedSized size is reduced by one, the data element at the top of the stack is deleted and returned; If it is empty, an exception will be reported, indicating that the stack is empty
📑 Code example:
import java.util.Arrays; public class MyStack { public int[] elem; public int usedSized; //initialization public MyStack() { this.elem = new int[100]; } //Determine whether the stack is full public boolean isFull() { return (this.usedSized == this.elem.length); } //Push public void push(int val) { if (isFull()){ this.elem = Arrays.copyOf(this.elem,this.elem.length*2); } this.elem[this.usedSized] = val; this.usedSized++; } //Determine whether the stack is empty public boolean empty() { return this.usedSized == 0; } //Get stack top element public int peek() throws RuntimeException{ if (empty()){ throw new RuntimeException("The stack is empty"); } return this.elem[this.usedSized-1]; } //Out of stack public int pop() throws RuntimeException{ if (empty()){ throw new RuntimeException("The stack is empty"); } this.usedSized--; return this.elem[this.usedSized - 1]; } }
2, Queue
1.1 concept
Queue operation has the characteristics of first in first out
Like queuing in life, it pays attention to first come, first serve and first go. In queuing events, new members join the end of the team, and the members who leave each time come from the head of the team, that is, the end of the team enters the team and the head of the team leaves the team.
This is how queues are used, first in, first out, last in, then out. The queue is only limited to inserting at one end, which is called the end of the queue. It is only limited to deleting at the other end, and the other end is called the head of the queue. The insertion operation is called in the queue, and the deletion operation is called out of the queue. With the insertion and deletion, the queue head and tail change all the time.
a1 is the team head element and an is the team tail element. The elements in the queue are queued in the order of a1, a2, a3,..., an-1 and an. According to the concept of queue, if the queue entry operation is to be performed, the queued element is an+1, which becomes the end of the queue. If the queue exit operation is performed, the queued element is a1, and the queue head is a2.
1.2 examples
1.2.1 number of students unable to eat lunch
Title:
The school buffet lunch offers round and square sandwiches, represented by the numbers 0 and 1, respectively. All the students stand in a queue. Each student likes either round or square.
The number of sandwiches in the restaurant is the same as that of students. All sandwiches are placed in a stack, each round:
If the student at the front of the queue likes the sandwich at the top of the stack, he will take it and leave the queue.
Otherwise, the student will give up the sandwich and go back to the end of the queue.
This process will continue until all the students in the queue don't like the sandwich at the top of the stack.
Here are two integer arrays, students and sandwiches, where sandwiches[i] is the type of the ith sandwich in the stack (i = 0 is the top of the stack), and students[j] is the j-th student's preference for sandwiches in the initial queue (j = 0 is the beginning of the queue). Please return the number of students who cannot have lunch.
Solution:
If this problem is solved with the idea of queue, it should be like this...
Step 1: first, judge whether the student array and sandwich array are empty to enhance the robustness of the code
Step 2: put the students' preferences for sandwiches into a queue
Step 3: enter a cycle. The condition is that the student queue is not empty and the length of the sandwich array cannot exceed (the number of sandwiches in this topic is the same as the number of students, so this condition will be true, so this condition is for different numbers)
Step 4: make a cycle judgment in the above cycle. If the preferences match successfully during the round, send the students at the head of the team away, and the j subscript of the sandwich array goes back one grid. If the elements of the j subscript of the student team head and the sandwich array are different, send them away from the head of the team and to the end of the team, and all members in the queue take turns, No one took the sandwich, which means that the size of the queue is the number of students without food
Step 5: after the cycle is completed, there is still no return. Under the condition of this question, the queue must be empty. If the number of sandwiches is different from the number of students, it may also be because the number of sandwiches is small, and the size of the queue is the number of students without food
Illustration example:
📑 Code example:
class Solution { public int countStudents(int[] students, int[] sandwiches) { //Judge whether the student array and sandwich array are empty if (students == null || students.length == 0) return 0; if (sandwiches == null || sandwiches.length == 0) return students.length; //Put the students' preferences for sandwiches into a queue Queue<Integer> queue1 = new LinkedList<>(); for (int i = 0; i < students.length; i++) { queue1.add(students[i]); } int j = 0; while (!queue1.isEmpty() && j < sandwiches.length) { int i = 0; for (; i < queue1.size(); i++) { //The student team head is different from the j subscript element of the sandwich array, so they send it away from the head of the team and to the end of the team if (queue1.peek() != sandwiches[j]) { queue1.add(queue1.poll()); }else { //Successful preference matching break; } } //All the members of the queue took turns. At this time, the size of the queue is the number of students without food if (i == queue1.size()) { return queue1.size(); } //If the preference matching is successful, the students at the head of the team will be sent away, and the j subscript of the sandwich array will go back one grid queue1.poll(); j++; } //Returns the size of the queue return queue1.size(); } }
1.3 self realization
Due to the characteristics of queue first in first out, if you want to use an ordinary one-dimensional array to implement the queue, when you want to insert elements at the end of the queue, the time complexity is O(1), but when you delete elements at the head of the queue, because the elements with subscript 0 are removed, it means that the following elements must move forward one grid collectively, and the time complexity is O(N)
Therefore, it is more advantageous to adopt the chain storage structure than the sequential storage structure. Here, we will use the linked list to realize the queue. We can use the two-way linked list or the single linked list. Here, we will use the single linked list to realize a queue, define a front node to record the head of the linked list, that is, the head of the queue, and define a rear node to record the tail of the linked list, that is, the tail of the queue
The operation of chained queue is actually a special case of inserting and deleting a single linked list. The queueing operation is equivalent to inserting a data element from the tail of the chain, and the queueing operation is equivalent to deleting a data element from the head of the chain. Both the queueing operation and the queueing operation can be realized by modifying the direction of the front node and the rear node.
Implementation legend:
Implementation method:
-
public void offer(int val)
Insert a new tail element val into the chain queue.
Step 1: generate a new node newNode with element value val
Step 2: judge whether the chain queue is empty. If it is empty, make the head node front point to the new node. If it is not empty, make the reference field of the tail node rear point to the new node
Step 3: make the tail node point to the new node
Step 4: the number of elements in the chain queue usedSize plus one
-
public int poll()
If the chain queue is empty, an exception of "empty queue" will be thrown; If it is not empty, the queue header element is taken from the queue and returned
Step 1: judge whether the queue is empty and throw an exception if it is empty
Step 2: define the variable ret to record the value of the data field of the queue head element at this time
Step 3: if there is only one node at this time, set both the head node and the tail node to null. If there is more than one node, make the head node front point to the next element it points to, that is, delete the node of the team head
Step 4: the number of elements in the chain queue usedSize minus one
Step 5: return to ret
-
public int peek()
If the chain queue is empty, an exception of "empty queue" will be thrown; If it is not empty, the queue header element is returned from the queue
Step 1: judge whether the queue is empty and throw an exception if it is empty
Step 2: return the value of the data field of the team head element pointed to by the head node front at this time
-
public boolean isEmpty()
Judge whether the chain queue is empty. If it is empty, usedSize is 0
-
public int size()
Returns the length of the chain queue
📑 Code example:
class Node { public int data; public Node next; public Node(int data) { this.data = data; } } public class MyQueueLinked { //When not initialized, the default front and rear point to null, and the usedSize is 0 private Node front; private Node rear; private int usedSize; //Queue public void offer(int val) { Node newNode = new Node(val); if (isEmpty()){ this.front = newNode; }else { this.rear.next = newNode; } this.rear = newNode; this.usedSize++; } //Out of queue public int poll() throws RuntimeException{ //Determine whether the queue is empty if (isEmpty()) { throw new RuntimeException("Queue is empty"); } int ret = this.front.data; //Judge whether it is a node if (this.front.next == null) { this.front = null; this.rear = null; }else { this.front = this.front.next; } this.usedSize--; return ret; } //Take team header element public int peek() throws RuntimeException{ //Determine whether the queue is empty if (isEmpty()) { throw new RuntimeException("Queue is empty"); } return this.front.data; } //Determine whether the chain queue is empty public boolean isEmpty() { return this.usedSize == 0; } //Returns the length of the chain queue public int size() { return this.usedSize; } }
1.4 Dual Ended queue (deque)
As the name suggests, it refers to a queue that allows both ends to enter and exit the queue, which means that elements can exit and join the team from the head of the team, or exit and join the team from the tail of the team.
In fact, after the implementation of the above queue, the implementation method of the double ended queue is basically the same. For convenience, the idea of two-way linked list is used to realize the double ended queue. I won't say much here. Go to the code!
📑 Code example:
class Node { public int data; public Node next; public Node prev; public Node(int data) { this.data = data; } } class MyDeque { public Node head;//Record the head of the queue public Node rear;//Record the end of the queue public int usedSized;//Number of elements in the queue public int capacity;//Capacity size of the queue //Initialize the capacity size of the queue public MyDeque(int k) { this.capacity = k; } //Add an element to the header of the double ended queue, and return true if the operation is successful public boolean insertFront(int value) { Node newNode = new Node(value); if (isFull()) { return false; } if (!isEmpty()) { newNode.next = this.head; this.head.prev = newNode; }else { this.rear = newNode; } this.head = newNode; this.usedSized++; return true; } //Add an element to the end of the double ended queue, and return true if the operation is successful public boolean insertLast(int value) { Node newNode = new Node(value); if (isFull()) { return false; } if (!isEmpty()) { this.rear.next = newNode; newNode.prev = this.rear; }else { this.head = newNode; } this.rear = newNode; this.usedSized++; return true; } //Delete an element from the header of the double ended queue, and return true if the operation is successful public boolean deleteFront() { if (isEmpty()) { return false; } //If there is only one node in the double ended queue if (this.head == this.rear) { this.head = null; this.rear = null; }else { this.head = this.head.next; } this.usedSized--; return true; } //Delete an element from the tail of the double ended queue, and return true if the operation is successful public boolean deleteLast() { if (isEmpty()) { return false; } //If there is only one node in the double ended queue if (this.head == this.rear) { this.head = null; this.rear = null; }else { this.rear = this.rear.prev; } this.usedSized--; return true; } //Get an element from the header of the double ended queue. If the double ended queue is empty, return - 1 public int getFront() { if (isEmpty()) return -1; return this.head.data; } //Get an element from the end of the double ended queue. If the double ended queue is empty, return - 1 public int getRear() { if (isEmpty()) return -1; return this.rear.data; } //Check whether the double ended queue is empty. The standard is that the number of data elements in the double ended queue is equal to 0 public boolean isEmpty() { return this.usedSized == 0; } //Check whether the double ended queue is full. The standard is that the number of data elements in the double ended queue is equal to the queue capacity public boolean isFull() { return this.usedSized == this.capacity; } }
3, Stacks and queues in Java
Stack and queue can be used directly in Java. The specific methods are as follows
3.1 stack in Java
method | effect |
---|---|
public E push(E item) | Push |
public synchronized E peek() | Out of stack |
public synchronized E pop() | View stack top element |
public boolean empty() | Determine whether the stack is empty |
📑 Code example:
import java.util.Stack; public class TestDemo { public static void main(String[] args) { Stack<Integer> stack = new Stack<>(); stack.push(2); stack.push(4); System.out.println(stack.peek());//4 System.out.println(stack.pop());//4 System.out.println(stack.empty());//false } }
3.2 queues in Java
effect | Mode 1 | Mode II |
---|---|---|
Queue | boolean add(E e) | boolean offer(E e) |
Out of queue | E remove() | E poll() |
View team leader element | E element() | E peek() |
📑 Code example:
import java.util.LinkedList; import java.util.Queue; public class TestDemo { public static void main(String[] args) { Queue<Integer> queue = new LinkedList<>(); queue.add(4); queue.offer(5); System.out.println(queue.element());//4 System.out.println(queue.peek());//4 System.out.println(queue.remove());//4 System.out.println(queue.poll());//5 System.out.println(queue.isEmpty());//true } }
explain:
Use LinkedList to implement the queue. Both mode 1 and mode 2 can implement the required methods. The difference is that when an error is encountered, mode 1 will throw an exception, and mode 2 will return a special value
3.3 double ended queue in Java
method | Mode 1 | Mode II |
---|---|---|
Queue | addFirst(e) addLast(e) | offerFirst(e) offerLast(e) |
Out of queue | removeFirst() removeLast() | pollFirst() pollLast() |
View element | getFirst() getLast() | peekFirst() peekLast() |
📑 Code example:
import java.util.Deque; import java.util.LinkedList; public class TestDemo { public static void main(String[] args) { Deque<Integer> deQueue = new LinkedList<>(); deQueue.addFirst(2); deQueue.addLast(3); deQueue.offerFirst(4); deQueue.offerLast(5); System.out.println(deQueue.removeFirst());//4 System.out.println(deQueue.peekLast());//5 System.out.println(deQueue.pollLast());//5 System.out.println(deQueue.getFirst());//2 System.out.println(deQueue.isEmpty());//false } }
explain:
Use LinkedList to implement the queue. Both mode 1 and mode 2 can implement the required methods. The difference is that when an error is encountered, mode 1 will throw an exception, and mode 2 will return a special value
Over!