I. Bài tập chương 1 - math.hcmus.edu.vnptbao/DataStructure/Book/baitap.pdf · II. Bài tập...
Transcript of I. Bài tập chương 1 - math.hcmus.edu.vnptbao/DataStructure/Book/baitap.pdf · II. Bài tập...
Trang 1
I. Bài tập chương 1 1. Hãy cho biết có bao nhiêu phép so sánh và gán dữ liệu trong đoạn chương trình.
for (g = 1; j < = n–1; j + +){
a = j + 1;
do{
if (A[i] < A[j])swap (A[i], A[j]);
i + +;
} while (i <= n)
}
2. Hãy tính số lần lặp các lệnh trong {…} trong đoạn chương trình.
for ( i = 0; i < n; i + +)
for ( j = i + 1; i < = n; j + +)
for ( k = 1; k < 10; k + +)
{ các lệnh };
3. Đánh giá thời gian thực hiện của các đoạn chương trình sau.
sum = 0;
for ( int i = 0; i < n; i + +)
for ( int j = 0; j < n; j + +) sum + +;
sum = 0;
for ( int i = 0; i < n; i + +)
for ( int j = 0; j < n*n; j + +)
for ( int k = 0; k < j; k + +)
sum + + ;
4. Đánh giá thời gian thực hiện của hàm đệ quy sau.
int Bart(int n)// n nguyên dương{
if ( n = = 1)
return 1;
else{
result = 0;
for ( int i = 2; i < = n; i + +)
result + = Bart(i – 1); return result ;
}
}
Trang 2
5. Chúng ta có thể tính ước chung lớn nhất của hai số nguyên dương bởi hàm đệ quy UCLN(n,
m). Bạn hãy đánh giá thời gian thực hiện của hàm UCLN dựa vào kích cỡ của dữ liệu là
n.
int UCLN( int n, int m){ // n và m là nguyên dương và n > m
if ( n % m = = 0) return;
else{
int k = n%m;
return UCLN(m,k);
}
}
6. Bạn hãy hoàn chỉnh xây dựng kiểu dữ liệu số phức và viết chương trình cho phép tính toán
các số phức.
7. Bạn hãy xây dựng một ADT phân số.
II. Bài tập chương 2
Bài tập cơ bản
1. Cài đặt các thuật toán tìm kiếm và sắp xếp trong một một chương trình với mảng được dùng
cấu trúc có tên MyArray như trong tập tin Array.h, số lượng phần tử sẽ được cấp phát động
như sau:
Tập tin MyArray.h
struct MyArray{
int *a;
int num;
};
// khai báo prototype
void initArray(MyArray &);
MyArray readArrayFromFile(char *);
int writeArrayToFile(MyArray, char *);
void displayArray(MyArray);
void deleteArray(MyArray &);
MyArray copyArray(MyArray);
MyArray copyArray(MyArray,int,int);
Bạn hãy cài đặt nội dung đầy đủ các hàm (đã khai báo) trong tập tin MyArray.cpp trong
bảng sau:
Tập tin MyArray.cpp
#include"MyArray.h"
Trang 3
// Hàm initArray dùng để khởi tạo ban đầu
void initArray(MyArray &ma){
ma.a = ;
ma.num = ;
}
// Hàm readArrayFromFile dùng để đọc một mảng từ một tập tin văn bản // có tên
là chuỗi filename
// tập tin này dạng có 02 dòng, dòng đầu tiên mô tả số lượng phần tử
// mảng, dòng thứ hai mô tả các phần tử của mảng
MyArray readArrayFromFile(char *fileName){
FILE *fp;
MyArray temp;
int buf;
initArray(temp);
fopen_s(&fp,fileName,"rt"); // Mở tập tin để đọc
if(!fp){
printf("Cannot open file..."); // Lỗi không thể mở tập tin
return temp;
}
fscanf_s(fp,"%d",&temp.num); //Đọc số phần tử của mảng.
// Bạn phải kiểm tra tính hợp lệ của số phần tử
// Tạo mảng tạm để đọc dữ liệu
// Kiểm tra có đủ bộ nhớ để tạo được mảng tạm hay không // Đọc từng phần tử vào mảng tạm đã tạo
for(int i=0;i<temp.num;i++){
….
}
fclose(fp); // Đóng tập tin
return temp; // Trả kết quả ra
}
// Hàm writeArrayToFile dùng để ghi một mảng ra một tập tin văn bản có //tên là
chuỗi fileName
int writeArrayToFile(MyArray as, char *fileName){
FILE *fp;
fopen_s(&fp,fileName,"wt"); // Mở tập tin để ghi
// Kiểm tra có mở tập tin để ghi được hay không
// Ghi số phần tử của mảng vào tập tin
// Ghi từng phần tử của mảng vào tập tin
fclose(fp); // Đóng tập tin
return 0;
}
// Hàm displayArray dùng để xuất mảng ra màn hình
void displayArray(MyArray ma){
Trang 4
}
// Hàm deleteArray dùng để xóa mảng khỏi bộ nhớ
void deleteArray(MyArray &ma){
}
// Hàm copyArray dùng để sao chép mảng ban đầu, kết quả trả về là một // mảng
mới
MyArray copyArray(MyArray as){
// Tạo một struct MyArray để sao chép
// Sao chép từng phần tử của mảng
// Trả mảng kết quả về
}
// Hàm copyArray(MyArray as,int left,int right) dùng để sao chép mảng
// ban đầu từ phần tử ở vị trí left đến phần tử ở vị trí right.
// Tương tự như hàm copyArray ở trên
MyArray copyArray(MyArray as,int left,int right){
}
Sau khi cài đặt hoàn thiện struct MyArray và các hàm tiện ích đi kèm. Chúng ta sẽ tiếp tục
cài đặt các thuật toán tìm kiếm và sắp xếp trong chương này, áp dụng trên struct MyArray đã
tạo ở trên.
Tập tin SearchArray.h
#include”MyArray.h”
// Hàm dùng để tìm kiếm tuần tự một phần tử trong mảng, kết quả trả ra // vị trí
của phần tử đó trong mảng, nếu không tồn tại thì trả về vị trí là -1
int LSearch(MyArray, int);
// Hàm tìm kiếm nhị phân để tìm một phần tử trong mảng đã được sắp //xếp, kết
quả trả ra là vị trí của phần tử đó trong mảng, nếu không tồn tại //thì trả về vị trí là
-1
int BSearch(MyArray, int);
Các hàm tìm kiếm sẽ được cài đặt trong tập tin SearchArray.cpp
Tập tin SearchArray.cpp
#include"SearchArray.h"
// Hàm tìm kiếm tuần tự, sinh viên tự cài đặt theo hướng dẫn trong lý thuyết
int LSearch(MyArray ad, int x){
Trang 5
}
// // Hàm tìm kiếm nhị phân, sinh viên tự cài đặt theo hướng dẫn trong lý thuyết
int BSearch(MyArray ad, int x){
}
Tiếp theo, chúng ta sẽ cài đặt các thuật toán sắp xếp trên struct MyArray
Tập tin SortArray.h
#include”MyArray.h”
/* Các hàm sắp xếp mảng dùng 11 thuật toán sắp xếp trong lý thuyết
1. Interchange Sort
2. Bubble Sort
3. Shaker Sort
4. Insert Sort
5. Binary Insert Sort
6. Shell Sort
7. Select Sort
8. Heap Sort
9. Quick Sort
10. Merge Sort
11. Radix Sort
*/
// Nhóm 1. Các thuật toán dựa trên ý tưởng làm giảm số nghịch thế trong mảng
void interchangeSort(MyArray);
void bubbleSort(MyArray);
// cải tiến của Bubble Sort để giảm bớt số lần lặp khi dữ liệu đã được sắp xếp cục
bộ
void shakerSort(MyArray);
// Nhóm 2. Các thuật toán dựa trên ý tưởng xây dựng mảng mới có thứ tự từ mảng
ban đầu
void insertSort(MyArray);
void binaryInsertSort(MyArray); // cải tiến bằng cách áp dụng hàm tìm kiếm nhị
phân
void shellSort(MyArray); // cải tiến bằng cách giảm bớt số nghịch thế trước khi
thực sự sắp xếp
void selectSort(MyArray);
// cải tiến của Select Sort, thông tin các lần tìm kiếm trước được lưu trữ lại
// để áp dụng cho những lần tiếp theo
void heapSort(MyArray);
// Nhóm 3. Các thuật toán dựa trên ý tưởng “chia để trị” để sắp xếp mảng
void qSort(MyArray);
void QuickSort(MyArray,int,int);
Trang 6
void mSort(MyArray);
void MergeSort(MyArray,int,int);
// Nhóm 4. Dựa trên nguyên tắc chính là phân loại và trình tự phân loại
void radixSort(MyArray);
Phần cài đặt chi tiết các hàm sắp xếp trên nằm trong tập tin SortArray.cpp. Sinh viên tự cài
đặt các thuật toán theo hướng dẫn trong lý thuyết.
Tập tin SortArray.cpp
#include”SortArray.h”
void interchangeSort(MyArray ad){
for(int i= ;i < ;i++)
for(j= ;j< ;j++)
if( )
swap(ad,i,j); // Hoán vị hai phần tử ở vị trí i j
}
void bubbleSort(MyArray ad){
for(int i= ; ; )
for(j= ; ; )
if( )
swap(ad, , );
}
int shakerSort(MyArray ad)
{
while(up<down){
for(int j=up; ; )
if( ){
swap(ad, , );
pos=j;
}
down=pos;
for(int j= ; ; )
if( ){
swap(ad, , );
pos=j;
}
up=pos;
}
}
void insertSort(MyArray ad){
for(i= ; ; ){
Trang 7
x=ad.a[i];
j=i-1;
while((x<ad.a[j])&&(j>=0)){
swap(ad, , );
j--;
}
ad.a[j+1]=x;
}
}
void binaryInsertSort(MyArray ad){
for(i= ;i< ;i++){
x=ad.a[i];
k=BSearch(ad,i,x);
for(j=i; ; )
ad.a[j]=ad.a[j-1];
ad.a[k]=x;
}
}
void shellSort(MyArray ad){
MyArray step=findStep(ad.num); // Tạo ra một phân hoạch
for(k= ;k>=0;k--)
for(i=step.a[k];i<ad.num;i++){
x=ad.a[i];
j=i;
while((x<ad.a[j-step.a[k]])&&(j>=step.a[k])){
ad.a[j]=ad.a[j-step.a[k]];
j-=step.a[k];
}
ad.a[j]=x;
}
}
void selectSort(MyArray ad){
for( ; ; ){
k=i;
for( )
if( )
k=j;
swap(ad,k,i);
}
}
void heapSort(MyArray ad){
createHeap(ad); // Tạo heap
Trang 8
for(int i=ad.num-1;i>0;i--){
swap(ad,0,i);
insertHeap(ad,0,i-1); // Chèn heap
}
}
void qSort(MyArray ad){
QuickSort(ad,0,ad.num-1); // Thuật toán Quick Sort
return;
}
void QuickSort(MyArray ad,int left, int right){
temp=ad.a[(left+right)/2];
while(i<=j){
while(ad.a[i]<temp) i++;
while(ad.a[j]>temp) j--;
if(i<=j){
swap(ad,i,j);
i++;
j--;
}
}
if(j>left) QuickSort(ad,left,j);
if(right>i) QuickSort(ad,i,right);
}
void mSort(MyArray ad){
MergeSort(ad,0,ad.num-1); // Thuật toán MergeSort
return;
}
void MergeSort(MyArray ad,int left, int right){
if(left<right){
int mid=(left+right)/2;
MergeSort(ad,left,mid);
MergeSort(ad,mid+1,right);
merge(ad,left,right); // Trộn hai phần thành một phần
}
}
void radixSort(MyArray ad){
int max=findMax(ad); // Tìm số chữ số lớn nhất của phần tử trong mảng
MyArray stack[10];
for(i=0; i<10; i++){
initArray(stack[i]);
stack[i].a=new int [ad.num];
Trang 9
// Kiểm tra tạo stack được hay không
}
for(i=0;i<max;i++){
for(j=0;j<ad.num;j++){
k=getDigit(ad.a[j],i); // Lấy chữ số thứ I
stack[k].a[stack[k].num++]=ad.a[j];
}
// Đưa ngược các phần tử trong stack vào lại mảng
for(j=0;j<10;j++){
count=0;
while(count<stack[j].num)
ad.a[k++]=stack[j].a[count++];
stack[j].num=0;
}
}
}
Bài tập áp dụng
2. Sau khi cài đặt các thuật toán tìm kiếm và sắp xếp đã trình bày ở trên, hãy thực thi thử
nghiệm chương trình với nhiều bộ dữ liệu được tạo ngẫu nhiên (về cả số phần tử của mảng và
giá trị của các phần tử).
a. Thống kê thời gian thực hiện các thuật toán bằng cách lấy thời gian khi thực hiện. Vẽ
đồ thị theo N (số lượng phần tử của mảng)
b. Thống kê số các thao tác so sánh, gán. Xuất kết quả ra tập tin và màn hình. Vẽ đồ thị
số phép gán và so sánh theo N (số lượng phần tử của mảng).
c. So sánh kết quả câu (a) với đánh giá về mặt lý thuyết trong các nhận xét.
d. Tính thời gian thực hiện của từng thuật toán với từng dữ liệu đầu vào và sau đó tính
thời gian thực thi trung bình của từng thuật toán để so sánh. Xuất kết quả ra tập tin và
màn hình. Vẽ đồ thị các thao tác so sánh và gán để kiểm chứng độ phức tạp của thuật
toán so với lý thuyết.
e. Cài đặt mở rộng phần đọc tập tin dữ liệu vào mảng:
Là tập tin văn bản chỉ có một dòng dữ liệu là các phần tử của mảng (hướng
dẫn: phải xác định số phần tử bằng cách duyệt tập tin đọc dữ liệu từng phần rồi
dời con trỏ tập tin về đầu tập tin để đọc dữ liệu lần thứ hai đưa vào mảng).
Là tập tin nhị phân (hướng dẫn: xác định dung lượng tập tin chia kích thước
cấu trúc).
3. Trong phần hướng dẫn về thuật toán Bubble Sort ở trên mới chỉ đề cập đến một chiều cho vật
nhẹ nổi lên. Hãy cài đặt Bubble Sort với ý tưởng cho vật nặng chìm xuống; và cho đồng thời
cả vật nhẹ chìm xuống và vật nhẹ nổi lên.
4. Hãy cài đặt thuật toán Shell Sort với nhiều loại bước nhảy khác nhau và thử nghiệm các loại
bước nhảy đó để so sánh thời gian thực thi.
5. Cài đặt hai cách chia trong thuật toán Merge Sort và so sánh bằng thực nghiệm như câu 2a và
2b.
6. Hãy viết hàm tìm dãy con tăng dần dài nhất trong mảng, biết dãy con là một dãy liên tiếp các
phần tử của a.
Trang 10
7. Khái niệm heap trong phần này được trình bày còn được gọi là heap max vì phần tử đầu tiên
của heap cũng là max của heap. Tương tự, chúng ta cũng có thể định nghĩa một heap min.
Dùng heap min, hãy cài đặt thuật toán để sắp xếp mảng theo thứ tự giảm dần.
8. Hãy viết hàm tìm
a. Phần tử trung vị (median) của mảng.
b. Phần tử lớn nhất (max) của mảng.
c. Phần tử nhỏ nhất (min) của mảng.
d. Các phần tử là số nguyên tố.
9. Hãy viết hàm trộn hai mảng một chiều có thứ tự tăng thành một mảng một chiều cũng có thứ
tự tăng.
10. Một thuật toán sắp xếp được gọi là ổn định (stable) nếu sau khi thực hiện sắp xếp, thứ tự
tương đối của các phần tử có giá trị bằng nhau là không đổi. Trong các thuật giải đã trình
bày, thuật giải nào là ổn định?
a. Chứng minh bằng lý thuyết.
b. Chứng minh bằng thực nghiệm.
Bài tập mở rộng
11. Cải tiến thuật toán tìm kiếm tuần tự để có thể tìm tất cả vị trí trong mảng A có giá trị là X.
12. Một Ternary Heap là một cây tam phân được cài đặt bằng mảng, mỗi node có 3 node con.
Giá trị node cha lúc nào cũng lớn hơn hoặc bằng giá trị của các node con. (Gợi ý: node thứ i
có 3 node con là 3i+1, 3i+2, 3i+3). Hãy cài đặt giải thuật sắp xếp mảng với Ternary Heap.
13. Nghiên cứu và cài đặt thuật toán tìm kiếm tam phân.
14. Hãy cài đặt thuật toán Quick Sort mở rộng bằng cách, thay vì chia đôi mảng để sắp xếp thì
bây giờ hãy chia ngẫu nhiên các phần của mảng để sắp xếp. Hãy cài đặt thuật toán QuickSort
không dùng đệ quy.
III. Bài tập chương 3 Bài tập cơ bản
1. Trong phần này chúng ta sẽ cài đặt cấu trúc dữa liệu danh sách liên kết đơn và một số hàm
tiện ích cho kiểu dữ liệu này.
Tập tin SingleLinkedList.h
struct tagNode{
int Data;
struct tagNode *Next;
};
typedef tagNode* Node; // một node trong danh sách liên kết đơn
struct tagList{
Node Head;
Node Tail;
};
typedef tagList SLL; // cấu trúc dữ liệu danh sách liên kết đơn
Trang 11
void initSLL(SLL &); // Khởi tạo danh sách liên kết đơn
void deleteSLL(SLL &); // Xóa danh sách liên kết đơn
// Đọc danh sách liên kết từ tập tin văn bản với tên trong chuỗi fileName
SLL readSLLFromFile(char *st);
// Ghi danh sách liên kết vào tập tin văn bản với tên trong chuỗi fileName
void writeToFile(SLL , char *fileName);
void displaySLL(SLL); // Hiển thị danh sách liên kết
int isEmpty(SLL); // Kiểm tra danh sách liên kết có rỗng hay không
Node getHead(SLL &); // Trả ra node đầu tiên
int getHeadData(SLL); // Lấy giá trị của node đầu tiên trong danh sách
int insertNode2Head(SLL &l, int x); // Chèn node có giá trị x vào đầu danh sách liên kết
int insertNode2Tail(SLL &l, int x); // Chèn node có giá trị x vào cuối danh sách liên kết
int insertNode2Tail(SLL &l, Node p); // Chèn node p vào cuối danh sách liên kết
int insertNode2After(SLL &l,Node q,int x); // Chèn node có giá trị x vào sau node q trong
danh sách
int removeNodeAtHead(SLL &l); // Loại bỏ node đầu tiên
int removeNodeAtTail(SLL &l); // Loại bỏ node cuối cùng
int removeNode(SLL &l,int x); // Loại bỏ node đầu tiên trong danh sách có giá trị bằng x
int removeNodes(SLL &l,int x); // Loại bỏ tất cả các node có giá trị bằng x
int appendList(SLL &first,SLL &second); // Hàm dùng để nối 2 danh sách liên kết
Sinh viên tự cài đặt các hàm tiện ích cho cấu trúc dữ liệu danh sách liên kết đơn đã liệt kê ở tập
tin SingleLinkedList.h vào tập tin SingleLinkedList.cpp. Tiếp theo chúng ta sẽ cài đặt một số
thuật toán sắp xếp phù hợp cho danh sách liên kết đơn.
Tập tin SortSLL.h
#include”SingleLinkedList.h”
// Các hàm tiện ích phục vụ cho các thuật toán sắp xếp ở dưới
void divideSLL(SLL &, SLL &, SLL &); // Chi đôi danh sách liên kết thành 2 danh sách
liên kết
void mergeSLL(SLL &, SLL &, SLL &); // Trộn hai danh sách liên kết
// Chia danh sách liên kết thành 2 danh sách liên kết,
// với các phần tử có giá trị <= giá trị node x và các phần tử có giá trị > giá trị node x.
void splitSLL(SLL &, SLL &,SLL &,Node &x);
void concatSLL(SLL &l,SLL &l1, Node &x, SLL &l2); // Nối lại theo thứ tự l1, x , l2
// Đưa các node vào các box tương ứng trong thuật toán Radix Sort
void send2Box(int, SLL &,SLL temp[]);
int findMax(SLL); // Tìm số chữ của số lớn nhất trong danh sách liên kết
// Các thuật toán sắp xếp Quick Sort, Merge Sort, và Radix Sort
void quickSort(SLL &);
void mergeSort(SLL &);
void radixSort(SLL &);
Trang 12
Sinh viên tự cài đặt các hàm sắp xếp danh sách liên kết đơn đã liệt kê ở tập tin SortSLL.h vào tập
tin SortSLL.cpp
2. Ứng dụng của danh sách liên kết: QUEUE (hàng đợi) và STACK (ngăn xếp): Cài đặt Queue,
tương tự như khi cài đặt danh sách liên kết đơn, đầu tiên chúng ta sẽ định nghĩa cấu trúc dữ
liệu Queue là một Single Linked List
Tập tin Queue.h
#include”SingleLinkedList.h”
typedef SLL Queue; //định nghĩa Queue
int isEmptyQueue(Queue); // kiểm tra Queue rỗng
int sizeQueue(Queue); // số phần tử của Queue
int front(Queue); // trả ra giá trị của phần tử ở đầu Queue
void enQueue(Queue &, int); // thêm một phần tử vào Queue
Node deQueue(Queue &); // lấy một phần tử ra khỏi Queue
Phần tiếp theo là cài đặt chi tiết các hàm tiện ích cho cấu trúc dữ liệu Queue đã xây dựng ở trên
vào tập tin “Queue.cpp” (theo hướng dẫn trong phần lý thuyết).
Cài đặt Stack, tương tự như khi cài đặt danh sách liên kết đơn, đầu tiên chúng ta sẽ định nghĩa
cấu trúc dữ liệu Stack là một Single Linked List
Tập tin Stack.h
#include”SingleLinkedList.h”
typedef SLL Stack; //định nghĩa Stack
int isEmptyStack(Stack); // kiểm tra stack rỗng
int sizeStack(Stack); // số phần tử trong stack
int top(Stack); // trả ra giá trị phần tử ở đầu stack
void push(Stack &, int x); // thêm một phần tử vào stack
Node pop(Stack &); // lấy một phần tử ra khỏi stack
Phần tiếp theo là cài đặt chi tiết các hàm tiện ích cho cấu trúc dữ liệu Stack đã xây dựng ở trên
vào tập tin “Stack.cpp” (theo hướng dẫn trong phần lý thuyết).
3. Thuật toán Ba Lan ngược sử dụng cấu trúc dữ liệu Stack để cài đặt
Tập tin ReversePolishNotation.cpp
#include”Stack.h”
void RPN(){
// khởi tạo Stack rỗng
while( ){ // lặp đến khi kết thúc biểu thức
// đọc một phần tử của biểu thức có thể là hằng, biến, phép toán, “(” hay “)”
if( ) // kiểm tra nếu phần tử vừa đọc là “(”
// đưa vào Stack
Trang 13
if( ) // kiểm tra nếu phần tử vừa đọc là “)”
// lấy các phần tử ra khỏi Stack đến khi gặp “(” trong Stack
if( ){ // kiểm tra nếu phần tử vừa đọc là một phép toán
if( ) // nếu Stack rỗng đưa vào Stack
if( ) // nếu Stack khác rỗng và phép toán vừa đọc có độ ưu tiên
// cao hơn phần tử đầu Stack thì đưa vào Stack
if( ) // nếu Stack khác rỗng và phép toán vừa đọc có độ ưu tiên
// thấp hơn hoặc bằng phần tử ở đầu Stack thì lấy phần tử
// ở đầu Stack ra; sau đó, lặp lại việc so sánh với phần tử
// ở đầu Stack.
}
if( ) // kiểm tra nếu phần tử là hằng hoặc biến thì không đưa vào Stack
}
// lấy hết các phần tử còn lại trong Stack ra ngoài
}
Bài tập áp dụng
4. Cài đặt đầy đủ các thuật toán sắp xếp và tìm kiếm đã nêu trong chương trước áp dụng cho
danh sách liên kết đơn.
5. Bạn hãy chạy thử chương trình với nhiều bộ dữ liệu được tạo ngẫu nhiên (về cả số phần tử
của mảng và giá trị của các phần tử).
i. Thống kê số các thao tác so sánh, gán. Xuất kết quả ra tập tin và màn hình.
ii. Tính thời gian thực hiện của từng thuật toán với từng dữ liệu đầu vào và sau đó
tính thời gian thực thi trung bình của từng thuật toán để so sánh. Xuất kết quả ra
tập tin và màn hình.
6. Cài đặt và thực thi chương trình thuật toán Ba Lan ngược để tính giá trị các biểu thức số học.
7. Cài đặt chương trình mô phỏng hàng đợi mua vé xem phim (gợi ý dùng cấu trúc dữ liệu
queue để mô phỏng).
8. Sử dụng danh sách liên kết để mô phỏng số nguyên lớn. Sau đó, cài đặt chương trình cộng,
trừ, nhân, chia, lũy thừa hai số nguyên lớn.
9. Sử dụng danh sách liên kết để mô phỏng tập hợp. Sau đó, cài đặt các phép toán giao, hội,
hiệu, phần bù của 02 tập hợp.
10. Sử dụng danh sách liên kết để mô phỏng đa thức.
a. Sau đó, cài đặt các phép toán cộng, trừ, nhân, chia 02 đa thức.
b. Ước lượng giá trị của đa thức khi biết x.
c. Rút gọn biểu thức.
11. Cho tập tin văn bản chứa một dãy các thao tác trong đó chỉ bao gồm các ký tự A..Z và ký tự
* Với mỗi chữ cái tượng trưng cho thao tác thêm ký tự đó vào Stack/Queue, dấu * tượng
trưng cho thao tác lấy nội dung một phần tử trong Stack/Queue và in lên màn hình.
Bài tập mở rộng
12. Sử dụng danh sách liên kết để cài đặt ma trận thưa. Sau đó, cài đặt các phép tính tổng, hiệu,
tích của hai ma trận. Ngoài ra, cài đặt phép toán tìm nghịch đảo của ma trận thưa đó (nếu có).
13. Bài toán Josephus có N sinh viên đứng thành vòng tròn và được trao thưởng là người thứ M
quanh vòng tròn, sau đó người đó bước ra khỏi vòng tròn và vòng tròn được thu hẹp lại. Yêu
cầu tìm ra thứ tự từng người được trao thưởng.
Trang 14
Ví dụ: N=9, M=5 thì thứ tự là 5, 1, 7, 4, 3, 6, 9, 2, 8
Hãy viết chương trình giải quyết bài toán Josephus.
14. Cài đặt một chương trình soạn thảo văn bản với các yêu cầu như sau:
a. Số dòng văn bản không hạn chế.
b. Mỗi dòng văn bản có chiều dài không hạn chế.
c. Các thao tác yêu cầu bao gồm:
Di chuyển trong văn bản (lên, xuống, qua trái, qua phải).
Thêm, xóa, sửa ký tự trong một dòng.
Thêm, xóa một dòng trong văn bản.
Đánh dấu, sao chép khối.
IV. Bài tập chương 4 Bài tập cơ bản
1. Trong phần này, chúng ta sẽ cài đặt cấu trúc dữ liệu cây nhị phân và cây nhị phân tìm kiếm.
Tập tin BinaryTree.h
typedef struct tagNode{
int Data;
tagNode *Left;
tagNode *Right;
};
typedef tagNode* Node;
typedef struct BinaryTree {
Node Root;
};
int isEmpty(BinaryTree); // Kiểm tra cây rỗng
int isLeaf(Node); // Kiểm tra 01 node có phải là node lá không?
void traversalTree(BinaryTree, int type); // Duyệt cây nhị phân theo kiểu type
nào đó
void NLR(Node); // Duyệt theo thứ tự trước
void LNR(Node); // Duyệt theo thứ tự giữa
void LRN(Node); // Duyệt theo thứ tự sau
Node searchNode(BinaryTree, int); // Tìm kiếm 01 phần tử trong cây nhị phân
Sinh viên tự cài đặt các hàm isEmpty, isLeaf đã khai báo prototype ở tập tin BinaryTree.h vào
tập tin BinaryTree.cpp . Hướng dẫn cài đặt hàm duyệt cây nhị phân.
Tập tin BinaryTree.cpp
#include”BinaryTree.h”
int isEmpty( ){
…
}
int isLeaf( ){
Trang 15
…
}
void traversalTree(BinaryTree BT, int type){
switch(type){
case 1: NLR(BT.Root); break;
case 2: LNR(BT.Root); break;
case 3: LRN(BT.Root); break;
}
}
void NLR(Node Root){
if(!isLeaf(Root)){
<Xử lý node root theo yêu cầu nào đó>
NLR(Root->Left);
NLR(Root->Right);
}
}
void LNR(Node Root){
if(!isLeaf(Root)){
LNR(Root->Left);
<Xử lý node root theo yêu cầu nào đó>
LNR(Root->Right);
}
}
void LRN(Node Root){
if(!isLeaf(Root)){
LRN(Root->Left);
LRN(Root->Right);
<Xử lý node root theo yêu cầu nào đó>
}
}
Node searchNode(BinaryTree Root, int x){
}
Sinh viên tự cài đặt hàm search bằng cách áp dụng các hàm duyệt cây đã viết ở trên. Tiếp theo,
chúng ta sẽ cài đặt cấu trúc dữ liệu cây nhị phân tìm kiếm.
Tập tin BinarySearchTree.h
typedef struct tagNode{
int Data;
tagNode *Left;
tagNode *Right;
};
Trang 16
typedef tagNode* Node;
typedef tag struct BinarySearchTree{
Node Root;
};
int isEmpty(BinarySearchTree); // Kiểm tra cây rỗng
int isLeaf(Node); // Kiểm tra 01 node có phải là node lá không?
void traversalTree(BinarySearchTree, int type); // Duyệt cây nhị phân tìm kiếm
theo kiểu nào đó
void NLR(Node); // Duyệt theo thứ tự trước
void LNR(Node); // Duyệt theo thứ tự giữa
void LRN(Node); // Duyệt theo thứ tự sau
Node searchNode(BinarySearchTree, int); // Tìm kiếm 01 phần tử trong cây
nhị phân tìm kiếm
int insertNode(BinarySearchTree &, int); // Thêm một node vào cây nhị phân
tìm kiếm
int delNode(BinarySearchTree &, int); // Hủy một node trong cây nhị phân tìm
kiếm
BinarySearchTree createTree(); // Tạo một cây nhị phân tìm kiếm
void removeTree(BinarySearchTree&); // Hủy cây nhị phân tìm kiếm
Sinh viên tự cài đặt các hàm isEmpty, isLeaf, traversalTree tương tự như với cây nhị phân. Đối
với hàm searchNode, sinh viên áp dụng tính chất của cây nhị phân tìm kiếm để cài đặt hàm này.
Tập tin BinarySearchTree.cpp
#include”BinarySearchTree.h”
int isEmpty( ){
…
}
int isLeaf( ){
…
}
void traversalTree( ){
…
}
void NLR( ){
…
}
void LNR( ){
…
}
Trang 17
void LRN( ){
…
}
Node searchNode(BinaryTree Root, int x){
…
}
int insertNode(BinarySearchTree &BST, int x){
return insertNode(BST.Root,x);
}
int insertNode(Node &BST, int x){
if(!isLeaf(BST)){
if(BST->Data == x) return 0; // đã tồn tại phần tử x trong cây
if(BST->Data>x)
return insertNode(BST->Left,x);
else
return insertNode(BST->Right,x);
}
BST = new tagNode;
if(isEmpty(BST)) return -1; // thiếu bộ nhớ
BST->Data=x;
BST->Left=BST->Right=NULL;
return 1; // thêm vào thành công
}
int delNode(BinarySearchTree &BST, int x){
return int delNode(BST.Root, x);
}
int delNode(Node &BST, int x){
if(isEmpty(BST)) return 0; // không có node nào trong cây có giá trị
bằng x
if(BST->Data>x)
return delNode(BST->Left,x);
if(BST->Data<x)
return delNode(BST->Right,x);
else{
Node p = BST;
if( ) // nếu BST không có node con bên trái
// gán node bên phải vào node hiện tại
else
if ( ) // nếu BST không có node con bên phải
// gán node bên trái vào node hiện tại
else{
Trang 18
Node q=BST->Right;
searchStandFor(p,q);
}
delete p;
}
}
void searchStandFor(Node &p, Node &q){
if( ) // nếu q có node con bên trái
searchStandFor(p,q->Left);
else{
p->Data=q->Data;
p=q;
q=q->Right;
}
}
Sau khi hoàn thành các hàm trong tập tin BinarySearchTree.cpp đã nêu trên. Sinh viên tiếp tục
cài đặt 02 hàm còn lại là createTree và removeTree cũng trong tập tin BinarySearchTree.cpp
Tập tin BinarySearchTree.cpp
BinarySearchTree createTree(){
// Tạo một cây nhị phân tìm kiếm thực hiện bằng cách thêm vào cây
mỗi lần một node
}
void removeTree(BinarySearchTree &BST){
removeTree(BST.Root);
}
void removeTree(Node &BST){
// Hủy cây nhị phân tìm kiếm thực hiện bằng cách duyệt theo thứ tự sau
}
Bài tập áp dụng
2. Sử dụng cây nhị phân để cài đặt bài toán Ba Lan ngược, thay vì sử dụng danh sách liên kết.
3. Dùng cây nhị phân tìm kiếm để quản lý các điểm trong không gian 02 chiều: tìm kiếm, thêm,
xóa, tạo cây, hủy cây, lưu dữ liệu xuống tập tin, đọc dữ liệu từ tập tin lên cây. Cấu trúc tập
gồm: tập tin văn bản và tập tin nhị phân.
4. Xây dựng cấu trúc dữ liệu biểu diễn cây N-phân (2<N<=20). Cài đặt hàm duyệt cây N-phân
và tạo ra cây nhị phân tìm kiếm tương ứng với các giá trị của node của cây N-phân.
5. Cho mảng A là một mảng có thứ tự. Hãy viết chương trình tạo một cây nhị phân tìm kiếm có
chiều cao thấp nhất từ các phần tử của A.
6. Viết hàm đảo nhánh (nhánh trái của một node trên cây trở thành nhánh phải và ngược lại) của
một cây nhị phân.
7. Viết các hàm xác định các thông tin của cây nhị phân T.
Trang 19
a. Số node lá.
b. Số node có đúng 01 cây con.
c. Số node có đúng 02 cây con.
d. Số node có giá trị nhỏ hơn x.
e. Số node có giá trị lớn hơn x.
f. Số node có giá trị lớn hơn x và nhỏ hơn y.
g. Số node ở mức k.
h. Kiểm tra CNPTK có cân bằng hoàn toàn không?
i. Chiều cao của cây.
j. Tổng số node ở mức k và in ra tất cả các node ở mức k.
k. In ra tất cả các node của cây theo thứ tự từ mức 0 đến mức h-1 (h là chiều cao của
cây T) của cây T.
l. Kiểm tra xem T có phải là cây cân bằng hoàn toàn hay không? Có phải là cây cân
bằng hay không?
m. Độ lệch lớn nhất trên cây. Độ lệch của một node là độ lệch giữa chiều cao của cây
con trái và cây con phải của node đó. Độ lệch lớn nhất trên cây là độ lệch lớn nhất
của một node trên cây.
Bài tập mở rộng
8. Cài đặt cấu trúc dữ liệu cây nhị phân cân bằng (cây AVL) và các hàm tiện ích đi kèm với cấu
trúc dữ liệu này.
9. Cải tiến cây nhị phân tìm kiếm để quản lý các điểm trong 2D: tìm kiếm, thêm, bớt, tạo cây,
hủy cây, lưu dữ liệu xuống file, đọc dữ liệu từ file lên cây.
10. Viết chương trình cho phép tạo, tra cứu và sửa chữa từ điển Anh-Việt.