Professional Documents
Culture Documents
Stack 3 5 2 …… 6 7
0 1 2 …. n maxn
+ trong đó:
maxn _ số phần tử tối đa xin cấp phát của ngăn xếp
n _ biến dùng để lưu trữ chỉ số phần tử ở đầu ngăn xếp
+ một hạn chế của việc sử dụng mảng là phải khai báo kích thước tối đa để dự
phòng nên gây lãng phí bộ nhớ hoặc phải thông báo tràn bộ nhớ (Full Stack) khi thực
hiện thao tác push
b) Danh sách móc nối (con trỏ):
+ mỗi nút bao gồm 2 thành phần cơ bản: elements để lưu trữ nội dung dữ liệu của
phần tử, *next là con trỏ đến nút tiếp theo trong ngăn xếp
+ thao tác bố sung và loại bỏ luôn làm việc ở nút đầu tiên thông qua việc sử dụng
một con trỏ *top trỏ đến nút đầu tiên trong ngăn xếp
+ việc sử dụng danh sách móc nối (hay con trỏ) có một ưu điểm là không phải khai
báo số phần tử tối đa của ngăn xếp để dự phòng từ đó có thể tiết kiệm được bộ nhớ
không cần thiết.
1
Cấu trúc dữ liệu và giải thuật ADT Stack
7 54 ……. 93 5 NULL
*top elements *next
2
Cấu trúc dữ liệu và giải thuật ADT Stack
Từ cái tên hậu tố cũng có thể đoán ra phần nào là theo cách biểu diễn này, các
toán tử sẽ được đặt sau các toán hạng. Cụ thể là biểu thức trung tố: 4+5 sẽ được
biểu diễn lại thành 4 5 +.
Ý tưởng là đọc biểu thức từ trái sang phải, nếu gặp một toán hạng (con số hoặc
biến) thì push toán hạng này vào ngăn xếp; nếu gặp toán tử, lấy hai toán hạng ra
khỏi ngăn xếp (stack), tính kết quả, đẩy kết quả trở lại ngăn xếp. Khi quá trình kết
thúc thì con số cuối cùng còn lại trong ngăn xếp chính là giá trị của biểu thức đó.
Ví dụ: biểu thức trung tố :
5 + ((1 + 2) * 4) + 3
được biểu diễn lại dưới dạng hậu tố là (ta sẽ bàn về thuật toán chuyển đổi từ trung
tố sang hậu tố sau):
512+4*+3+
Quá trình tính toán sẽ diễn ra theo như bảng dưới đây:
Push 3
4 Push 4 5, 3, 4
* Tính 3 * 4 5, 12
Push 12
+ Tính 12 + 5 17
Push 17
3 Push 3 17, 3
+ Tính 17 + 3 20
Push 20
3
Cấu trúc dữ liệu và giải thuật ADT Stack
Để dễ hiểu, bạn hãy quan sát quá trình thực thi của thuật toán qua một ví dụ cụ
thể sau:
Biểu thức cần chuyển đổi: 3+4*2/(1-5)
Ký tự Thao tác Stack Chuỗi hậu tố
3 Ghi 3 vào k.quả 3
+ Push + +
4 Ghi 4 vào k.quả 34
* Push * +*
2 Ghi 2 vào kquả 342
/ Lấy * ra khỏi stack, ghi vào + / 342*
k.quả, push /
( Push ( +/( 342*
1 Ghi 1 vào k.quả +/( 342*1
- Push - +/(- 342*1
5 Ghi 5 vào k.quả +/(- 342*15
) Pop cho đến khi lấy được (, + / 342*15-
ghi các toán tử pop được ra
k.quả
2 Ghi 2 ra k.quả +/ 342*15–2
Pop tất cả các toán tử ra 342*15–2 /+
khỏi ngăn xếp và ghi vào
kết quả
4
Cấu trúc dữ liệu và giải thuật ADT Stack
}
elseif(…)
{
…
}
…
}
+ Vòng lặp này thực hiện cho đến khi gặp phần tử NULL của biểu thức (kết thúc
biểu thức), nếu là ký tự rỗng (dấu cách) thì bỏ qua (tăng chỉ số phần tử của xâu
thêm một đơn vị) rồi tiếp tục cho đến khi hết các ký tự cách liền nhau, gặp chữ số
hoặc dấu chấm thì đưa vào output cho đến khi hết các ký tự này liền nhau… Vì vậy,
thuật toán chỉ duyệt qua xâu biểu thức đúng một lần thời gian tính là O(n)
thời gian tính của hàm convert(char *input, char *output) là:
O(g(n)) = max{O(n),O(n)} = O(n)
* Đánh giá độ phức tạp của hàm tinhtoan(char *bieuthuc) tính O(h(n))
Trình tự thực hiện của hàm này cũng tương tự như hàm convert. Tuy nhiên độ dài
của xâu đầu vào có sự thay đổi do hàm convert đã bỏ qua các dấu cách và đơn giản
hóa các toán tử (như sin s, cos c, ...)
Giả sử độ dài của xâu biểu thức đầu vào là m (m<n)
- thời gian tính toán các phép tính cơ bản là hằng số
- các phép toán cơ bản trên stack là hằng số
- thời gian thực hiện gán các giá trị vào các biến tạm thời cũng là hằng số
- xâu biểu thức đầu vào được duyệt qua đúng một lần
Từ đó, ta có thể dễ dàng suy ra tương tự như trên, thời gian tính của hàm
tinhtoan (char *bieuthuc) là:
O(h(n)) = O(m)
Vậy thời gian tính của toàn bộ thuật toán tính giá trị biểu thức
dạng hậu tố là:
O(f(n)) = max {O(n),O(m)} = O(n)
3- Mô tả cài đặt chương trình:
Đầu vào: Biểu thức số học dạng trung tố.
Đầu ra: Biểu thức số học dạng hậu tố và giá trị kết quả của biểu thức đó.
*Cài đặt chương trình:
Sử dụng các phép toán cơ bản xây dựng được trên kiểu dữ liệu trừu tượng ngăn
xếp, tiến hành việc chuyển đổi biểu thức số học trung tố do người dùng nhập từ bàn
phím sang biểu thức số học dạng hậu tố, tính toán giá trị của biểu thức đó và hiển
thị kết quả ra màn hình. Chương trình gồm:
- Hàm main(): thực hiện việc nhập biểu thức vào xâu input, gọi các hàm chuyển
đổi và tính toán đưa kết quả ra màn hình, tạo giao diện người dùng (có thể lặp lại
việc tính toán với biểu thức khác).
- Hàm convert(char *input,char *output): thực hiện việc phân tích xâu biểu
thức vào (input), chuyển đổi thành xâu biểu thức ra (output) là biểu thức số học
ban đầu dưới dạng hậu tố.
+ duyệt lần lượt các phần tử của xâu input.
+ bỏ qua khi gặp dấu cách.
+ nếu là chữ số hoặc dấu chấm (số thực) thì đưa vào xâu output.
+ gặp dấu mở ngoặc ‘(‘ thì đẩy vào stack.
+ gặp dấu cộng ‘+’ hoặc trừ ‘-‘ mà khi đó đỉnh ngăn xếp là dấu ‘(‘thì đẩy vào, còn
không phải là dấu mở ‘(‘ thì lấy phần tử đỉnh ra khỏi ngăn xếp trước, đưa vào xâu
output rồi mới đẩy vào stack.
5
Cấu trúc dữ liệu và giải thuật ADT Stack
+ gặp dấu nhân ‘*’ hoặc chia ‘/’: đẩy vào nếu đỉnh không phải là ‘(‘, ‘+’ hoặc ‘-‘,
lấy phần tử đỉnh ra trước đưa vào output rồi mới đẩy vào stack nếu ngược lại.
+ tương tự khi gặp các toán tử lũy thừa ‘^’, căn thức ‘v’, logarit ‘l’, và các hàm
lượng giác (sin - ‘s’, cos – ‘c’, tan – ‘t’): đẩy vào ngay nếu đỉnh không phải là các
toán tử ở trên, lấy phần tử đỉnh ra trước đưa vào output rồi mới đẩy vào stack nếu
ngược lại.
+ gặp dấu đóng ngoặc ‘)’ thì đẩy các phần tử trong ngăn xếp ra cho đến khi gặp
dấu mở ngoặc ‘(‘, đưa vào xâu output.
+ cuối cùng ta thu được xâu output là biếu thức dạng hậu tố
- Hàm tinhtoan(char *bieuthuc): thực hiện việc phân tích biểu thức dưới dạng
hậu tố, tính toán giá trị biểu thức theo thuật toán Ba Lan, trả lại giá trị là kết quả của
biểu thức ban đầu.
+ duyệt xâu chứa biểu thức dạng hậu tố.
+ duyệt từng phần tử của xâu.
+ nếu gặp dấu cách thì bỏ qua, nếu phần tử = NULL thì kết thúc.
+ gặp chữ số hoặc dấu chấm lưu ra một xâu tạm thời để thực hiện việc chuyển đổi
sang kiểu số thực đẩy vào stack.
+ khi gặp các toán tử hai ngôi như +, -, *, /, ^: đẩy lần lượt 2 phần tử ở đỉnh ngăn
xếp ra, lưu vào 2 biến tạm thời rồi tiến hành thực hiện phép tính.
+ khi gặp toán tử một ngôi như sin, cos, tan, ln, e^: thực hiện việc đẩy phần tử
đỉnh ra lưu vào biến tạm thời rồi tính.
+ lặp lại quá trình cho đến khi duyệt hết xâu (khi ngăn xếp còn duy nhất một phần
tử).
- Ngoài ra, chương trình còn có một thư viện lưu trữ cấu trúc của ngăn xếp và các
phép toán cơ bản trên ngăn xếp (file Pstack.h).
4- Hướng dẫn sử dụng:
* Các phép toán mà chương trình có thể thực hiện được:
Phép toán Biểu diễn Ví dụ
Cộng x+y 2+3
Trừ x–y 8-1
Nhân x*y 5*7
Chia x/y 9/2
Lũy thừa x^y 5^6
Lũy thừa cơ số e ex e4
Căn bậc 2 vx v25
Logarit tự nhiên lnx hoặc lx l20
sin sinx hoặc sx s4
cos cosx hoặc cx c45
tan tanx hoặc tx t32
* Một số chú ý:
- Số thực. Ví dụ: 5.67
- Số âm. Ví dụ: -6
- Các góc trong các hàm lượng giác được tính theo đơn vị radian.
- Có thể sử dụng các dấu đóng mở ngoặc. Ví dụ: (2+3)*5-7.4/(2.56-5.32)
- Các cách nhập biểu thức sau là không hợp lệ
sin-5 hợp lệ: sin(-5)
e-2 hợp lệ: e(-2)
2/-1 hợp lệ: 2/(-1)
<dấu trừ không được viết liền sau các toán tử để phân biệt số âm và toán tử ‘-‘>
vv7 hợp lệ: v(v7)
lnsin2 hợp lệ: ln(sin2)
<Các toán tử một ngôi không được viết liền nhau>