# Go Data Structure and Algorithms - Queues

Keywords: Go Algorithm data structure

# introduce

A queue is a special linear table. A queue allows data to be inserted at one end and retrieved at the other end, and the data in the queue follows the First In First Our (FIFO) rule. One end of the retrieved data is called the head of the queue, and the other end of the inserted data is called the end of the queue.

Queues can be divided into: sequential queue, circular queue, chain queue.

# Sequential Queue

```type Queue struct {
MaxSize int
Front   int
Rear    int
Element []int
}

// Initialize Queue
func (q *Queue) initQueue(maxSize int) {
q.MaxSize = maxSize
q.Element = make([]int, maxSize)
q.Front = -1
q.Rear = -1
}

// Determine if the queue is empty
func (q *Queue) isEmpty() bool {
flag := false
if q.Front == q.Rear {
flag = true
}
return flag
}

// Add an element to the queue
func (q *Queue) add(data int) (err error) {
// Full Judgement
if q.Rear == q.MaxSize-1 {
err = errors.New("The queue is full and cannot be added!")
return err
}
q.Rear++
q.Element[q.Rear] = data
return
}

// Get an element of the queue
func (q *Queue) poll() (data int, err error) {
if q.isEmpty() {
err = errors.New("the queue is empty")
return
}
q.Front++
data = q.Element[q.Front]
return data, err
}

// View all elements of the queue
func (q *Queue) list() {
for i := q.Front + 1; i < len(q.Element)-1; i++ {
fmt.Printf("element[%d]: %d\n", i, q.Element[i])
}
}
func main() {
queue := Queue{}
var key int
var value int
var size int
for {

fmt.Println("0. Initialize Queue")
fmt.Println("2. Get data from a queue")
fmt.Println("3. Display queue data")
fmt.Println("4. Exit the program")
fmt.Scanf("%d\n", &key)
switch key {
case 0:
fmt.Println("Please enter the capacity of the initialization queue")
fmt.Scanf("%d\n", &size)
queue.initQueue(size)
case 1:
fmt.Println("Enter the data you want to add:")
fmt.Scanf("%d\n", &value)
if err != nil {
fmt.Println(err)
return
}
case 2:
data, err := queue.poll()
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Data taken from the queue is: %d\n", data)
case 3:
queue.list()
case 4:
os.Exit(0)
}
}
}

```

Sequential queues can cause false overflow because their storage units do not have a mechanism for reuse. Therefore, the space utilization of sequential queues is not too high. The solution is to design sequential queues as a circular structure.

# Circular Queue

A circular queue logically connects the end and end of the queue. This allows the location of the data removed to continue to be used in the next cycle, improving the space utilization of the queue.

The following points need attention:

1. Both the queue header and queue tail subscripts start at 0. For recycling purposes, subscripts are not suitable to start at -1 as sequential queues do.
2. When the head and tail of the queue are equal, the queue is empty.
3. When (rear + 1)% maxSize = front, the queue is full. Plus one is because there is an empty position at the end and the head of the queue. Modifying maxSize is for looping. Actual mocking is used in many places to achieve looping.
4. The number of elements in the queue is: (rear+maxSize-front)% maxSize.

For example, the head of the team is 3 and the end of the team is 1: the maximum length is 5
(rear+maxSize-front) % maxSize=(1+5-3)%5=3

5. A position is empty between the queue head and the queue tail because the pointer at the queue tail points to the next unused position. If you do not empty a position, then the situation where the queue tail pointer points to the queue head pointer occurs. This conflicts with Point 2.
```type CircleQueue struct {
MaxSize int
Front   int
Rear    int
Element []int
}

// Initialize Queue
func (cq *CircleQueue) initCircleQueue(maxSize int) {
cq.MaxSize = maxSize
cq.Element = make([]int, maxSize)
cq.Front = 0
cq.Rear = 0
}

// Determine if the queue is empty
func (cq *CircleQueue) isEmpty() bool {
flag := false
if cq.Front == cq.Rear {
flag = true
}
return flag
}

// Determine if the queue is full
func (cq *CircleQueue) isFull() bool {
flag := false
if (cq.Rear+1)%cq.MaxSize == cq.Front {
flag = true
}
return flag
}

// Insert a Data
func (cq *CircleQueue) add(data int) (err error) {
if cq.isFull() {
err = errors.New("the queue is full")
return
}
cq.Element[cq.Rear] = data
cq.Rear = (cq.Rear + 1) % cq.MaxSize
return
}

// Take out a data
func (cq *CircleQueue) poll() (data int, err error) {
if cq.isEmpty() {
err = errors.New("the queue is empty")
return
}
data = cq.Element[cq.Front]
cq.Front = (cq.Front + 1) % cq.MaxSize
return data, err
}

// Show all values in the queue
func (cq *CircleQueue) list() {
if cq.isEmpty() {
fmt.Println("the queue is empty")
return
}
// Set an auxiliary traversal because the header of the queue cannot be changed
tempFront := cq.Front
for i := 0; i < (cq.Rear+cq.MaxSize-cq.Front)%cq.MaxSize; i++ {
fmt.Printf("arr[%d]=%d\n", tempFront, cq.Element[tempFront])
tempFront = (tempFront + 1) % cq.MaxSize
}
}
```

# Chained Queue

Comparison of chained and circular queues:

• Time: All O(1)
• Spatially: A circular queue requests space in advance and does not release during use. For a chain queue, it takes some time for each request and release node, but it is more flexible in space use.
• Cyclic queues are preferred when the maximum queue length can be determined, or chained queues are preferred.
```// Queue Node
type Node struct {
element int
next    *Node
}

// Queue Chain List
Front  *Node
Rear   *Node
length int
}

// Initialize Queue
lq.Front = nil
lq.Rear = nil
lq.length = 0
fmt.Printf("Initialization succeeded!\n")
}

// Determine if the queue is empty
func (lq *LinkedQueue) isEmpty() bool {
flag := false
if lq.length == 0 {
flag = true
}
return flag
}

// Entry

// Construct a new node
node := Node{element: data}

// Determine if this is the first insert
if lq.isEmpty() {
lq.Front = &node
lq.Rear = &node
} else {
lq.Rear.next = &node
lq.Rear = &node
}
lq.length++
}

// Queue
func (lq *LinkedQueue) poll() (data int, err error) {
if lq.isEmpty() {
err = errors.New("the quenen is empty")
return
}

// Remove data from node
data = lq.Front.element

// Handle the case where the last node is left
if lq.length == 1 {
lq.Front = nil
lq.Rear = nil
lq.length--
return
}

lq.Front = lq.Front.next
lq.length--

return data, err
}

// View all elements of the queue
if lq.isEmpty() {
fmt.Println("the queue is empty")
return
}
// Because the queue head cannot be changed, all temporary variables are used instead
tempFront := lq.Front
for i := 0; i < lq.length; i++ {
fmt.Println(tempFront.element)
tempFront = tempFront.next
}
}
```

# Last

The complete code is here: https://github.com/bigzoro/go_algorithm/tree/main/queue

Posted by davejj on Fri, 15 Oct 2021 09:25:47 -0700