1. Trang chủ
  2. » Công Nghệ Thông Tin

cấu trúc dữ liệu và giải thuật_ bài tập lớn

6 2K 24

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 6
Dung lượng 118,5 KB

Nội dung

Định nghĩa: *ADT Abstract Data Types – kiểu dữ liệu trừu tượng bao gồm: - Tập các giá trị đối tượng - Tập các phép toán có thể thực hiện với tất cả các giá trị này - Cách biểu diễn dữ li

Trang 1

Bài tập lớn

CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT

Đề tài: ADT Stacks [Ngăn xếp]

-o0o-Mai Xuân Cường - Nguyễn Trung Dũng A - Hoàng Mạnh Hùng

Nguyễn Thị Thu Nga - Vũ Thị Quỳnh Trang - Ngô Anh Tuấn - Nguyễn Tuấn

***

I Định nghĩa:

*ADT (Abstract Data Types) – kiểu dữ liệu trừu tượng bao gồm:

- Tập các giá trị (đối tượng)

- Tập các phép toán có thể thực hiện với tất cả các giá trị này

- Cách biểu diễn dữ liệu được sử dụng chung cho tất cả các giá trị này

*Stack (ngăn xếp): là một kiểu dữ liệu trừu tượng, một dạng đặc biệt của danh

sách tuyến tính (dãy gồm 0 hoặc nhiều hơn các phần tử cùng kiểu cho trước) trong

đó các đối tượng được nạp vào (push) và lấy ra (pop) chỉ từ một đầu gọi là đỉnh (top) của danh sách

 nguyên tắc: vào sau ra trước (LIFO – last in first out)

- Đối tượng cất giữ: các phần tử - số (nguyên, thực), ký

tự, xâu (string), mảng (array), bản ghi (record), file

- Các phép toán cơ bản:

+ push  đẩy phần tử vào ngăn xếp

+ pop  lấy phần tử ra khỏi ngăn xếp

+ isEmpty  kiểm tra ngăn xếp rỗng

+ size  trả về số phần tử của ngăn xếp

+ top  trả về giá trị của phần tử nằm ở đầu danh sách mà không lấy nó ra khỏi

ngăn xếp

II Cài đặt:

1- Các cấu trúc dữ liệu để biểu diễn và cài đặt các phép toán:

a) Mảng:

+ nạp các phần tử theo thứ tự từ trái sang phải

+ có biến lưu trữ chỉ số của phần tử ở đầu ngăn xếp

+ 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

Trang 2

7 54 …… 93 5 NULL

*top elements *next

2- Chương trình minh họa:  file: StackPtr.cpp, STACKARR.cpp

III Ứng dụng:

1- Phát biểu bài toán:

*Tính giá trị biểu thức số học:

Biểu thức số học xuất hiện rất nhiều trong cuộc sống và việc tính toán giá trị của các biểu thức đó là điều tất yếu, quan trọng Ví dụ:

- Khi sử dụng máy tính (calculator), người dùng sẽ nhập các biểu thức số học cần tính thông qua các phím số và phép toán có sẵn Nhiệm vụ của máy sẽ tính toán và hiển thị kết quả chính xác ra màn hình

- Khi sử dụng các bảng tính điện tử (như Excel, …) để lập các bảng tính lương, thống kê việc thu chi của doanh nghiệp…, người dùng sẽ nhập các biểu thức số học vào các ô tính Khi đó, chương trình sẽ có nhiệm vụ phân tích và tính toán rồi đưa ra kết quả tại ô đó

- Hay trong các ngôn ngữ lập trình (như C, PASCAL…), ta đã khá quen thuộc với các lệnh gán

X=<biểu thức số học> //X là biến Khi thực hiện chương trình, gặp các lệnh gán, máy tính cần phải xác định giá trị của các biểu thức số học đó và gán kết quả cho biến X

Vì vậy, việc xây dựng một chương trình tính toán giá trị của các biểu thức số học là cần thiết

Biểu thức số học là một dãy các toán hạng (hằng (số), biến hoặc hàm) nối với nhau bởi các phép toán số học (như cộng, trừ, nhân, chia, lũy thừa, căn thức, ) Trong các biểu thức có thể chứa các dấu ngoặc tròn để xác định thứ tự ưu tiên Khi đó các phép toán trong ngoặc sẽ được thực hiện trước các phép toán ở ngoài

Các phép toán số học được phân ra làm 2 loại:

- Các phép toán hai ngôi (+, -, *, /, ^,…): mỗi phép toán được đặt giữa các toán hạng Ví dụ: 2+3, 56*12, 3^8,…

- Các phép toán một ngôi (căn, logarit, các hàm lượng giác: sin, cos, tg,…): mỗi phép toán đi ngay trước toán hạng Ví dụ: sin4, ln7,…

Hơn nữa, khi tính các biểu thức số học, ta cần phải tuân theo những quy tắc sau:

Thứ tự ưu tiên: ^  *, /  +,

Quy tắc kết hợp: cho biết khi có 2 phép toán có cùng thứ tự ưu tiên thì sẽ thực hiện phép toán nào trước

+ ^: thực hiện từ phải qua trái

+ + - * /: thực hiện từ trái qua phải

+ dấu () được ưu tiên hơn cả thứ tự ưu tiên và quy tắc kết hợp

2- Mô tả thuật toán giải và đánh giá độ phức tạp của thuật toán:

a) Mô tả thuật toán giải:

Bài toán tính toán giá trị của biểu thức số học được xây dựng một cách dễ dàng bằng thuật toán Ba Lan trên cơ sở ứng dụng của kiểu dữ liệu trừu tượng ngăn xếp (stack)

*Ký pháp nghịch đảo Ba Lan:

Ký pháp nghịch đảo Ba Lan được phát minh vào khoảng giữa thập kỷ 1950 bởi Charles Hamblin - một triết học gia và khoa học gia máy tính người Úc - dựa theo công trình về ký pháp Ba Lan của nhà Toán học người Ba Lan Jan Łukasiewicz Hamblin trình bày nghiên cứu của mình tại một hội nghị khoa học vào tháng 6 năm

1957 và chính thức công bố vào năm 1962

Trang 3

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):

5 1 2 + 4 * + 3 +

Quá trình tính toán sẽ diễn ra theo như bảng dưới đây:

Push 12

5, 12

Push 17

17

Chuyển đổi từ trung tố sang hậu tố:

Thuật toán chuyển đổi này được phát minh bởi vị giáo sư người Đức nổi tiếng Edsger Dijkstra (cũng là tác giả của thuật toán tìm đường đi ngắn nhất được đặt theo tên ông và semaphore, một kỹ thuật để đồng bộ các tiến trình trong lập trình đa nhiệm) Thuật toán này cũng dựa theo cơ chế ngăn xếp Ý tưởng chung của thuật toán cũng là duyệt 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ì ghi nó vào chuỗi kết quả (chuỗi kết quả là biểu thức trung tố)

- Nếu gặp dấu mở ngoặc, đưa nó vào stack

- Nếu gặp một toán tử (gọi là o1 ), thực hiện hai bước sau:

+ Chừng nào còn có một toán tử o2 ở đỉnh ngăn xếp VÀ độ ưu tiên của o1 nhỏ hơn hay bằng độ ưu tiên của o2 thì lấy o2 ra khỏi ngăn xếp và ghi vào kết quả

+ Push o1 vào ngăn xếp

- Nếu gặp dấu đóng ngoặc thì cứ lấy các toán tử trong ngăn xếp ra và ghi vào kết quả cho đến khi lấy được dấu mở ngoặc ra khỏi ngăn xếp

- Khi đã duyệt hết biểu thức trung tố, lần lượt lấy tất cả toán hạng (nếu có) từ ngăn xếp ra và ghi vào chuỗi kết quả

Để 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)

Trang 4

4 Ghi 4 vào k.quả 3 4

/ Lấy * ra khỏi stack, ghi vào

1 Ghi 1 vào k.quả + / ( 3 4 2 * 1

5 Ghi 5 vào k.quả + / ( - 3 4 2 * 1 5

) Pop cho đến khi lấy được (,

ghi các toán tử pop được ra

k.quả

+ / 3 4 2 * 1 5

-2 Ghi 2 ra k.quả + / 3 4 2 * 1 5 – 2

Pop tất cả các toán tử ra

khỏi ngăn xếp và ghi vào

kết quả

3 4 2 * 1 5 – 2 / +

b) Đánh giá độ phức tạp của thuật toán:

Giả sử xâu biểu thức dạng trung tố đầu vào có độ dài là n

Gọi thời gian tính của thuật toán là O(f(n)),

- thời gian tính của hàm convert (char *input, char *output) chuyển đổi xâu

biểu thức đầu vào từ dạng trung tố sang hậu tố là O(g(n)),

- thời gian tính của hàm tinhtoan (char *bieuthuc) tính toán giá trị của biểu thức

dạng hậu tố là O(h(n)).

Ta có: thời gian cài đặt và thực hiện các phép toán cơ bản trên stack như Push(),

Pop(), Is_Empty(), … là hằng số  O(1).

Vậy ta có thời gian tính của thuật toán là:

O(f(n)) = max {O(g(n)), O(h(n))}

* Đánh giá độ phức tạp của hàm convert(char *input, char *output)  tính O(g(n))

- Chuyển xâu biểu thức đầu vào sang chữ thường strlwr(input) thời gian tính là

O(n);

- Duyệt toàn bộ xâu:

while(input[i]!=’\0’)

{

while(input[i]==””) // loại bỏ dấu “ ”;

{

// câu lệnh hoặc khối câu lệnh với thời gian O(1);

i++;

} if(((input[i]>='0')&&(input[i]<='9'))||(input[i]=='.')) {

while(((input[i]>='0')&&(input[i]<='9'))||(input[i]=='.')) {

// câu lệnh hoặc khối câu lệnh với thời gian O(1);

}

… }

elseif(…) {

Trang 5

}

+ 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.

+ 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

Trang 6

+ 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:

* 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>

Ngày đăng: 05/07/2014, 14:24

TỪ KHÓA LIÊN QUAN

w