define
A queue is a linear table with limited operation, allowing only insertion at one end and deletion at the other. The inserted end is called the end of the queue, and the deleted end is called the head of the queue. Because of this limitation, the queue has the first-in, first-out characteristic, so the queue is also a first-in, first-out linear table.
Sequential storage
The sequential storage structure of the queue, in addition to storing the array, also needs a pointer to rear and a pointer to front. When the queue is initialized, rear and front both point to the same index 0, so shi is null.
- When an element joins the queue, the tail pointer is +1
- When an element goes out of the queue, the queue head pointer +1
- In a non-empty queue, the head pointer always points to the head element, and the tail pointer always points to the next position of the tail element
In this case, false overflows occur because both the head and tail Pointers are increased and not decreased during enqueue and dequeue operations, causing the deleted element space to never be reused. That is, although the number of elements in the queue is much smaller than the size of the array, the tail pointer has exceeded the upper bound of the array, so that the queue operation cannot be performed. This phenomenon is called false overflow.
The array size is 4. During queue operation, the change process of the head and tail Pointers is shown in the following figure
Circular queue
What can be improved to overcome false overflow? The natural idea is to put the new element in the free space, back at index 0, so that it looks like a ring ending to end. This queue is called a loop queue.
We’re still adding one to the head and tail of the loop queue, but when the head and tail reach the upper bound of the array, we’re adding one back to the lower bound of 0.
// I stands for head, tail pointer
if i+1 == maxSize {
i = 0
} else {
i++
}
Copy the code
The above operation can be simplified by using the modulo operation, I = (I +1) % maxSize, which makes full use of all the space on the array and does not overflow unless the array space is full. Let’s take a look at the loop queue and see how the head and tail Pointers change.
We analyze this problem with the maximum queue capacity of 4:
- First, the state of the queue is judged by the relative positions of front and rear, so there are four situations: 0,1,2,3
- In fact, there are five states in the queue: 0 (empty queue), 1 element, 2 elements, 3 elements, and 4 elements (full queue)
- So, to use 4 cases to distinguish 5 cases, obviously, is not practical
How do you solve it? Generally, there are two schemes:
1, Use the extra tag bit, set the tag to 1 when entering the team and 0 when leaving the team, so that when front==rear, you can use the tag value to determine whether the team is empty or full
(rear+1) %maxSize == front, (rear+1) %maxSize == front, (rear+1) %maxSize == front, (rear+1) %maxSize == front, (rear+1) %maxSize == front, (rear+1) %maxSize == front (The example code below, implemented in this scenario) is shown below
Of course, you can also use chained storage to build the queue. If chained storage is used, there is no capacity problem, so there is no need to judge the queue is full.
The main operating
Structure describing
type data interface{}
type Queue struct {
list []data
front int / / the head pointer
rear int / / the tail pointer
maxSize int // Maximum capacity
}
func New(maxSize int) *Queue {
q := &Queue{
list: make([]data, maxSize+1),
front: 0,
rear: 0,
maxSize: maxSize + 1.// A spare capacity is not used
}
return q
}
Copy the code
To the full
func (q *Queue) IsFull(a) bool {
return (q.rear + 1) % q.maxSize == q.front
}
Copy the code
Sentenced to empty
func (q *Queue) IsEmpty(a) bool {
return q.front == q.rear
}
Copy the code
The team
Judge if the team is full, report an error if it is full, otherwise join the team
func (q *Queue) Enqueue(value data) (bool, error) {
if q.IsFull() {
return false, errors.New("The line is full")
}
q.list[q.rear] = value
q.rear = (q.rear + 1) % q.maxSize
return true.nil
}
Copy the code
Out of the team
If the team is empty, an error will be reported, otherwise the team will be out
func (q *Queue) Dequeue(a) (data, error) {
if q.IsEmpty() {
return nil, errors.New("Line is empty.")
}
value := q.list[q.front]
q.list[q.front] = nil
q.front = (q.front + 1) % q.maxSize
return value, nil
}
Copy the code
Gets the value of the queue header
func (q *Queue) GetHead(a) (data, error) {
if q.IsEmpty() {
return nil, errors.New("Line is empty.")}return q.list[q.front], nil
}
Copy the code
Application: Output Yang Hui triangle
Yang Hui triangle is a geometric arrangement of binomial coefficients in triangles, as shown in the figure below
- The values at the end of each line are 1
- Each number is equal to the sum of the left and right digits on the previous row. This property can be used to write the entire Yang Hui triangle, that is, the i-th number of row n+1 is equal to the sum of the i-th and i-th numbers of row n, that is, C(n+1, I)=C(n, I)+C(n,i-1).
Based on these properties, one idea when using a program to input Yang Hui’s triangle is to take two arrays, and as you output the current row, compute the value of the next row, put it in another array, and alternate between the two arrays.
In the second scenario, we can use the queue to output, which can reduce the space of an array. When using the Yang Hui triangle of queue output, there is a small trick, which is to add two zeros at both ends of each row, which is the following form
0 1 0
0 1 1 0
0 1 2 1 0
Copy the code
On this premise, the algorithm idea (n represents the number of lines) :
1. Initialize a queue and queue the first column 0, 1, 0 in sequence;
2. The number of elements in each line is N + 2, and each element in the line is output in turn, 0 is output but no output;
3, at the same time of the element out of the queue, calculate the value of the corresponding position of the next line, that is, out of the queue element + the new queue head element, and add the calculated value to the queue;
4. When each element in the line is output, the next element in the queue is calculated, and then 0 is added to the queue. This 0 is the 0 at the end of this line and the 0 at the beginning of the next line.
5. Repeat 2,3,4 until n ends.
Let’s take n = 4 as an example to see what happens to the elements in the queue:
const maxSize = 1000
func printYangHui(n int) {
q := Queue.New(maxSize)
q.Enqueue(0)
q.Enqueue(1)
q.Enqueue(0)
for i := 1; i <= n; i++ {
formatPrint(n-i) // Format output
for j := 1; j < i+2; j++ {
// Line I has I + 2 digits in the queue, including the first and last two zeros,
/ / 1 0 0
// 0 1 1 0
// 0 1 2 1 0
s, _ := q.Dequeue()
ifs ! =0 {
fmt.Printf(" %d", s)
}
t, _ := q.GetHead()
q.Enqueue(s.(int) + t.(int)) // The number in the next line is the sum of the left and right shoulders
}
q.Enqueue(0) // add 0 to each row
fmt.Println()
}
}
printYangHui(4) // The result is shown below
Copy the code
conclusion
The above application is only a small application of queue, theoretically the data flow conforms to the first-in, first-out rule, can consider using queue to solve the problem. For example, the printer’s printing schedule, the advanced content will be printed first, and so on.
Thanks!