Thông thường, một cấu trúc dữ liệu được chọn cẩn thận sẽ cho phép thực hiện thuật toán hiệu quả hơn.. Một cấu trúc dữ liệu được thiết kế tốt cho phép thực hiện nhiều phép toán, sử dụng c
Trang 1Mục lục
Mục lục 1
Phần I: Mở đầu 2
I Giới thiệu đề tài 2
II Mục đích yêu cầu của đề bài 2
1 Mục đích 2
2 Yêu cầu 3
III Phương pháp nghiên cứu 3
Phần II: Nội dung 3
I Ngăn xếp (Stack) 3
II Hàng đợi (Queue) 4
III Ứng dụng của Stack và Queue trong ký pháp Ba Lan 4
1 Khái niệm: 4
2 Chuyển đổi dạng Infix(trung tố) sang Postfix(hậu tố) 5
3 Tính giá trị biểu thức dạng Postfix(hậu tố) 6
4 Chuyển đổi dạng Infix(trung tố) sang Prefix(tiền tố) 7
5 Tình giá trị biểu thức dạng Prefix(tiền tố) 8
IV Chương trình đầy đủ 9
Phần III: Kết luận 24
TÀI LIỆU THAM KHẢO 25
Trang 2Phần I: Mở đầu
I Giới thiệu đề tài
Trong khoa học máy tính, cấu trúc dữ liệu là cách lưu dữ liệu trong máy
tính sao cho nó có thể được sử dụng một cách hiệu quả Thông thường, một cấu trúc dữ liệu được chọn cẩn thận sẽ cho phép thực hiện thuật toán hiệu quả hơn
Việc chọn cấu trúc dữ liệu thường bắt đầu từ chọn một cấu trúc dữ liệu trừu
tượng Một cấu trúc dữ liệu được thiết kế tốt cho phép thực hiện nhiều phép toán,
sử dụng càng ít tài nguyên, thời gian xử lý và không gian bộ nhớ càng tốt Các cấu trúc dữ liệu được triển khai bằng cách sử dụng các kiểu dữ liệu, các tham chiếu và các phép toán trên đó được cung cấp bởi một ngôn ngữ lập trình
Trong đó nổi trội lên là hai cấu trúc dữ liệu đó là Stack (ngăn xếp) và Queue(hàng đợi) Stack và Queue có ứng dụng rất nhiều kể cả trong thuật toán lẫn trong thực tế Hàng ngày chúng ta thường xuyên làm việc và tiếp xúc với các biểu thức, toán hạng, toán tử… và máy tính cũng vậy Tuy nhiên máy tính không thể nào hiểuđược ngôn ngữ và cách viết của con người, vì vậy để máy tính hiểu được các biểu thức thì chúng ta phải chuyển chúng về một dạng mà máy tính có thể thực hiện
được Vì vậy em xin chọn đề tài “Ứng dụng ngăn xếp (Stack) và hàng đợi (Queue)
để viết chương trình biến đổi biểu thức trung tố thành tiền tố và hậu tố” để làm bài tiểu luận
II Mục đích yêu cầu của đề bài
1 Mục đích
Đề tài này giúp em củng cố, nâng cao kiến thức về môn học cấu trúc dữ liệu
và giải thuật Từ đó hiểu sâu hơn và vận dụng vào trong các bài toán số liệu thực tếđồng thời thông qua việc làm đề tài này giúp em biết được các phương pháp
Trang 32 Yêu cầu
Dùng ngôn ngữ lập trình C/C++ để cài đặt chương trình Với dữ liệu đượcnhập vào từ bàn phím
III Phương pháp nghiên cứu
+ Tham khảo tài liệu: cấu trúc dữ liệu và giải thuật, trên mạng…
+ Tìm hiểu thực tiễn, thực tế, quy cách, nhu cầu của bài toán
+ Xin ý kiến, hướng dẫn của giáo viên hướng dẫn
Phần II: Nội dung
I Ngăn xếp (Stack)
Ngăn xếp (Stack) là một danh sách có thứ tự mà phép chèn và xóa được
thực hiện tại đầu cuối của danh sách và người ta gọi đầu cuối này là đỉnh (top) của stack Với nguyên tắc vào sau ra trước, danh sách kiểu LIFO (last
- in - first - out)
Có 2 cách lưu trữ Stack:
+ Bằng mảng
+ Bằng danh sách liên kết
Các thao tác cơ bản trên Stack:
Push: Đưa một phần tử vào đỉnh của Stack
Pop: Lấy từ đỉnh của Stack một phần tử
Peek: Xem đỉnh của Stack chứa nội dung là gì?
Trang 4Trong trình soạn thảo văn bản, thao tác Undo được lưu
trong stack
Ứng dụng gián tiếp:
Cấu trúc dữ liệu bổ trợ cho thuật toán khác
Một thành phần của cấu trúc dữ liệu khác
II Hàng đợi (Queue)
Hàng đợi là kiểu danh sách tuyến tính mà phép bổ sung được thực hiện ở 1 đầu, gọi là lối sau (rear) và phép loại bỏ thực hiện ở 1 đầu khác gọi là lối
trước (front) Nguyên tắc vào trước ra trước, danh sách kiểu FIFO (first - in
Prefix: Biểu thức tiền tố được biểu diễn bằng cách đặt toán tử lên trước các
toán hạng Cách biểu diễn này còn được biết đến với tên gọi “ký pháp Ba Lan” do
Trang 5nhà toán học Ba Lan Jan Łukasiewicz phát minh năm 1920 Với cách biểu diễn
này, thay vì viết x + y như dạng trung tố, ta sẽ viết + x y Tùy theo độ ưu tiên của toán tử mà chúng sẽ được sắp xếp khác nhau, bạn có thể xem một số ví dụ ở phía sau phần giới thiệu này
Postfix: Ngược lại với cách Prefix, biểu thức hậu tố tức là các toán tử sẽ
được đặt sau các toán hạng Cách biểu diễn này được gọi là “ký pháp nghịch đảo
Ba Lan” hoặc được viết tắt là RPN(Reverse Polish notation), được phát minh vào
khoảng giữa thập kỷ 1950 bởi một triết học gia và nhà khoa học máy tính Charles Hamblin người Úc
Ví dụ:
2 Chuyển đổi dạng Infix(trung tố) sang Postfix(hậu tố)
Thuật toán:
Bước 1: Đọc một thành phần của biểu thức E (dạng Infix biểu diễn
bằng xâu, đọc từ trái qua phải) Giả sử thành phần đọc được là x
Bước 1.1: Nếu x là toán hạng thì viết nó vào bên phải biểu thức E1 (xâu
lưu
kết quả của Postfix)
Bước 1.2: Nếu x là dấu ‘(’ thì đẩy nó vào Stack.
Bước 1.3: Nếu x là một trong các phép toán +, -, *, / thì
Bước 1.3.1: Xét phần tử y ở đỉnh Stack.
Bước 1.3.2: Nếu Pri(y) >= Pri(x) là một phép toán thì loại y ra khỏi
Stack và viết y vào bên phải của E1 và quay lại bước 1.3.1
Bước 1.3.3: Nếu Pri(y) < Pri(x) thì đẩy x vào Stack.
Trang 6Bước 1.4: Nếu x là dấu ‘)’ thì
Bước 1.4.1: Xét phần tử y ở đầu của Stack.
Bước 1.4.2: y là phép toán thì loại y ra khỏi Stack, viết y vào bên
phải E1 và quay trở lại 1.4.1
Bước 1.4.3: Nếu y là dấu ‘(’ loại y ra khỏi Stack.
Bước 2: Lập lại bước 1 cho đến khi toàn bộ biểu thức E được đọc qua
Bước 3: Loại phần tử ở đỉnh Stack và viết nó vào bên phải E1 Lặp lại bước này
cho đến khi Stack rỗng
Bước 4: Tính giá trị của biểu thức dưới dạng hậu tố
Ví dụ: Cho biểu thức: E = a * (b + c) – d / e
3 Tính giá trị biểu thức dạng Postfix(hậu tố)
Thuật toán:
Bước 1: Đọc lần lượt các phần tử của biểu thức E1 (từ trái qua phải) Nếu gặp toán
hạng thì đẩy nó vào Stack Nếu gặp phép toán thì lấy hai phần tử liên tiếp trong
Stack thực hiện phép toán, kết quả được đẩy vào trong Stack
Bước 2: Lập lại bước 1 cho đến khi hết tất cả các phần tử trong biểu thức E1 lúc
đó đỉnh của Stack chứa giá trị của
Trang 7biểu thức cần tính
Bước 3: Kết thúc.
Ví dụ: tính giá trị của biểu thức sau: 4 5 + 2 3 + * 6 + 8 7 + /
Giả thuật được trình bày như sau:
4 Chuyển đổi dạng Infix(trung tố) sang Prefix(tiền tố)
Thuật toán:
Ý tưởn: Sử dụng Stack và Queue và Stackkq.
Bước 1: Đọc một thành phần của biểu thức E (dạng Infix biểu diễn bằng xâu, đọc
từ phải qua trái) Giả sử thành phần đọc được là x:
Bước 1.1: Nếu x là toán hạng thì đưa nó vào Queue.
Bước 1.2: Nếu x là dấu ‘)’ thì đẩy nó vào Stack.
Bước 1.3: Nếu x là một trong các phép toán +, -, *, / thì:
Bước 1.3.1: Kiểm tra xem stack có rỗng không? Nếu rỗng, đẩy
vào Stack, nếu không rỗng, sang bước 1.3.2
Bước 1.3.2: Lấy phần tử y ở đỉnh Stack.
Bước 1.3.3: Nếu Pre(y)>=Pre(x), đưa tất cả các phần tử trong
Queue vào Stackkq, đưa y vào Stackkq, đưa x vào Stack
Bước 1.3.4: Nếu Pre(y)<Pre(x) thì đẩy x vào Stack.
Bước 1.4: Nếu x là dấu ‘(’ thì:
Bước 1.4.1: Đưa tất cả các phần tử trong Queue vào Stackkq,
Trang 8Bước 1.4.2: Xét phần tử y ở đầu của Stack.
Bước 1.4.3: y là phép toán thì loại y ra khỏi Stack, đưa y vào
Stackkq, quay về bước 1.4.2
Bước 1.4.3: Nếu y là dấu ‘)’ loại y ra khỏi Stack.
Bước 2: Lập lại bước 1 cho đến khi toàn bộ biểu thức E được duyệt.
Bước 3: Đưa tất cả các phần tử trong Queue vào Stackkq, tất cả phần tử trong
Stack và Stackkq
Bước 4: Lấy từng phần tử trong Stackkq ra, đó là kết quả dạng Prefix.
Bước 5: Tính giá trị của biểu thức dưới dạng tiền tố.
Ví dụ: Cho biểu thức sau hãy chuyển về dạng Prefix:
E = a * b + c / dGiải thuật được trình bày như sau:
5 Tình giá trị biểu thức dạng Prefix(tiền tố)
Thuật toán:
Bước 1: Đọc lần lượt các phần tử của biểu thức E1 (từ phải qua trái)
Bước 1.1: Nếu gặp toán hạng thì đẩy nó vào Stack.
Bước 1.2: Nếu gặp phép toán thì lấy hai phần tử liên tiếp trong Stack
thực hiện phép toán, kết quả được đẩy vào trong Stack
Bước 2: Lập lại bước 1 cho đến khi hết tất cả các phần tử trong biểu thức E1 Lúc
đó đỉnh của Stack chứa giá trị của biểu thức cần tính
Bước 3: Kết thúc.
Trang 9Ví dụ: tính giá trị của biểu thức sau: /+7 8 + 6 * + 3 2 + 5 4
Giả thuật được trình bày như sau:
IV Chương trình đầy đủ
Trang 10void InitStack(STACK *stack){
stack ->top = NULL;
stack ->size = 0;
}
int IsStackEmpty(STACK *stack){
return (stack ->size == 0);
}
void PushStack(STACK *stack, Item item){
if(stack ->top == NULL){
stack ->top = (STACKNODE
*)malloc(sizeof(STACKNODE));
stack ->top ->item = item;
stack ->top ->next = NULL;
}else{
Trang 11Item PopStack(STACK *stack){
if(stack ->size > 0){
STACKNODE *t = stack ->top;
stack ->top = stack ->top ->next;
Item item = t ->item;
free(t);
stack ->size ;
return item;
}}
Item PeekStack(STACK *stack){
struct _QUEUENODE *next;
struct _QUEUENODE *prev;
} QUEUENODE;
typedef struct _QUEUE{
_QUEUENODE *front, *rear;
int size;
} QUEUE;
void InitQueue(QUEUE *queue){
queue ->front = NULL;
queue ->rear = NULL;
queue ->size = 0;
Trang 12int IsQueueEmpty(QUEUE *queue){
return (queue ->size == 0);
}
void PushQueue(QUEUE *queue, Item item){
if(queue ->front == NULL && queue ->rear == NULL){
queue ->front = (QUEUENODE
*)malloc(sizeof(QUEUENODE));
queue ->front ->item = item;
queue ->front ->next = NULL;
queue ->front ->prev = NULL;
queue ->rear = queue ->front;
}else{
}
Item PopQueue(QUEUE *queue){
if(queue ->size != 0){
QUEUENODE *t = queue->rear;
if(queue ->rear == queue->front){
queue ->rear = NULL;
Trang 13queue ->front = NULL;
}else{
queue ->rear = queue ->rear ->prev;
queue ->rear ->next = NULL;
}queue ->size ;
Item i = t ->item;
free(t);
return i;
}}
Item PeekQueue(QUEUE *queue)
{
if(queue->size > 0)
return queue->front->item;
}
int DocSo(char str[], int &i){
int len = strlen(str);
t[j++] = '\0';
break;
}
Trang 14}return atoi(t);
}
void Tach(char str[], QUEUE *queue){
int len = strlen(str);
item.value = str[i];
item.type = PARENT_OPEN;
PushQueue(queue, item);
}else if(str[i] == ')'){
item.value = str[i];
item.type = PARENT_CLOSE;
PushQueue(queue, item);
}else if(str[i] >= '0' && str[i] <= '9'){
item.value = DocSo(str, i);
item.type = OPERAND;
PushQueue(queue, item);
i ;
Trang 15}}
// chuyen tu bieu thuc trung to sang hau to
void Convert(QUEUE *queue, QUEUE *output){
int size = queue ->size;
Trang 16while(priority(item.value) <=
priority(PeekStack(&stack).value) && stack.size > 0){
Item iTemp = PopStack(&stack);
PushQueue(output, iTemp);
}PushStack(&stack, item);
break;
}}
while(stack.size > 0) {
item = PopStack(&stack);
PushQueue(output, item);
}}
void Print(QUEUE *queue){
QUEUENODE *i;
Trang 17}
// tinh gia tri cua bieu thuc hau to
float Calculate(QUEUE *queue){
Trang 18item.type = OPERAND;
PushStack(&stack, item);
}}
return PopStack(&stack).value;
}
// chuyen bieu thuc trung to sang tien to
void Convert2(QUEUE *queue, QUEUE *output){
int size = queue ->size;
Trang 19priority(PeekStack(&stack).value) && stack.size > 0){
Item iTemp = PopStack(&stack);
PushQueue(output, iTemp);
}PushStack(&stack, item);
break;
case PARENT_CLOSE:{
Trang 20PushStack(&stack, item);
}
break;
}}
while(stack.size > 0){
item = PopStack(&stack);
PushQueue(output, item);
}}
void Print2(QUEUE *queue){
printf("\n");
}
// tinh gia tri cua bieu thuc tien to
Trang 21float Calculate2(QUEUE *queue){
Trang 22result = a / b;
}item.value = result;
item.type = OPERAND;
PushStack(&stack, item);
}}
Trang 23Kết quả như sau:
Nhận xét: vẫn còn rất nhiều cách khác thuật toán khác để có thể áp dụng chuyển
1 biểu thức trung tố sang hậu tố và tiền tố, tính giá trị của biểu thức tiền tố và hậu
tố Thế nhưng đây là cách đầy đủ và rõ ràng nhất Nó áp dụng rất tốt cho nhưng
người đang lập trình Bởi vì nó bao gồm đầy đủ, sử dụng cả Stack và Queue
Trang 24Phần III: Kết luận
Thông qua việc tìm hiểu về ứng dụng của Stack và Queue để để viết chươngtrình biến đổi biểu thức trung tố thành tiền tố, hậu tố và tính giá trị của biểu thứctiền tố và hậu tố em đã rút ra được rất nhiều điều Từ cách xác định được giải thuậtđến tư duy logic và chương trình đã chạy thành công trên phần mềm Devcpp++5.11 bằng ngôn ngữ C/C++ Rút ra được cách làm một bài tiểu luận đúng vàchuẩn
Có được kết quả trên là nhờ có sự dạy dỗ chu đáo của cô giáo Trịnh ThịPhú trong quá trình truyền thụ kiến thức môn học cấu trúc dữ liệu và giải thuật,đồng thời cô cũng là người hướng dẫn tận tình trong suốt quá trình thực hiện đề tàitiểu luận môn học này
Em xin chân thành cám ơn cô!
Thanh Hóa, ngày….tháng 11 năm 2015
Giáo viên hướng dẫn
Ths Trịnh Thị Phú
Sinh viên thực hiện
Hoàng Năng Hưng
Trang 25TÀI LIỆU THAM KHẢO
[1] Giáo trình cấu trúc dữ liệu và giải thuật, Đỗ Xuân Lôi, NXB Đại học Quốc Gia
Hà Nội
[2] Lập trình C căn bản, Hanoi Aptech Computer Education Center
[3] Gs Phạm Văn Ất Kỹ thuật lập trình C cơ sở và nâng cao, NXB Giao thông
vận tải Hà Nội – 2006