Thơng thường các vấn đề liên quan đến cơ chế “vào trước ra trước” đều cĩ thể dùng cấu trúc dữ liệu Queue. Ví dụ như cơ chế sản xuất và tiêu thụ, hàng hĩa sản xuất trước được đưa vào kho và sẽ được xuất ra trước. Các ứng dụng đặt vé tàu lửa máy bay, hệ thống
rút tiền...Ngồi ra hàng đợi cịn được dùng nhiều trong hệ điều hành: bộ đệm ứng dụng, hàng đợi xử lý các sự kiện, hàng đợi xử lý phím nhấn, tiến trình...
• Bài tốn quản lý kho hàng:
Đây là dạng bài tốn sản xuất & tiêu dùng, mặt hàng được sản xuất ra sẽ được lưu vào kho, hàng hĩa từ kho này sẽ được xuất ra ngồi cho nhà phân phối. Khi đĩ những mặt hàng nào đưa vào kho trước tiên sẽ ra được xuất kho trước. Đây là dạng FIFO nên chúng ta cĩ thể dùng cấu trúc dữ liệu Queue để minh họa cho nĩ.
#include "stdio.h" #include "conio.h" #define MAX 100 #define TRUE 1 #define FALSE 0 typedef struct {
char MaSP[10]; // Ma san pham
char TenSP[50]; // Ten san pham } Data;
typedef struct {
int Rear, Front; Data Q[MAX]; }Queue;
void Insert(Queue &queue, Data x); Data Remove(Queue &queue); int IsEmpty(Queue queue); int IsFull(Queue queue);
void InitQueue(Queue &queue); void Insert(Queue &queue, Data x) {
queue.Rear++; // tăng Rear lên 1
if (IsFull(queue))
printf("Queue full!"); else
queue.Q[queue.Rear] = x; }
Data Remove(Queue &queue) { Data x; if (IsEmpty(queue)) printf("Queue empty!"); else x = queue.Q[queue.Front++]; return x; }
int IsEmpty(Queue queue) {
if (queue.Front > queue.Rear) return TRUE;
return FALSE; }
int IsFull(Queue queue) {
if (queue.Rear == MAX) return TRUE; return FALSE; }
void InitQueue(Queue &queue) { queue.Rear = -1; queue.Front = 0; } Data InputProduct() // đọc một sản phẩm { Data sp;
printf("\nMa san pham: "); scanf("%s",sp.MaSP); printf("Ten san pham: "); scanf("%s", sp.TenSP); return sp;
}
void OutputProduct(Data sp) // xuất một sản phẩm
{
printf("\nSan pham : MaSP: %s, TenSP: %s", sp.MaSP, sp.TenSP);
}
void ListProducts(Queue q) // liệt kê những sản phẩm trong kho {
if (!IsEmpty(q)) // nếu cĩ sản phẩm trong kho
{
for(int i= q.Front; i <= q.Rear; i++) // duyệt qua các phần tử {
printf("\nSan pham: MaSP: %s, TenSP: %s",q.Q[i].MaSP, q.Q[i].TenSP); } } } int main() { Queue queue;
InitQueue(queue); // khởi tạo hàng đợi
clrscr(); int i; do {
clrscr();
printf("CHUONG TRINH MINH HOA QUAN LY KHO\n"); printf("---\n"); printf("1. Them san pham vao kho.\n");
printf("2. Xuat san pham ra khoi kho.\n"); printf("3. Xem san pham chuan bi xuat kho.\n"); printf("4. Xem tat ca hang hoa trong kho. \n"); printf("5. Thoat.\n");
printf("Chon chuc nang: (1- 5): "); scanf("%d",&i);
switch (i) {
case 1:
Insert(queue, InputProduct()); // thêm sp vào kho
break; case 2:
OutputProduct(Remove(queue)); // lấy sp khỏi kho
break; case 3:
if (!IsEmpty(queue)) // xem sp sắp lấy
OutputProduct(queue.Q[queue.Front]); break; case 4: // xem tất cả sp ListProducts(queue); break; } getch();
return 0; }
• Duyệt cây theo chiều rộng trong đồ thị:
Đây là phương pháp duyệt hay tìm kiếm phổ biến trên đồ thị. Việc duyệt một đỉnh v sẽ cho phép chúng ta đi duyệt tiếp với những đỉnh kề của nĩ sao cho thỏa thứ tự ưu tiên theo chiều rộng, tức là đỉnh nào gần với v nhất sẽ được duyệt trước.
A
B C D
E F G
Duyệt sau đỉnh B, C,...…D
Hình 5.11: Minh họa thứ tự duyệt theo chiều rộng.
Khi đĩ tại mỗi bước xét một đỉnh v ta cĩ một danh sách các đỉnh kề của nĩ, những đỉnh này chờ được duyệt do đĩ ta sẽ đưa những đỉnh này vào cuối danh sách chờ được duyệt, khi đĩ những đỉnh nào vào danh sách chờ trước thì sẽ được duyệt trước. Với cấu trúc dữ liệu này thì thích hợp cho việc sử dụng Queue.
Thuật tốn: Breadth First Search
void BFS(int v) // thủ tục duyệt theo chiều rộng từ một đỉnh v
{
Queue QList;
InitQueue(QList); // khởi tạo hàng đợi
Insert(QList, v); // đưa v vào hàng đợi
Xet[v] = TRUE; // đánh dấu v đã duyệt
while (IsEmpty(QList) == 0) {
p = Remove(QList); // lấy đỉnh trong hàng đợi
Visit(p); // thăm đỉnh p, cĩ thể xuất đỉnh ra ...
for( i=1; i <= n; i++) // duyệt qua các đỉnh
if ( A[v][i] == 1 && Xet[i] == FALSE) // nếu i là đỉnh kề v và chưa xét {
Insert(QList, i); // đưa vào hàng đợi
} }
}
Mảng A[][]: chứa ma trận kề của đồ thị (G)
Mảng Xet[]: đánh dấu một đỉnh v đã được duyệt hay chưa; 1: đã duyệt, 0: chưa duyệt. Tất cả các phần tử của mảng Xet[] ban đầu được khởi tạo là 0.
Chương trình minh họa duyệt theo chiều rộng:
#include "stdio.h" #include "conio.h" #define MAX 100 #define TRUE 1 #define FALSE 0 typedef int Data; typedef struct {
int Rear, Front; Data Q[MAX]; } Queue;
void Insert(Queue &queue, Data x); Data Remove(Queue &queue); int IsEmpty(Queue queue); int IsFull(Queue queue);
void InitQueue(Queue &queue); void Insert(Queue &queue, Data x) {
queue.Rear++; // tăng Rear lên 1
if (IsFull(queue))
printf("Queue full!"); else
queue.Q[queue.Rear] = x; }
Data Remove(Queue &queue) { Data x; if (IsEmpty(queue)) printf("Queue empty!"); else x = queue.Q[queue.Front++]; return x; }
int IsEmpty(Queue queue) { if (queue.Front > queue.Rear) return TRUE; return FALSE; }
int IsFull(Queue queue) {
if (queue.Rear == MAX) return TRUE; return FALSE; }
void InitQueue(Queue &queue) {
queue.Rear = -1; queue.Front = 0; }
void BFS(int v, int a[][MAX], int xet[], int n) { int d; Queue queue; InitQueue(queue); Insert(queue, v); xet[v] = TRUE; while (!IsEmpty(queue)) { d = Remove(queue); printf("%d\t", d);
for(int i=1; i <= n; i++)
if (a[d][i]== 1 && xet[i]== FALSE) { Insert(queue, i); xet[i] = TRUE; } } } int main() {
int a[MAX][MAX], xet[MAX], n; clrscr();
char name[50];
gets(name);
FILE * f = fopen(name,"r"); if (f == NULL)
{
printf("Loi doc file!"); return 1;
}
fscanf(f,"%d",&n);
for(int i=1; i <= n;i++)
for(int j=1; j <= n; j++) fscanf(f,"%d",&a[i][j]); // danh dau cac dinh chua xet
for(i =1; i <= n; i++) xet[i] = FALSE;
// duyet qua cac dinh cua do thi
printf("Cac dinh cua do thi duoc duyet theo chieu rong\n");
for(i =1; i <= n; i++) if (xet[i] == FALSE) BFS(i, a, xet, n); getch(); return 0; } Tập tin ma trận kề cĩ dạng sau: N A[1][1]… A[1][N] … A[N][1]… A[N][N]
Trong đĩ A[i][j] = 1 nếu (i, j) cĩ cạnh nối và ngược lại A[i][j] = 0 nếu khơng cĩ cạnh nối Ví dụ tập tin ma trận kề minh họa đồ thị cĩ 5 đỉnh.
5 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0
BÀI TẬP
1. Tinh chỉnh lại đoạn chương trình sau: …
for (int i=0; i < n; i++) {
if ( flag)
b[i] = 0; a[i] = a[i] + b[i]; c[i] = a[i] + 2*b[i]; }
…
2. Tinh chỉnh lại đoạn chương trình sau: ...
while ( sqrt( i*i) < sqrt (a*a) ) {
// đoạn chương trình ….
i++; }
3. Tinh chỉnh lại đoạn chương trình sau: ... flag = FALSE; k =0; while ( k < n) { if (a[i] == x) flag = TRUE; k++; }
if (flag)
printf("Tim thay phan tu!"); …
4. Tinh chỉnh lại đoạn chương trình sau: ... while ( i < n) { d = sqrt (a+b)*i; printf("gia tri %.2f", d); i++; } ...
5. Tinh chỉnh lại đoạn chương trình sau: ...
for(int i=0; i < strlen(strBuff); i++) {
…. }
6. Tinh chỉnh lại đoạn chương trình sau: if (a==0 && b==0 && c==0)
Function1(); else if (a==0 && b==0)
Function2(); else if (a==0)
Function3(); else
Function4();
7. Tối ưu lại đoạn chương trình sau: char S1[MAX], S2[MAX]; ….
for (i=0;i<strlen(S1); i++) {
if ( i<strlen(S1)/3 && S1[i] < ’M’ ) S2[i]= S1[i]+2;
else if ( i < strlen(S1)/2 && S[i] < ‘X’) S2[i] = S1[i] +3;
else S2[i]= S1[i]; }
8. Tinh chỉnh đoạn chương trình sau … if (strcmp(s1, s2)==0) printf(“equal”); else if (strcmp(s1, s2) >0) printf(“greater than”); else printf(“less than”); …
9. Tinh chỉnh lại đoạn chương trình sau: …
x1 = (-b + sqrt(b*b-4*a*c))/(2*a); x2 = (-b - sqrt(b*b-4*a*c))/(2*a); …
10. Viết chương trình với giao diện đồ hoạ minh họa cho bài tốn tháp Hanoi. Chương trình thể hiện từng bước sự chuyển các đĩa. Cho phép nhập vào số n và thời gian di chuyển từng đĩa.
11. Viết hàm đệ quy tìm phần tử lớn nhất và nhỏ nhất trong mảng nguyên. 12. Viết hàm đệ quy tính tổng các phần tử trong mảng các số nguyên.
13. Viết chương trình tìm kiếm giá trị x trên ma trận vuơng các số nguyên, dùng đệ quy để tìm kiếm.
14. Viết chương trình chuyển đổi cơ số từ hệ thập phân sang hệ k, yêu cầu dùng hàm đệ quy để giải.
15. Viết chương trình tháp Hanoi khơng dùng đệ quy.
16. Viết chương trình chuyển đổi số từ cơ số thập phân sang cơ số k bất kỳ, khơng dùng đệ quy.
17. Viết chương trình liệt kê chuỗi nhị phân n bit. Dùng phương pháp sinh.
18. Cho tập các số tự nhiên {1, 2, .., n}, viết chương trình liệt kê tập con k phần tử. Dùng phương pháp sinh.
19. Viết chương trình liệt kê hốn vị n phần tử. Dùng phương pháp sinh. 20. Nhập vào một chuỗi ký tự S, hãy liệt kê các hốn vị ký tự của chuỗi S.
Ví dụ: chuỗi S nhập vào là "abc" thì kết quả là abc, acb, bac, bca, cab, cba. 21. Tương tự như bài 1, liệt kê tổ hợp chập k ký tự của chuỗi S.
22. Một hốn vị hồn tồn của tập{1, 2, .., n} là dãy hốn vị mà thoả mãn x[i] ≠ i, ∀i: 1≤ i ≤ n. Viết chương trình nhập vào giá trị n, sau đĩ xuất ra tất cả hốn vị hồn tồn của tập {1, 2, .., n}.
23. Viết chương trình nhập vào một số nguyên n, liệt kê tất cả cách chia số tự nhiên n thành tổng các số nhỏ hơn. Ví dụ: n = 4 Kết quả 3 1 2 2 2 1 1 1 1 1 1
24. Cho một bàn cờ tổng quát cĩ kích thước nxn và quân mã. Viết chương trình cho phép liệt kê ra hành trình của quân mã từ một vị trí (x ,y) đi qua tất cả các ơ cịn lại của bàn cờ, mỗi ơ đi qua một lần.
Hình: Các nước đi cĩ thể cĩ của quân mã
25. Cho bàn cờ vua nxn, hãy viết chương trình minh hoạ bàn cờ và cách sắp xếp n quân hậu sao cho các quân hậu khơng thể ăn lẫn nhau.
Hình: Một cách bố trí các quân Hậu.
26. Nhập danh sách tên gồm n người, liệt kê ra tất cả cách chọn k người trong số n người đĩ.
Ví dụ: n =6: {"Nam", "Hung", "Viet", "Hoa", "Lan", "Tien"},
27. Nhập danh sách tên gồm n người, xuất ra các cách xếp n người đĩ vào trong một bàn trịn.
28. Nhập vào danh sách n bạn nam và n bạn nữ, hãy xuất ra danh sách cách xếp 2n bạn đĩ vào một bàn trịn trong đĩ cĩ sự xen lẫn giữa nam và nữ.
29. Viết chương trình liệt kê tất cả hốn vị của từ “HUTECH”
30. Viết chương trình liệt kê tất cả hốn vị chữ cái trong từ "MISSISSIPPI"
31. Viết chương trình mơ phỏng từng bước thực hiện của các thuật tốn sắp xếp sau: a. Phương pháp chọn
b. Phương pháp nổi bọt c. Phương pháp chèn
d. Phương pháp đổi chỗ trực tiếp e. Phương pháp ShellSort
f. Phương pháp phân đoạn g. Phương pháp cơ số
32. Cho một mảng nguyên n >100 phần tử, các phần tử phát sinh ngẫu nhiên. Viết hàm tìm kiếm một phần tử trong mảng.
33. Tương tự như bài tập bên trên, nhưng hàm tìm kiếm theo dạng nhị phân. Sinh viên cĩ thể dùng một trong các phương pháp sắp xếp để sắp xếp lại mảng trước khi thực hiện tìm kiếm nhị phân.
34. Viết chương trình đo thời gian thực hiện của các thuật tốn sắp xếp bên trên. Chương trình phát sinh ngẫu nhiên bộ dữ liệu test (mảng số nguyên, cĩ kích thước n >= 1000) , cho các thuật tốn lần lượt chạy và ghi nhận lại thời gian thực hiện. 35. Viết chương trình khơng đệ quy cho thuật giải Quicksort, (áp dụng stack để khử đệ
quy).
36. Nhập vào một biểu thức trung tố, chuyển đổi thành biểu thực hậu tố và tính giá trị của biểu thức. Lưu ý: tốn hạng cĩ thể nhiều hơn một con số hay số thực. Sinh viên mở rộng với các tốn tử khác...
Ví dụ: (20+5)*3+(10/5) => 20 5 + 3 * 10 5 / + Kết quả: 77
37. Viết chương trình mơ phỏng hàng đợi mua vé xem phim. Thơng tin của việc đăng ký gồm: họ tên, địa chỉ, tuổi và số ghế...
38. Viết chương trình mơ phỏng hàng đợi mua vé xe lửa.
39. Viết chương trình sắp xếp theo cơ số (RadixSort), trong đĩ sử dụng cấu trúc dữ liệu queue để lưu tạm trong quá trình sắp xếp. Hướng dẫn sử dụng 10 hàng đợi để lưu tạm các con số. Gồm hàng đợi 0 đến 9, khi đĩ hàng đợi 0 sẽ chỉ lưu những con số cĩ số 0 ở các bước phân hàng đơn vị, hàng chục, hàng trăm tương ứng...
TÀI LIỆU THAM KHẢO
1. Brian W. Kernighan, Rob Pike, The Practice of Programming, Addison Wesley, 1999.
2. Ellis Horowitz, Sartaj Sahni, Fundamentals of Data Structures, ebook, 1981.
3. R. Neapolitan, K. Naimipour , Foundations of Algorithms Using C++ Pseudocode, Jones and Bartlett Publishers , 2004.
4. Lê Hồi Bắc, Nguyễn Thanh Nghị, Kỹ năng lập trình, NXB KHKT, 2005.
5. Trần Hồng Thọ, Giáo trình Kỹ thuật Lập trình Nâng cao, ĐH Đà Lạt, 2002.
6. Dương Anh Đức, Trần Hạnh Nhi, Nhập mơn Cấu trúc dữ liệu và thuật tốn, ĐH KHTN, 2000.
7. Lê Hữu Lập, Nguyễn Duy Phương, Giáo trình kỹ thuật lập trình, NXB Bưu Điện, 2002.
8. Lê Minh Hồng, Giải thuật và lập trình, NXB ĐH Sư Phạm HN, 1999- 2002