Cài đặt và sử dụng Ngăn xếp – Stack trong C++

1324

Bài viết được sự cho phép bởi tác giả Sơn Dương

Bài viết hôm nay, chúng ta sẽ cùng nhau tìm hiểu một loại cấu trúc dữ liệu rất phổ biến, đó là ngăn xếp Stack.

Khi lập trình, chúng ta thường phải xử lý một lượng lớn dữ liệu thô và chưa được tổ chức. Điều này đỏi hỏi cần một cấu trúc dữ liệu để lưu dữ liệu và cho phép người dùng thao tác trên dữ liệu một cách hiệu quả.

Giới thiệu ngăn xếp Stack

Ngăn xếp Stack là một cấu trúc dữ liệu tuyến tính hoạt động theo nguyên tắc LIFO (Last IFirst Out). Tức là phần tử cuối cùng được đưa vào ngăn xếp sẽ là phần tử đầu tiên được lấy ra khỏi ngăn xếp stack khi cần.

Bạn có thể hình dung ngăn xếp stack như một chồng sách đặt trong một cái hộp. Khi thêm một sách mới thì nó sẽ được đặt lên trên, cuốn sách cuối cùng được đặt ở trên đỉnh cũng sẽ là cuốn được lấy ra đầu tiên.

ngăn xếp Stack

Như bạn thấy ở hình trên, phần tử cuối cùng sẽ là phần tử đầu tiên được lấy ra khỏi ngăn xếp stack.

Việc xóa hay thêm mới một phần tử trong stack cũng sẽ luôn thực hiện ở cùng một đầu cuối cùng của stack.

Cách thao tác với Stack

Với ngăn xếp stack, chúng ta chỉ có các thao tác xử lý dữ liệu sau:

  • push(): Thêm một phần tử mới vào ngăn xếp, số phần tử ngăn xếp tăng lên 1
  • pop(): Xóa một phần tử khỏi stack
  • top(): Trả về giá trị của phần tử trên đỉnh của stack. Số phần tử ngăn xếp không thay đổi.
  • isEmpty(): Kiểm tra ngăn xếp stack có rỗng hay không.
  • isFull(): Kiểm tra trạng thái stack đã đầy hay chưa.

  Ngăn xếp và hàng đợi trong C++

  Ý nghĩa từng câu lệnh trong C++, giải thích đơn giản, dễ hiểu

Cài đặt ngăn xếp Stack trong C/C++

Chúng ta sẽ cùng nhau tìm hiểu cơ chế hoạt động của hai thao tác phổ biến nhất trên stack là Push() và Pop().

Lưu ý: Chúng ta thiết lập một con trỏ gọi là “top” để lưu trữ vị trí của phần tử trên cùng của stack. Điều này sẽ giúp thao tác thêm mới, xóa được thực hiện hiệu quả hơn.

Thao tác PUSH (chèn mới):

  • Đầu tiên, kiểm tra stack đã đầy hay chưa. Tức kiểm tra điều kiện tràn
  • Trong trường hợp ngăn xếp stack đã đầy, thoát thao tác với thông báo: “Stack đã đầy”.
  • Ngược lại, ngăn xếp chưa đầy, con trỏ “top” tăng lên 1, sau đó dữ liệu được thêm vào stack.

Dưới đây là code minh họa.

void stack_data::push (int a)
{
    if(top >= 5)
    {
        cout << "Overflown";
    }
    else
    {
        data[++top] = a; 
    }
}

Tham khảo việc làm C++ Developer hấp dẫn tại TopDev

Thao tác POP (xóa một phần tử):

  • Trước tiên, kiểm tra xem ngăn xếp có rỗng hay không.
  • Nếu ngăn xếp bị rỗng, thoát thao tác với thông báo: “Ngăn xếp rỗng”.
  • Ngược lại, hiển thị giá trị phần tử đang được trỏ bởi con trỏ “top”. Sau đó giảm con trỏ “top” đi 1.

Code minh họa cho thao tác Pop

int stack_data::pop ()
{
    if(top < 0)
    {
        cout << "Underflown";
        return 0;
    }
    else
    {
        int pop = data[top--];
        return pop;
    }
}

Cuối cùng là chương trình cài đặt stack hoàn thiện như sau:

# include<iostream>
using namespace std;

class stack_data
{
    public:
    int top;
    int data[5]; 
    stack_data()
    {
        top = -1;
    }

    void push (int a);
    int pop ();
    void isEmpty();
    void isFull();
    void display();
};

void stack_data::push (int a)
{
    if(top >= 5)
    {
        cout << "Overflown";
    }
    else
    {
        data[++top] = a; 
    }
}

int stack_data::pop ()
{
    if(top < 0)
    {
        cout << "Underflown";
        return 0;
    }
    else
    {
        int pop = data[top--];
        return pop;
    }
}

void stack_data::isEmpty()
{
    if(top < 0)
    {
        cout << "Stack Underflown";
    }
    else
    {
        cout << "Stack can occupy elements.n";
    }
}


void stack_data::isFull()
{
    if(top >= 5)
    {
        cout << "Overflown";
    }
    else
    {
        cout << "Stack is not full.n";
    }
}


void stack_data::display()
{
    for(int i=0;i<5;i++)
    {
        cout<<data[i]<<endl;
    }
}

int main() {

    stack_data obj;
    obj.isFull();
    obj.push (40);
    obj.push (99);
    obj.push (66);
    obj.push (40);
    obj.push (11);

    cout<<"Stack after insertion of elements:n";
    obj.display();

    cout<<"Element popped from the stack:n";
    cout<<obj.pop ()<<endl;

}

Kết quả khi chạy chương trình trên sẽ như dưới đây:

Stack is not full.
Stack after insertion of elements:
40
99
66
40
11
Element popped from the stack:
11

Ứng dụng của Stack

Tùy vào mỗi yêu cầu cụ thể mà bạn sẽ sử dụng stack để giải quyết. Dưới đây là một số ứng dụng phổ biến của ngăn xếp stack.

  • Stack hay được dùng để hoán đổi vị trí của các phần tử trong Array hoặc String.
  • Một Stack có thể cần thiết trong tình huống cần Backtracking trong một số thuật toán.
  • Trong các ứng dụng kiểu như Text editor, các tính năng undo, redo có thể sẽ cần tới stack.
  • Chuyển đổi Infix/Prefix/Postfix trong Binary Trees.

Độ phức tạp của các thao tác trong Stack

Độ phức tạp của push() và pop() là như nhau, đều là: O(1)

Lý do là việc thêm mới và xóa phần tử trong stack đều thực hiện ở một đầu – đỉnh của stack.

Lời kết

Như vậy, trong bài viết này, chúng ta đã hiểu được ngăn xếp stack là gì, cách thức hoạt động cũng như ứng dụng của nó trong lập trình.

Mình hi vọng, những kiến thức này sẽ giúp ích cho bạn trong các dự án sắp tới.

Hẹn gặp lại ở bài viết sau nhé.

Bài viết gốc được đăng tải tại vntalking.com

Có thể bạn quan tâm:

Đừng bỏ lỡ công việc IT được cập nhật mỗi ngày trên TopDev