ng

ki u d ả ể ữ ừ ượ

Mô t li u tr u t ệ stack

Nội dung

 Mô tả kiểu dữ liệu trừu tượng: stack  Cài đặt  Các ứng dụng minh họa

 Phân tích cú pháp:  XHTML, C++  Lời gọi hàm  Cú pháp nghịch đảo Balan

Stack là một cấu trúc dữ liệu hoạt động theo cơ

Mô tả chế last­in—first­out (LIFO):  Mỗi lần chỉ được đưa vào hoặc lấy ra khỏi stack một

 Đối tượng được lấy ra khỏi stack là đối tượng được

đối tượng

 Các thao tác chèn và lấy ra được gọi là push và pop

chèn vào stack mới nhất

Mô tả

A

B

C

D

... M

N

top

Có hai lỗi liên quan với kiểu dữ liệu trừu tượng này:

 pop một stack rỗng hoặc  Chưa định nghĩa vị trí top của một stack rỗng

Hiện thực

Chỉ có thể chèn vào hoặc lấy ra trong

thời gian O(1) tại cuối dãy

Vì vậy, có thể hiện thực stack bằng

việc push và pop ở cuối dãy:

0

1

2

3

4

6

8

11 15 17 18 22

top

 Nếu mảng bị đầy, ta có các tùy chọn:

Errors 1. Tăng kích thước mảng, 2. Thông báo lỗi, 3. Bỏ qua phần tử đang push vào Stack, 4. Cho tiến trình ngưng lại chờ đến khi gặp thao tác

pop ra từ Stack

Stack, sử dụng mảng Giả sử Stack chứa các phần tử kiểu nguyên  Khai báo cấu trúc Stack

Typedef struct Stack {

int *arrStack; // mảng chứa các phần tử int max; // số phần tử tối đa // vị trí đỉnh Stack int top;

}

Thao tác khởi tạo Stack rỗng

Stack, sử dụng mảng

int Init(Stack &s, int maxItems) {

s.arrStack = new int(maxItems); if (s.arrStack == NULL)

return 0; // không cấp phát được bộ nhớ

s.max = maxItems; s.top = -1; // chưa có phần tử nào trong Stack return 1; // khởi tạo thành công

}

Stack, sử dụng mảng

Thao tác kiểm tra Stack rỗng

int IsEmpty(const Stack &s) {

Stack rỗng

if (s.top == -1) return 1; //

return 0; //

Stack không rỗng }

Stack, sử dụng mảng

 Thao tác kiểm tra Stack đầy

int IsFull(const Stack &s) {

if (s.top == s.max -1)

return 1; // Stack đầy return 0; // Stack chưa

đầy }

Thao tác Push: thêm 1 phần tử vào đỉnh Stack

Stack, sử dụng mảng int Push(Stack &s, int newItem) {

if (IsFull(s))

return 0; // Stack đầy, không thêm vào được

s.top++; s.arrStack[s.top] = newItem; return 1; // thêm thành công

}

Thao tác Pop: lấy ra 1 phần tử từ đỉnh Stack

Stack, sử dụng mảng

int Pop(Stack &s, int &outItem) {

if (IsEmpty(s))

return 0; // Stack rỗng, không lấy ra được

outItem = s.arrStack[s.top]; s.top--; return 1; // lấy ra thành công

}

khai báo cấu trúc 1 phần tử trong stack

Stack, sử dụng danh sách liên  kết Typedef struct tagSTACK_NODE {

int Data; tagSTACK_NODE *next;

} S_Node;

khai báo cấu trúc stack

Typedef S_Node *SList;

Stack, sử dụng danh sách liên  kết

Thao tác khởi tạo Stack rỗng

void InitStack(SList s) {

s = NULL;

}

Stack, sử dụng danh sách liên  kết

Thao tác kiểm tra Stack rỗng

int IsEmpty(SList s) {

if(s == NULL)

return 1; // Stack rỗng

return 0; // Stack không rỗng

}

Stack, sử dụng danh sách liên  kết Thao tác Push: thêm 1 phần tử vào đỉnh Stack

int Push(SList s, int newItem) {

SList pNew = new S_Node; pNew fi Data = newItem; pNew fi next = s; s = pNew; return 1; // thêm thành công

}

Stack, sử dụng danh sách liên  kết Thao tác Pop: lấy ra 1 phần tử từ đỉnh Stack

int Pop(SList s, int &outItem) {

if (IsEmpty(s))

return 0; // Stack rỗng, không lấy ra được

outItemp = s fi Data; SList temp = s; s = s fi next; delete temp; return 1; // lấy ra thành công

}

Applications  phân tích cú pháp (parsing code):  Kiểm tra các dấu ngoặc đơn  XML (e.g., XHTML)

 Trong trình biên dịch (thông dịch), khi thực hiện các

thủ tục, Stack được sử dụng để lưu môi trường của các  thủ tục   Khử đệ qui  Tổ chức lưu vết các quá trình tìm kiếm theo chiều sâu

 Xử lý các thao tác undo/redo  Cú pháp nghịch đảo Balan

và quay lui.

Applications Cách giải quyết vấn đề:  Giải quyết một vấn đề có thể dẫn đến nhiều bài toán

 Một bài toán có thể là kết quả của những bài toán nhỏ

khác nhau

 Khi các bài toán nhỏ hơn đã được giải quyết, điều ta

hơn.

quan tâm là trở về bài toán ban đầu

Parsing

Hầu hết việc phân tích cú pháp đều dùng Stack Những ví dụ gồm có:

 Đối sánh các tag trong XHTML  Đối sánh các dấu ngoặc               ( ... )  [ ... ]  { ... }

 đơn  vuông, và  kép trong C++

Phân tích cú pháp trong XHTML

XHTML sử dụng các tag lồng nhau

 Tags mở,ví dụ , ,                           đối sánh    tags đóng, ví dụ, 

Ví dụ

Hello

This appears in the browswer.

Nesting chỉ ra rằng bất kỳ tag đóng nào cũng

Phân tích cú pháp trong  XHTML phải đối sánh với tag mở gần nhất Chiến lược phân tích trong XHTML:  Đọc trang tài liệu một cách tuyến tính.  Đặt tag mở vào stack  Khi gặp một tag đóng, kiểm tra nó có đối sánh với tag

đang ở đỉnh stack ?

Phân tích cú pháp trong  XHTML

Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

<head> <html></p> <h4><head><title>Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

<head> <html></p> <h4><head><title>Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

Hello

This appears in the browswer.

Phân tích cú pháp trong  XHTML

Hello

This appears in the browswer.

Ta đã kết thúc việc phân tích cú pháp, và stack

Phân tích cú pháp trong  XHTML đã rỗng

 Những lỗi có thể xảy ra:

 Tag đóng không phù hợp với tag mở trên đỉnh  stack  Còn tag đóng khi stack rỗng  stack không rỗng khi đã ở cuối tài liệu

Phân tích cú pháp trong C++

Giống như tag mở và tag đóng, các dấu  ngoặc trong C++ cũng được lồng nhau  một cách tương tự:

int initialize( int * array, int

n )

{ for ( int i = 0; i < n; ++i ) { array[i] = 0; } }

Lời gọi hàm

Hãy xem đoạn chương trình sau:

#include using namespace std; int main() { cout << sin( -3.14 ) << endl;

return 0; }

Lời gọi hàm cũng tương tự như giải quyết vấn đề

Lời gọi hàm đã trình bày trước đây:  Ta viết một hàm để giải quyết bài toán  Hàm này cần phải giải những bài toán con nhỏ hơn, vì

 Một khi hàm con đã thực hiện xong, nó trở về nơi đã

vậy nó phải gọi hàm khác

gọi nó

Thông thường, biểu thức toán được biểu diễn dưới

Cú pháp nghịch đảo Balan

dạng sau:

(3 + 4) × 5 – 6

Toán tử được đặt giữa các toán hạng Yếu điểm: cần các dấu ngoặc ( parentheses)

= 29 = 17 = –1 = –7

(3 + 4) × 5 – 6 3 + 4 × 5 – 6 3 + 4 × (5 – 6) (3 + 4) × (5 – 6)

Thay vào đó, ta có thể đặt các toán hạng đứng

Cú pháp nghịch đảo Balan trước các toán tử:

(3 + 4) × 5 – 6 3 4 + 5 × 6 –

Đọc từ trái sang, thực hiện thao tác trên hai toán

hạng sau cùng nhất:

3 4 + 5 × 6 – 7 5 × 6 – 35 6 – 29

Cú pháp nghịch đảo Balan

Ví dụ:

3 4 5 × + 6 – 3 20 + 6 – 23 6 – 17 3 4 5 6 – × + 3 4 –1 × + 3 –4 + –1

 Ưu điểm:

Cú pháp nghịch đảo Balan  Không có sự tối nghĩa, và cũng không cần các dấu ngoặc  Tương tự với tiến trình tính toán trên máy tính:

 Các toán hạng được load vào thanh ghi trước khi thực hiện

phép toán

Cú pháp nghịch đảo Balan  Cú pháp nghịch đảo Balan, được xử lý bằng cách dùng  Stack:

 Các toán hạng được đặt vào stack.  Khi gặp toán tử:

 pop hai phần tử trên đỉnh stack,  Thực hiện thao tác, và  push kết quả vào lại stack

Cú pháp nghịch đảo Balan

Đánh giá cú pháp nghịch đảo ba lan

khi sử dụng stack:

1 2 3 + 4 5 6 × – 7 × + – 8 9

× +

Cú pháp nghịch đảo Balan

 Push 1 lên stack

× +

1

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Push 2  vào stack

× +

2 1

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Push 3

× +

3 2 1

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Pop 3 và 2, tiếp theo push 2 + 3 = 5

× +

5 1

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Push 4

× +

4 5 1

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Push 5

× +

5 4 5 1

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Push 6

× +

6 5 4 5 1

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Pop 6 và 5 sau đó push 5 × 6 = 30

× +

30 4 5 1

1 2 3 + 4 5 6 × – 7 × + – 8 9

 Pop 30 và 4 tiếp tục push 4 – 30 = –26

Cú pháp nghịch đảo Balan

–26 5 1

1 2 3 + 4 5 6 × – 7 × + – 8 9 × +

Cú pháp nghịch đảo Balan

 Push 7

× +

7 –26 5 1

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Pop 7 và –26 ; push –26 × 7 = –182

× +

–182 5 1

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Pop –182 và 5 ; push –182 + 5 = –177

× +

–177 1

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Pop –177 và 1 ; push 1 – (–177) = 178

× +

178

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Push 8

× +

8 178

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Push 9

× +

9 8 178

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Pop 9 và 8 ; push 8 × 9 = 72

× +

72 178

1 2 3 + 4 5 6 × – 7 × + – 8 9

Cú pháp nghịch đảo Balan

 Pop 72 và 178 ; push 178 + 72 = 250

× +

250

1 2 3 + 4 5 6 × – 7 × + – 8 9

Vì vậy

Cú pháp nghịch đảo Balan

1 2 3 + 4 5 6 × – 7 × + – 8 9 × +

có kết quả là giá trị tại đỉnh stack: 250

Cú pháp tương đương như sau:

((1 – ((2 + 3) + ((4 – (5 × 6)) × 7))) + (8 × 9))

Nếu sử dụng trật tự ưu tiên của toán tử:

1 – (2 + 3 + (4 – 5 × 6) × 7) + 8 × 9