Thực hành cài đặt và sử dụng Hàng đợi – Queue trong C++

0
Dịch vụ dạy kèm gia sư lập trình
Bài này thuộc phần 6 của 9 phần trong series Cấu trúc dữ liệu và giải thuật

Tiếp tục series về thuật toán chuyên sâu, chúng ta cùng nhau tìm hiểu về một loại cấu trúc dữ liệu rất phổ biến, đó là Hàng đợi (Queue).

Hàng đợi – Queue là kiểu cấu trúc dữ liệu tuyến tính, việc truy xuất dữ liệu của nó theo nguyên tắc FIFO, tức là dữ liệu vào trước thì lấy trước, vào sau lấy sau.

Thực hành cài đặt và sử dụng Hàng đợi - Queue trong C++

Với Queue, phần tử đầu tiên được đưa vào Queue cũng sẽ là phần tử đầu tiên được xóa nếu có yêu cầu. Tức là, nó không cho phép bạn truy xuất trực tiếp dữ liệu nằm ở giữa queue.

Lý thuyết Queue

Queue – hàng đợi nó mô phỏng lại văn hóa xếp hàng của con người chúng ta thôi. Khi chúng ta đến một tiệm cắt tóc, hôm nay khá là đông khách nên mọi người phải xếp hàng. Ai đến trước thì được cắt trước, cứ lần lượt từng người một. Ai mà bon chen đến sau mà đòi cắt trước thì chỉ có cắt… cái đầu gối thôi 😊

Nguyên lý hoạt động của Queue là FIFO (First In, First Out), tức là vào trước ra trước.

Lý thuyết hàng đợi - queue

Về cơ bản, cấu trúc dữ liệu kiểu Queue cung cấp một số thao tác để làm bạn như sau:

  • enqueue: Thêm phần tử vào queue, tất nhiên phần tử này sẽ vào cuối của queue
  • dequeue: Xóa phần tử đầu trong queue. Nếu queue rỗng thì thông báo lỗi.
  • isEmpty: Kiểm tra xem queue có rỗng không?
  • isFull: Kiểm tra queue đã đầy hay chưa?
  • peek: Lấy giá trị của phần tử đầu trong queue, chỉ lấy giá trị ra chứ kg có xóa phần tử, queue không thay đổi.

Khi nào sử dụng Queue?

Sau khi hiểu được định nghĩa cũng như nguyên lý hoạt động của Queue rồi, chúng ta sẽ đặt ngay câu hỏi, vậy Queue được ứng dụng trong những bài toán nào? Khi nào thì sử dụng Queue mà không phải Stack?

Để trả lời cho câu hỏi này một cách chính xác 100% thì thật là khó? Việc vận dụng một công cụ trong các bài toán thực tế là vô cùng đa dạng. Với những đúc kết kinh nghiệm thực tiễn và qua nghiên cứu, mình thấy Queue thường sử dụng trong các trường hợp sau:

  • Sử dụng queue để lưu trữ tạm các dữ liệu, thực hiện tính toán các biểu thức toán học.
  • Ứng dụng trong các bài toán về in ấn.
  • Quản lý các request trên một tài nguyên được chia sẻ, ví dụ như lập lịch CPU (CPU scheduling)
  • Routers và switches trong mạng networking
  • Quản lý các bài hát (media playlist) trong các chương trình đa phương tiện (Media players)
  • Và còn rất nhiều ứng dụng khác nữa.v.v… (các bạn bổ sung thêm ở phần bình luận nhé)

💦 Đọc thêm về các thuật toán:

Thực hành Implement một queue bằng C++

Ở phần này, chúng ta sẽ cùng nhau cài đặt các chức năng chính của một queue (như đã đề cập ở phần trên). Có nhiều cách để bạn tạo queue, nhưng sau khi tham khảo rất nhiều ví dụ, mình nghĩ sử dụng mảng để tạo queue là cách đơn giản nhất.

Để đỡ dài dòng lan man, mình sẽ comment thẳng vào trong code để giải thích nhé.

1. Cài đặt Queue

#include <iostream>
#include <cstdlib>
using namespace std;
 
// Define the default capacity of a queue
#define SIZE 1000
 
// A class to store a queue
class Queue
{
    int *arr;       // array to store queue elements
    int capacity;   // maximum capacity of the queue
    int front;      // front points to the front element in the queue (if any)
    int rear;       // rear points to the last element in the queue
    int count;      // current size of the queue
 
public:
    Queue(int size = SIZE);     // constructor
    ~Queue();                   // destructor
 
    int dequeue();
    void enqueue(int x);
    int peek();
    int size();
    bool isEmpty();
    bool isFull();
};
 
// Constructor to initialize a queue
Queue::Queue(int size)
{
    arr = new int[size];
    capacity = size;
    front = 0;
    rear = -1;
    count = 0;
}
 
// Destructor to free memory allocated to the queue
Queue::~Queue() {
    delete[] arr;
}

2. Kiểm tra kích thước queue

// Utility function to return the size of the queue
int Queue::size() {
    return count;
}

3. isEmpty – Kiểm tra queue có rỗng không?

// Utility function to check if the queue is empty or not
bool Queue::isEmpty() {
    return (size() == 0);
}

4. isFull – Kiểm tra xem queue đã đầy hay chưa?

// Utility function to check if the queue is full or not
bool Queue::isFull() {
    return (size() == capacity);
}

5. enqueue – Thêm mới phần tử

// Utility function to add an item to the queue
void Queue::enqueue(int item)
{
    // check for queue overflow
    if (isFull())
    {
        cout << "Overflow\nProgram Terminated\n";
        exit(EXIT_FAILURE);
    }
 
    cout << "Inserting " << item << endl;
 
    rear = (rear + 1) % capacity;
    arr[rear] = item;
    count++;
}

6. dequeue – Xóa phần tử

// Utility function to dequeue the front element
int Queue::dequeue()
{
    // check for queue underflow
    if (isEmpty())
    {
        cout << "Underflow\nProgram Terminated\n";
        exit(EXIT_FAILURE);
    }
 
    int x = arr[front];
    cout << "Removing " << x << endl;
 
    front = (front + 1) % capacity;
    count--;
 
    return x;
}

7. peek– lấy giá trị của phần tử đầu tiên trong queue

int Queue::peek()
{
    if (isEmpty())
    {
        cout << "Underflow\nProgram Terminated\n";
        exit(EXIT_FAILURE);
    }
    return arr[front];
}

8. Sử dụng queue

int main()
{
    // create a queue of capacity 5
    Queue q(5);
 
    q.enqueue(1);
    q.enqueue(2);
    q.enqueue(3);
 
    cout << "The front element is " << q.peek() << endl;
    q.dequeue();
 
    q.enqueue(4);
 
    cout << "The queue size is " << q.size() << endl;
 
    q.dequeue();
    q.dequeue();
    q.dequeue();
 
    if (q.isEmpty()) {
        cout << "The queue is empty\n";
    }
    else {
        cout << "The queue is not empty\n";
    }
 
    return 0;
}

Toàn bộ code minh họa trong bài viết được để tại đây:

Trên đây là những giaỉ thích lý thuyết và cách để bạn cài đặt một queue. Mình hi vọng, nó sẽ ích cho bạn trong các bài tập thực tế sau này.

Xem tiếp các bài trong Series
Phần trước: Thuật toán đảo ngược chuỗi liên kết (Linked List)Phần kế tiếp: Thuật toán tìm kiếm nhị phân – Binary Search Tree (BTS)
Dịch vụ phát triển ứng dụng mobile giá rẻ - chất lượng
Bài trước[Design Pattern] Ứng dụng Adapter Design Pattern trong dự án
Bài tiếp theoThực hành Deploy dự án Nodejs từ A-Z bằng Docker
Tên đầy đủ là Dương Anh Sơn. Tốt nghiệp ĐH Bách Khoa Hà Nội. Mình bắt đầu nghiệp coder khi mà ra trường chẳng xin được việc đúng chuyên ngành. Mình tin rằng chỉ có chia sẻ kiến thức mới là cách học tập nhanh nhất. Các bạn góp ý bài viết của mình bằng cách comment bên dưới nhé !

Bình luận. Cùng nhau thảo luận nhé!

avatar
  Theo dõi bình luận  
Thông báo