You are on page 1of 35

TRƯỜNG ĐẠI HỌC GIAO THÔNG VẬN TẢI

PHÂN HIỆU TẠI TP. HỒ CHÍ MINH


BỘ MÔN CẤU TRÚC DỮ LIỆU

BÁO CÁO BÀI TẬP LỚN

ĐỀ TÀI : QUẢN LÝ THƯ VIỆN

Giảng viên hướng dẫn: TRẦN THỊ DUNG

Sinh viên thực hiện: NGUYỄN THANH HƯNG

Lớp : KTĐT-VT

Khoá : 62

Tp. Hồ Chí Minh, năm 2022


CHƯƠNG 1. MỞ ĐẦU
I. Mục đích môn học
1. Môn học cung cấp những kiến thức cơ bản, nền tảng về một số cấu trúc dữ liệu
và một số thuật tóan. Nó là cơ sở để xây dựng các hệ thống phần mềm lớn và phức
tạp. 
2. Môn học giúp sinh viên hiểu cách thức tổ chức lưu trữ dữ liệu trong bộ nhớ của
máy tính và làm thế nào để sử dụng nó một cách có hiệu quả trong các chương
trình. Sử dụng những kiến thức này để xây dựng các cấu trúc dữ liệu phù hợp cho
các hệ thống phức tạp khác.
3. Cung cấp cho sinh viên một số thuật toán cơ bản trên các cấu trúc dữ liệu
4. Sinh viên hiểu và biết phân tích thời gian, không gian (bộ nhớ) cần cho một thuật
toán.
II. Các đối tượng nghiên cứu trong môn học
 Các cấu trúc dữ liệu chuẩn
• Vectors, lists, stack, queue,trees, graphs,…
 Các thuật toán chuẩn
• Sắp xếp (Sorting)
• Tìm kiếm (Selection)
 Phân tích độ phức tạp về thời gian và không gian (bộ nhớ) của các thuật toán.
 Những kỹ năng lựa chọn thuật toán, cấu trúc dữ liệu và cài đặt thuật toán
III. Các kiến thức bổ trợ cho môn học
 Ngôn ngữ lập trình C++
 Phương pháp lập trình hướng đối tượng (Object Oriented Programming method-
OOP)
IV. Tài liệu tham khảo
1. Data structures and Algorithms in C++ - Michael T. Goodrich, Roberto Tamassia
2. Introduction to Algorithms - Thomas H. Cormen, Charles E. Leiserson, Ronald L.
Revest, Clifford Stein,, MIT Press
3. Thuật toán và lập trình - Lê Minh Hoàng, ĐH Sư phạm Hà Nội
4. Cấu trúc dữ liệu và giải thuật – Nguyễn Văn Long, NXB GTVT
5. Cấu trúc dữ liệu và giải thuật – Đỗ Xuân Lôi, NXB Khoa học kỹ thuật
6. Cẩm nang thuật toán (vol1 + vol2), Robert Sedgewick, NXB KHKT
7. Lập trình hướng đối tượng và C++, Phạm Văn Ất, NXB GTVT
V. Một số mục tiêu của công nghệ phần mềm
 Tin cậy và chính xác (Reliability – correctness)
 Hữu dụng (utility)
o Đạt được những gì mong muốn
o Đáp ứng đúng thời điểm
 Mềm dẻo (flexibility)
o Có khả năng mang chuyển được (Portability), tức là có thể dễ
dàng mang cài đặt sang một hệ thống khác.
o Khả năng tương thích
- Dễ bảo trì
- Dễ hiểu
- Có thể sử dụng lại
 Hiệu quả (efficiency)
o Người lập trình (không mất quá nhiều công sức cho
việc lập trình)
o Máy
 Thời gian
 Bộ nhớ

VI. Các nguyên lý của CNPM


 Trừu tượng (Abstract): Chắt lọc một hệ thống phức tạp xuống những phần cơ bản
nhất của nó và mô tả những phần này bằng một ngôn ngữ đơn giản, chính xác
 Modul hóa (Modularity): Hạn chế độ phức tạp bằng cách phân chia ra thành nhiều
phần (Chia để trị).
 Mô-đun đề cập đến một nguyên tắc tổ chức mã trong đó các thành phần
khác nhau của hệ thống phần mềm được chia thành các đơn vị chức năng
riêng biệt
 Đóng gói (Encapsulation):
 Các thành phần khác nhau của một hệ thống phần mềm không được tiết lộ
các chi tiết bên trong của chúng
CHƯƠNG 2. Ngôn ngữ lập trình C++
I.Giới thiệu
 Ngôn ngữ lập trình C++ là ngôn ngữ được phát triển dựa trên ngôn ngữ lập
trình C.
 Do đó về cơ bản, cú pháp của C++ giống với cú pháp của C. Tuy nhiên nó
có một số mở rộng sau đây:
- Nhập, xuất dữ liệu (cout, cin)
- Hàm có đối mặc định, hàm có đối tham chiếu
- Nạp chồng hàm (hay tải bội hàm – overload function)
- Hàm mẫu
- Lớp (có khả năng xây dựng các chương trình HĐT)

Thiết lập biên dịch C++11 cho dev C++


• Tools-> Compiler option -> General
• Check vào box
Add the following commands when calling the
compiler
• Soạn vào
–std=c++11
II. Nhập xuất dữ liệu
• Nhập dữ liêu kiểu số
cin>>Tênbiến1>>Tênbiến2>>…>>Tênbiếnn;
Ví dụ:
float x,y;
int m, n;
cin>>x>>y;
cin>>m;
cin>>n;
• Nhập dữ liệu kiểu xâu ký tự
cin.ignore(1);
cin.get(Tênbiến, n); //n là số ký tự tối đa cần gán cho biến
Ví dụ:
char ht[30];
char w[10];
cin.ignore(1);
cin.get(ht, 30);
cin.ignore(1);
cin.get(w, 5);
• Xuất dữ liệu
cout<<Bthức1<<Bthức2<<…<<Bthứcn;
Ví dụ:
#include”iostream.h”
void main()
{
float x, y=10;
cout<<“Nhap x=“;
cin>>x;
cout<<“x+y=“<<x+y;
cout<<“x-y=“<<x-y;
}
III. Hàm
• Khi xây dựng các hàm ngoài các kiểu hàm như trong C thì C++ còn cho
phép xây dựng các kiểu hàm sau đây:
• Đối tham chiếu
• Đối mặc định
• Nạp chồng hàm (overload function)
• Hàm mẫu (template)
• Hàm có đối tham chiếu
- - Khai báo hàm:
- DataType Func_Name(DataType & Arg_Nam,..);
- Sử dụng hàm: Các đối thực sự tương ứng với đối tham chiếu phải là các
biến cùng kiểu với kiểu của đối.
- Sự hoạt động của hàm như hàm có đối con trỏ
- Ví dụ: Xây dựng hàm hoán đổi giá trị của hai biến
void hoandoi(float &a, float &b) void main(){
{
float x, y;
float tg;
cout<<“ Nhap x, y: ”;
tg = a;
cin>>x>>y;
a = b;
cout<< “x = “<< x <<“ y = “<<y;
b = tg;
hoandoi(x,y);
}
cout<< “x = “<< x <<“ y = “<<y;
getch(); }

#include <iostream.h>
#include <conio.h>
void duplicate (int& a, int& b, int& c)
{ a = 2; b = 2; c = 2; }
int main (){
int x=1, y=3, z=7;
duplicate (x, y, z);
cout << "x=" << x << ", y=" << y << ", z=" << z;
return 0;
}

CHƯƠNG 3. DANH SÁCH LIÊN KẾT


I.Khái niệm danh sách
 Là cấu trúc dữ liệu tuyến tính, trong đó các phần tử dữ liệu đượcsắp xếp theo
một thứ tự xác định
 Là một tập sắp thứ tự các phần tử cùng một kiểu
 Ví dụ
o Danh sách sinh viên
o Danh sách điện thoại
o Danh sách môn học
o Danh sách bài hát
o Danh sách công việc

II.Trừu tượng hóa danh sách


• Đặc tả dữ liệu
• Tất cả các phần tử của danh sách sắp theo thứ tự nào đó.
• Đặc tả các phép toán
1. Khởi tạo/xóa danh sách
2. Kiểm tra danh sách rỗng/đầy
3. Đếm số phần tử có trong danh sách
4. Truy xuất một phần tử trong danh sách theo vị trí
5. Thêm/xóa/thay thế một phần tử
6. Duyệt tất cả các nút
• Đặc tả dữ liệu
A = (a0, a1, …, an), trong đó ai là phần tử thứ i của danh sách A. Ví dụ:
A = (1, 2, 3, 4, 5)
A = (‘Toán’, ‘Lý’, ‘Hóa’, ‘Văn’, ‘Anh’)
• Đặc tả các phép toán
1. Khởi tạo/xóa danh sách: init(A)
2. Kiểm tra danh sách rỗng/đầy: isEmpty(A) / isFull(A)
3. Đếm số phần tử có trong danh sách: length(A)
4. Truy xuất một phần tử trong danh sách theo vị trí: retrieve(A, i)
5. Thêm phần tử x vào vị trí i trong danh sách: insert(A, i, x)
6. Thêm phần tử x vào đuôi danh sách: append(A, x)
7. Xóa phần tử ở vị trí i trong danh sách: remove(A, i)
8. Thay thế phần tử ở vị trí i trong danh sách: replace(A, i, x)
Cài đặt danh sách bằng mảng
Mảng (array)
• Tập hợp các phần tử (các biến) có cùng một kiểu.
• Một phần tử cụ thể trong mảng sẽ được xác định và truy cập bởi một chỉ số.
• Trong C/C++, các phần tử của mảng được đặt cạnh nhau tạo thành một khối liên
tục. Địa chỉ thấp nhất tương ứng với phần tử đầu tiên, địa chỉ cao nhất tương ứng
với phần tử cuối cùng.
• Mảng thì có thể là một chiều hoặc nhiều chiều.
• Mảng một chiều tĩnh:
• int A[100];
• phanso dayphanso[1000];
• Mảng một chiều động:
• int *A = new int[100];
• phanso *dayphanso = new phanso[1000];

Cài đặt hàm


Cài đặt danh sách bằng mảng động

Hàm insert, append của Dlist


• Khi mảng đầy
• Cấp phát động một mảng mới có cỡ gấp đôi mảng cũ
• Chép đoạn đầu của mảng cũ sang mảng mới
• Đưa phần tử cần xen vào mảng mới
• Chép đoạn còn lại của mảng cũ sang mảng mới
• Hủy mảng cũ
• Cập nhật size, last
• Ví dụ:
• A = (1, 3, 4, 8); size = 4; last = 3
• insert(A, 3, 50)
Danh sách liên kết
Độ phức tạp khi cài đặt danh sách bằng mảng
• Truy cập phần tử
• Cập nhật?
• Chèn phần tử?
• Xóa phần tử?
Danh sách liên kết
• Là tập dữ liệu tuần tự mà mỗi phần tử chứa vị trí của phần tử tiếp theo.
• Cấp phát động lúc chạy chương trình
• Các phần tử nằm rải rác ở nhiều nơi trong bộ nhớ
• Kích thước danh sách chỉ bị giới hạn do RAM
• Thao tác thêm xoá đơn giản
• DSLK đơn, DSLK kép, DSLK vòng.

Danh sách liên kết đơn


Khái niệm DSLK đơn
• DSLK đơn là một danh sách các nút,
• Mỗi nút gồm 2 thành phần:
• Phần chứa dữ liệu – data
• Phần chỉ vị trí (địa chỉ) của phần tử tiếp theo trong danh sách –Phần next là
con trỏ, trỏ đến nút kế tiếp
Danh sách liên kết đơn bằng C++
• Mỗi nút là một biến Node.
• Nút cuối cùng có giá trị next bằng NULL.
• Xác định DSLK bằng địa chỉ của nút đầu tiên
trong danh sách.
• Gọi biến lưu địa chỉ này là con trỏ đầu head
• Khởi tạo danh sách rỗng: Node *head = NULL;
data next

• Có thể sử dụng thêm con trỏ đuôi tail để các thao tác trên DSLK được thuận lợi
• Danh sách rỗng: head = tail = NULL;

CHƯƠNG 4. CẤU TRÚC NGĂN XẾP (STACK)


I.Stack là gì?
• Stack là một danh sách đặc biệt mà việc thêm vào và loại bỏ được thực hiện tại
một đầu (gọi là đỉnh – top của stack).
• Hoạt động theo cơ chế LIFO (Last In First Out)
Phương pháp cài đặt
Khai báo
struct node
{
DataType data; node *next;
};
typedef node* stack;
Các thao tác trên stack
• Khởi tạo: init
• Kiểm tra rỗng: isEmpty
• Thêm một phần tử vào stack: push
• Lấy một phần tử ra khỏi stack: pop
• Xem nội dung của phần tử đầu tiên trong stack: peek
Thêm phần tử x vào stack- push
• Tạo nút mới có dữ liệu là x
• Thêm nút vừa tạo vào đầu stack

// khai bao cau truc stack struct node


{
int data; node *next;
};
typedef node* stack;
void push(stack &s, int x)
{
node* p = new node; p->data = x;
p->next = s; s = p;
}
Lấy một phần tử ra khỏi stack – pop
• Lấy ra phần tử đầu danh sách
• Trả về nội dung và giải phóng nút
Trung tố, tiền tố, và hậu tố
• Trung tố (infix), hậu tố (postfix, ký pháp Ba Lan ngược), tiền tố (prefix, ký pháp
Ba Lan) là các cách biểu diễn khác nhau của biểu thức toán học.
• Trung tố: cách biểu diễn thông dụng
• Toán tử được viết giữa các toán hạng.
• Phép toán có thứ tự ưu tiên.
• Thứ tự thực hiện phép toán từ trái qua phải nếu phép toán có cùng thứ tự ưu
tiên.
• Sử dụng dấu ngoặc đơn () để thay đổi thứ tự thực hiện của
phép toán.
• Ví dụ: A * (B + C) / D
• Tiền tố (Ký pháp Ba Lan)
• Toán tử được viết trước toán hạng.
• Thứ tự thực hiện phép toán từ trái qua phải.
• Không sử dụng dấu ngoặc đơn () để thay đổi thứ tự thực hiện của phép
toán.
• Phép toán được thực hiện trên các toán hạng nằm ngay sau
phép toán.
• Ví dụ: / * A + B C D
• Hậu tố (Ký pháp Ba Lan ngược)
• Toán tử được viết sau các toán hạng.
• Thứ tự thực hiện phép toán từ trái qua phải
• Không sử dụng dấu ngoặc đơn () để thay đổi thứ tự thực hiện của phép
toán.
• Phép toán được thực hiện trên các toán hạng nằm ngay trước
phép toán.
• Ví dụ: A B C + * D /
Một số ví dụ
• a / (b – c + d) * (e – a) * c
• a/b–c+d*e–a*c
• a+b*c–d/e*f
• (a + b * c – d) / (e * f)
• (7 / (5 – 3) * 9 + 2)
CHƯƠNG 5. TÌM KIẾM (SERCH)
1. Tìm kiếm tuần tự (sequence search)
- Thuật toán :
o Xuất phát từ phần tử đầu của dãy, thực hiện so sánh khóa của nó với
k. Nếu trùng nhau thì dừng lại, nếu không trùng thì lặp lại với phần
tử tiếp theo.
o Quá trình dừng lại khi tìm thấy hoặc không còn phần tử nào nữa. Khi
đó thông báo không tìm thấy.
- Chương trình:
while (i<n){
if (S[i].key == k)
return i;
i = i+1; //Chuyển sang phần tử kế tiếp
}
return -1;
- Thời gian chạy: O(n)

2. Tìm kiếm nhị phân (Binary search)


- Tìm kiếm nhị phân trên mảng:
o Thuật toán tìm kiếm nhị phân được thiết kế dựa trên chiến lược loại
trừ
o Thuật toán: So sánh khóa k với khóa của phần tử ở giữa dãy.
 Nếu trùng thì thông báo tìm thấy và dừng
 Nếu k> thì gọi đệ qui tìm trên nửa cuối dãy
 Nếu k< thì gọi đệ qui tìm trên nửa đầu dãy
 Quá trình tìm nếu phải tìm trong dãy rỗng thì dừng lại và
thông báo không tìm thấy
- Chương trình:
i = 1;
j = n;
while(i<=j){
mid = (i+j) / 2;
if (k==S[mid].Key
return mid;
else
if (k < S[mid].Key)
j = mid – 1;
else
i = mid+1;
}
return -1;
3. Bảng băm
a. Cấu trúc hàm băm
- Hàm băm có dạng như sau: h : K → 0..m-1

- Trong đó:
o h được gọi là hàm băm (hash function)
o K là tập giá trị khóa
o 0..m-1 là bảng địa chỉ (là các số nguyên)
o m là kích thước của bảng
- Yêu cầu khi xây dựng hàm băm:
o Hàm phải dải đều các địa chỉ trên bảng địa chỉ
o Hàm băm phải được tính toán đơn giản.
b. Một số phương pháp xây dựng hàm băm
- Phương pháp chia
o Để tính địa chỉ dải của đối tượng ta lấy giá trị khóa chia cho kích
thước của bảng. Địa chỉ dải là phần dư của phép chia đó.
H(K) = K % m
o Yêu cầu:
 hàm h phải dải đều các đối tượng trên bảng một cách ngẫu
nhiên. Để có được điều đó h phải phụ thuộc vào m.
 Phụ thuộc vào m
 Thông thường người ta chọn m là một số nguyên tố nhỏ hơn
gần với (10,100, 1000,...) nhất.
- Phương pháp nhân
o Giá trị khóa được phân ra thành nhiều đoạn bằng nhau
 Người ta sử dụng hai kỹ thuật phân đoạn sau đây:
 Tách: Tách các đoạn ra và mỗi đoạn được xếp thành một
hàng, dóng lề trái hoặc lề phải.
 Gấp: Gấp các đoạn lại theo đường biên tương tự như gấp giấy,
các chữ rơi vào cùng một chỗ được đặt thành hàng thẳng
nhau.
c. Bảng băm - Hash table
- Một bảng băm là một cấu trúc dựa trên mảng để lưu trữ các phần tử, mỗi
phần tử là một cặp Khóa-Giá trị (key-value)
- Các thành phần cấu thanh lên bảng băm:
o Mảng chứa
o Mỗi phần tử mảng quản lý một danh sách các phần tử có khóa qua
ánh xạ h cho cùng một địa chỉ.
o Hàm băm h(k) - Hash function, h(k)
o Mã băm

EX: Giả sử có hàm h(k) = k % 5 Có các giá trị: 11, 21, 44, 23, 41, 4, 34, 12

d. Cấu trúc dữ liệu bảng băm


- Thuộc tính:
o Mảng (mỗi phần tử mảng lưu một danh sách các phần tử)
o m: kích thước mảng
- Các phương thức:
o Node *Add(Key, Object)
o void Remove(Key)
o Node *Find(Key)
o bool Contains(Key)
o int Count()

Chương 6: Các thuật toán sắp xếp (SORTING)


Bài toán
 Input:
 Dãy các phần tử (và một thứ tự)
 (Dãy các phần tử thường được lưu bằng mảng.)

 Output:
 Dãy các phần tử được sắp theo thứ tự tăng hoặc giảm dần theo một hoặc
một vài thuộc tính của nó (các thuộc tính này gọi là thuộc tính khóa).
 Thuộc tính khóa được sắp xếp theo một hàm logic, ví dụ (<=) hoặc các toán
tử so sánh khác.
Các thuật toán với thời gian chạy O(n2)
 Nổi bọt – Bubble sort
 Chèn – Insertion sort
 Chọn – Selection sort
Sắp xếp nổi bọt – Bubble sort
Ý tưởng: Thực hiện chuyển dần các phần tử có giá trị khóa lớn về cuối dãy,
các phần tử có khóa bé về đầu dãy.
Thuật toán

- Trong đó swap là thủ tục tráo đổi vị trí của hai phần tử
void Swap(object &a, object &b){
Object tg;
tg = a;a = b; b = tg;
}
Sắp xếp chọn - Selection sort
• Ý tưởng: Chọn phần tử có khóa nhỏ nhất trong các phần tử còn lại chuyển nó về
đầu và loại bỏ nó khỏi dãy.

Sắp xếp chèn – Insertion sort

10
Chia và trị - Divide and conquer
 Chia và trị là phương pháp thiết kế thuật toán theo kiểu:
 Phân chia: Chia dữ liệu đầu vào S của bài toán thành 2 tập con rời nhau S1
và S2
 Đệ qui: Giải bài toán với dữ liệu vào là các tập con S1 và S2
 Trị: kết hợp các kết quả của S1 và S2 thành kết quả của S
 Trường hợp cơ sở cho thuật toán đệ qui ở đây là các bài toán có kích thước 0 hoặc
1
Sắp xếp nhanh – Quick sort
 Ý tưởng (sử dụng phương pháp chia và trị):
 Thực hiện phân hoạch dãy S cần sắp thành 3 dãy S1, S2, S3. Trong đó:
• S2 chỉ có một phần tử
• Tất cả các phần tử của dãy S3 đều > phần tử của dãy S2.
• Tất cả các phần tử của dãy S1 đều ≤ phần tử của dãy S2
• Dãy S1, S3 có thể là rỗng
 Tiếp tục phân hoạch dãy S1 và S3 độc lập theo nguyên tắc trên đến khi dãy
cần thực hiện phân hoạch chỉ có một phần tử thì dưng lại. Khi đó ta được
dãy các phần tử được sắp.
Thuật toán sắp xếp Quick sort
 Từ ý tưởng của thuật toán, ta có thể dễ dàng xây dựng thuật toán sắp xếp dưới
dạng đệ qui như sau:
 Algorithm QuickSort (array A, i, j );
 Input: Dãy các phần tử A[i],..,A[j] và hai số nguyên i, j
 Output: Dãy A[i],..,A[j] được sắp.
 if i < j then
 Partition (A,i, j, k); //k lấy chỉ số của phần tử làm S2
 Quicksort (A,i, k-1);
 Quicksort (A,k+1, j);
Thuật toán phân hoạch
• Chọn một phần tử bất kỳ của dãy làm dãy S2 (phần tử này được gọi là phần tử chốt
-pivot).
• Thực hiện chuyển các phần tử có khóa ≤ phần tử chốt về bên trái và các phần tử >
phần tử chốt về bên phải, sau đó đặt phần tử chốt về đúng vị trí của nó trong dãy.
Chú ý :
• Phần tử chốt có thể được chọn là một phần tử bất kỳ của dãy.
- Phần tử chốt có thể chọn là phần tử đầu hoặc giữa hoặc cuối dãy.
- Tốt nhất là chọn phần tử chốt mà nó làm cho việc phân hoạch thành hai dãy S1 và
S3 có số phần tử xấp xỉ bằng nhau.
Thuật toán
• Phân hoạch dãy gồm các phần tử A[i],..,A[j]
• Chọn phần tử đầu dãy làm chốt
• Sử dụng 2 biến left và right:
- left chạy từ trái sang phải bắt đầu từ i.
- right chạy từ phải sang trái bắt đầu từ j
- Biến left được tăng cho tới khi A[left].Key> A[i].Key hoặc
left >right
- Biến right được giảm cho tới khi A[right].Key <= A[i] .Key
- Nếu left< right thì ta đổi A[left] và A[right]
- Quá trình trên được lặp lại cho tới khi nào left > right
- Cuối cùng tráo đổi A[i] và A[right]
Thuật toán phân hoạch
Algorithm Partition (Array A, i, j, &right )
Input: Dãy các phần tử A[i],..,A[j], 2 số nguyên i, j
Output: Dãy A[i],..,A[j] được phân hoạch, right là chỉ số của
phần tử làm S2.
p  A[i];
left  i; right  j;
while ( left < right )
while( A[left].Key <= p.Key && left≤right)
left  left+1;
while( A[right].Key > p.Key ) right right-1;
if left < right then
SWAP(A[left],A[right]);
if i<> right then
A[i]  A[right];
A[right]  p;
Thời gian chạy
• Thủ tục partition kiểm tra tất cả các phần tử trong mảng nhiều nhất một lần, vì vậy
nó mất thời gian tối đa là O(n).
• Thủ tục partition sẽ chia phần mảng được sắp thành 2 phần.
• Mặt khác cần chia liên tiêp n phần tử thành hai phần thì số lần chia nhiều nhất là
log2n lần.
• Vậy thời gian chạy của thuật toán QuickSort là O(nlogn)

Thuật toán MergeSort


Ý tưởng:
• Giả sử ta có hai dãy A[i],..,A[k] và A[k+1],..,A[j] và hai dãy này đã được sắp.
• Thực hiện trộn hai dãy trên để được dãy A[i],..,A[j] cũng được sắp
• Do hai dãy A[i],..,A[k] và dãy A[k+1],..,A[j] đã được sắp nên việc trộn hai dãy
thành một dãy được sắp là rất đơn giản.
• Vậy trộn như thế nào?
Thuật toán trộn
• Sử dụng hai biến left, right, t và sử dụng mảng phụ B[i],..,B[j]. left xuất phát từ i,
right xuất phát từ k+1, t xuất phát tử i trên mảng phụ B.
• Nếu A[left].key<A[right].key thì B[t]A[left], t t+1 và leftleft+1
• Nếu A[left].keyA[right].key thì B[t]A[right], t t+1 và rightright+1
• Quá trình trên được thực hiện cho đến khi left>k hoặc right>j thì dừng lại.

• Nếu left>k thì B[t]A[right],..,B[j]A[j].


• Nếu right>j thì B[t]A[left], B[t+1]A[letf+1],.., B[t+k-left]A[k].
• Gán A[i] B[i], .., A[j] B[j]
Mảng biểu diễn cây heap
• Mảng A[1],..,A[n] là mảng biểu diễn cây heap nếu:
• A[i]A[2i] và A[i]A[2i+1] với i=1..n/2
• Như vậy phần tử đầu của mảng có giá trị lớn nhất
Thuật toán sắp xếp vun đống
Ý tưởng:
• Tạo mảng A[1],..,A[n] biểu diễn cây Heap.
• Tráo đổi phần tử A[1] với phần tử A[n].
• Tạo mảng A[1],..,A[n-1] biểu diễn cây heap
• Tráo đổi phần tử A[1] với phần tử A[n-1].
• Lặp lại quá trình trên đến khi mảng chỉ còn 1 phần tử
Tạo mảng biểu diễn cây heap
• Theo tính chất của mảng biểu diễn cây Heap thì các phần tử từ n/2+1 đến n không
cần điều kiện ràng buộc. Vì vậy ta thực coi các phần tử này đã thỏa mãn điều kiện
cây heap.
• Ta thực hiện:
- Bổ sung phần tử n/2 vào A[n/2+1],..,A[n] để được mảng gồm A[n/2],..,A[n]
thỏa mãn kiện
- Bổ sung phần tử n/2-1 vào A[n/2],..,A[n] để được mảng gồm A[n/2-
1] ,..,A[n] thỏa mãn kiện
- Và cứ tiếp tục làm như vậy cho đến khi bổ sung phần tử A[1] vào
A[2],..,A[n] để được mảng gồm A[1],..,A[n] thỏa mãn điều kiện
Algorithm Heapsort(Array A, n);
Input: Mảng A có n phần tử và số nguyên n
Output: Mảng A được sắp theo thứ tự tăng dần của thuộc tính khóa
for i n/2 downto 1 do
Pushdown(A, i, n);
for i ndownto 2 do
swap(A[1],A[i]);
Pushdown(A,1,i-1);

Thời gian chạy


• Thời gian thực hiện thủ tục Pushdown.
 Là t/g thực hiện của vòng lặp while.
 Gọi k là số lần lặp, ta có i*2k  n hay k  log2(n/i).
 T/g thực hiện hàm Pushdown (A,i, n) là 0(log(n/i)
• Xét thủ tục HeapSort 
 Vòng lặp for đầu có số lần lặp là n/2
 Mỗi lần gọi hàm Pushdown 1 lần. Do đó t/g thực hiện là 0(log2n).
 Tương tự, vòng lặp for thứ 2 có số lần lặp là n-1. 0(nlog2n).
 Vì vậy t/g thực hiện HeapSort là O(nlog2n).4

CHƯƠNG 7: VECTOR
I. Khái niệm về vector
- Vector là một mảng động có thể thay đổi kích thước,không nhất thiết phải khai
báo kích thước cố định như mảng tĩnh.Nó có thể tự động tăng hay giảm kích
thước khi ta xóa hoặc chèn phần tử khác vào vector

II. Một số điểm nổi bật của vector


Không cần phải khai báo kích thước của mảng vì vector có thể tự động tăng kích
thước lên.
- Nếu bạn nâng thêm 1 phần tử vào vector đã đầy rồi, thì nó sẽ tự động tăng kích
thước của nó để dành chỗ cho giá trị mới này.
- Vector còn giúp bạn biết số lượng các phần tử mà bạn đã lưu trong đó.
- Dùng số phần tử âm vẫn được trong vector.
III. Cú pháp của một vector:
Vector< kiểu dữ liệu >tên vector.
- Để sử dụng được cấu trúc dữ liệu vector ta phải khai báo thư viện vector
o Hàm push_back(): Là hàm cho phép ta đẩy 1 phần tử vào vị trí sau
cùng của vector.
o Hàm pop_back(): Là hàm cho phép ta xóa phần tử cuối cùng của
vector.
o Hàm size: Là hàm cho phép ta kiểm tra số lượng phần tử trong
vector.
Ví dụ:
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int>v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(50);
v.pop_back();
cout<<v.size()<<endl;
for(int i=0;i<v.size();i++){
cout<<v[i]<<endl;
}
}
CHƯƠNG 8. CÂY
I.Tổng quát:
- Cây là một tập các nút với quan hệ cha-con (parent-child) giữa các nút.
Trong đó có một nút được gọi là gốc và nó không có cha.
- Các ứng dụng:
o Tổ chức biểu đồ
o Hệ thống file
o Các môi trường lập trình …

II.Một số khái niệm:


- Gốc (root): gốc là nút không có nút cha ( vd: A)
- Nút trong: Nút có ít nhất một nút con (Vd: A, B, C, F)
- Nút ngoài (lá): nút không có nút con (Vd: E, I, J, K, G, H, D)
- Đô sâu của một nút: Nút gốc có độ sâu là 0, nếu nút cha có độ sâu là h thì
nút con có độ sâu là h+1
- Chiều cao của cây: là giá trị lớn nhất của độ sâu của tất cả các nút (3)
- Cây con: Cây bao gồm một số nút của một cây ban đầu

III.Cấu trúc dữ liệu cây


- Định nghĩa Cấu trúc dữ liệu cây là một cấu trúc dữ liệu phi tuyến, trừu
tượng, phân cấp có quan hệ cha con giữa hai node kề nhau gồm:
o Một node gốc không có cha
o Và các cây con của nó sao cho 1 node bất kỳ đều có duy nhất một
đường đi tới gốc do mỗi node có duy nhất 1 cha.
- Các phương thức chung:
o int size()
o int isEmpty()
- Các phương duyệt cây:
o void preorder(Node*)
o void inorder(Node*)
o void postorder(Node*)
- Các phương thức truy cập:
o Địa chỉ root()
- Các phương thức truy vấn:
o int isInternal(Node*)
o int isExternal(Node*)
o int isRoot(Node*)
- Thêm vào đó là những phương thức cập nhật được định nghĩa trong các cấu
trúc dữ liệu tạo Tree ADT (Node tạo cây)
- Phương thức thêm phần tử vào cây.
o void insert(Node* parent, Element e)
- Phương thức xóa phần tử
o void remove(Node*);

IV.Các phương thức duyệt

a. Preorder (tiền thứ tự) : Gốc rồi đến các cây con
- Duyệt cây là cách đi thăm các nút của cây theo một hệ thống
- Duyệt theo thứ tự trước, tức là: nút cha được thăm trước sau đó thăm các nút
con, cháu, …

Algorithm preOrder(v)

If(v!=null)

visit(v)

for mỗi nút con w của v

preorder (w)

b. Inorder (trung thứ tự) : Con cả đến Gốc rồi các con còn lại
- Duyệt theo thứ tự giữa, tức là: nút con được thăm trước sau đó thăm nút cha
- Ứng dụng: Tính toán không gian sử dụng bởi các files và
các thư mục con

Algorithm inOrder(v)
If(v!=null)
w = con cả của v
inOrder(w)
visit(v)
for mỗi nút con w1#w của v
inOrder (w1)

c. Postorder (Hậu thứ tự) : Các con rồi đến gốc


- Duyệt theo thứ tự sau, tức là: nút con được thăm trước sau đó thăm nút cha
- Ứng dụng: Tính toán không gian sử dụng bởi các files và các thư mục con

Algorithm postOrder(v)

If(v!=null)

for mỗi nút con w của v

postOrder (w)

visit(v)

V.Cây nhị phân (Binary tree)


- Cây nhị phân là một cây có các tính chất sau:
o Mỗi một nút trong có nhiều nhất 2 nút con
o Các nút con của một nút là một cặp có thứ tự
- Chúng ta gọi con của một nút trong là con trái và con phải
- Định nghĩa cây nhị phân bằng đệ qui:
o Cây nhị phân là:
 Một cây chỉ có một nút hoặc
 Là cây mà nút gốc của nó có cặp nút con có thứ tự, mỗi một
nút con là gốc của một cây nhị phân
- Ứng dụng:
o Biểu diễn các biểu thức toán học
o Quá trình quyết định
o Tìm kiếm
VI.Cây biểu thức
- Cây nhị phân biểu diễn một biểu thức toán học
o Các nút trong: là các toán tử (operators)
o Các nút ngoài: các toán hạng (operands)
- Ví dụ: Cây biểu thức cho biểu thức (2 × (a − 1) + (3 × b))

VII.Cây quyết định (Decision tree)


- Cây kết hợp với một quá trình quyết định
o Các nút trong: Các câu hỏi với câu trả lời yes/no
o Các nút ngoài: các quyết định
- Ví dụ: Cây quyết định tuyển nhân viên

1. Một số định nghĩa


- Cây nhị phân hoàn chỉnh: Là cây nhị phân mà tất cả các nút trong của nó đều
có đủ hai nút con
- Cây nhị phân đầy đủ: là cây nhị phân hoàn chỉnh và tất cả các lá đều ở cùng
mức

PHẦN II: Chương tình quản lý thư viện bằng ngôn ngữ
C++

MỤC ĐÍCH : Nhóm chọn đề tài này vì nó thể hiện được gần hết những thứ đã
được học trong môn cấu trúc dữ liệu
Giao diện chương trình

Cấu trúc chương trình:


- Menu:
o code in ra phần menu:
do{
system ("cls");
cout << "\t\t\t\t\t************************************"<<endl;
cout << "\t\t\t\t\t* MENU *"<<endl;
cout << "\t\t\t\t\t* 1. Dang ki ban doc *"<<endl;
cout << "\t\t\t\t\t* 2. Nhap sach *"<<endl;
cout << "\t\t\t\t\t* 3. Muon sach *"<<endl;
cout << "\t\t\t\t\t* 4. Tra sach *"<<endl;
cout << "\t\t\t\t\t* 5. Danh sach ban doc *"<<endl;
cout << "\t\t\t\t\t* 6. Danh sach sach *"<<endl;
cout << "\t\t\t\t\t* 7. Sap xep so ban doc *"<<endl;
cout << "\t\t\t\t\t* 0. Thoat khoi chuong trinh *"<<endl;
cout << "\t\t\t\t\t************************************"<<endl;
cout << "Chon cong viec can thuc hien: ";
o sử dụng lênh switch…case để chọn lệnh cần thực hiện
cin >> u;
switch(u){
o lệnh 1:
 thực hiện đang kí bạn đọc
 gọi đến lệnh bandoc::dangki()
case 1: {
cout<<"Dang ki ban doc"<<endl;
bandoc::dangki();
system("pause");
break; }
o Lệnh 2:
 Thực hiện việc nhập sách cho thư viện
 Gọi đến lệnh sach::nhapsach()
case 2: {
cout<<"Nhap sach"<<endl;
sach::nhapsach();
system("pause");
break;
}
- Lệnh 3:
o Thực hiện việc mượn sách của sinh viên
o Gọi đến lệnh muon()
case 3: {
cout<<"Muon Sach"<<endl;
muon();
system("pause");
break;
}
o Lệnh 4:
 Thực hiện việc trả sách của sinh viên
 Gọi đến lệnh tra()
case 4 : {
cout<<"Tra sach"<<endl;
tra();
system("pause");
break;
}
o Lệnh 5:
 Thực hiện việc in ra danh sách các bạn đọc cùng với số sách
đã mượn
 Gọi đến lệnh bandoc::inds()
case 5: {
cout<<"Danh sach ban doc"<<endl;
bandoc::inds();
system("pause");
break;
}
o Lệnh 6:
 Thực hiện việc in ra danh sách sách cùng với số đầu sách
 Gọi đến lệnh sach::inds()
case 6: {
cout<<"Danh sach sach"<<endl;
sach::inds();
system("pause");
break;
}
o Lệnh 7:
 Thực hiện việc sắp xếp các bạn đọc
 Gọi đến lệnh bandoc::sapxep()
case 7: {
bandoc::sapxep();
system("pause");
break;
}
- Lênh 0: chọn số 0 chương trình sẽ trả về nhập lại số khác
case 0:{
break;
}
- Lệnh khác: lệnh không thực hiện trả về chọn lại
default: "nhap lai";
system("pause");
break;
}
}while(u!=0);
system("pause");
return 0;
}
- Lệnh đăng kí bạn đọc:
o Nhập mã bạn đọc, gán với biến ma đã khai báo
o Nhập tên bạn đọc, gán với biến ten đã khai báo
o In ra đã đăng kí thành công

int k;
char ma[80];
cin.getline(ma, sizeof(k));
cout << "Ma ban doc : ";
cin.getline(ma, sizeof(ma));
char ten[80];
cout << "Ten ban doc : ";
cin.getline(ten, sizeof(ten));
cacbandoc[sobandoc++] = new bandoc(ma, ten);
cout << " Ban doc " << ten << " voi ma so la "<<
ma << " da duoc dang ki "<<endl;

- Lệnh tìm bạn đọc : thực hiện cấu trúc tìm kiếm tuần tự (sequence
search):
for (int i = 0; i < sobandoc; i++)
if (strcmp(ma, cacbandoc[i]->ma) == 0)
return cacbandoc[i];
return 0;
- Lênh xóa bạn đọc: thực hiện viện xóa bạn đọc
for (int i = 0; i < sobandoc; i++)
delete[] cacbandoc[i];
sobandoc = 0;
- Lệnh sắp xếp các bạn đọc :
o Sử dụng thuật toán sắp xếp nổi bọt để sắp xếp các bạn đọc giảm dẫn
theo số sách đã mượn
o In ra bảng biểu diễn các bạn đọc theo danh sách đã sắp xếp .
if (sobandoc==0){
cout<<"khong co ban doc nao trong thu vien"<<endl;
}
else {
cout<<"Danh sach cac ban doc muon nhieu nhat: "<<endl;
for (int i=0;i<sobandoc-2;i++){
for (int j =sobandoc; j>i; j--){
if (cacbandoc[i]->somuon < cacbandoc[j-1]->somuon){
swap(cacbandoc[i]->somuon, cacbandoc[j-1]->somuon);
}
}
}
cout<<left<<setw(20)<<"Ma"<<left<<setw(20)<<"Ten"<<left<<setw(20)<<"Somuon"<<endl;
cout << "------------------------------------------------------------"<<endl;
for (int i = 0; i < sobandoc; i++)
cout << left<<setw(20)<<cacbandoc[i]->ma << left<<setw(20)<<
cacbandoc[i]->ten<< left<<setw(20)<< cacbandoc[i]->somuon <<endl;
}
}
- Lệnh nhập sách vào kho:
o Nhập mã sách , gán vào biến ma đã khai báo
o Nhập tên sách , gán vào biến ten đã khai báo
o Nhập số đầu sách sách , gán vào biến n đã khai báo
int h;
char ma[80];
cin.getline(ma, sizeof(h));
cout << "Ma sach :";
cin.getline(ma, sizeof(ma));
char ten[80];
cout << "Ten sach : ";
cin.getline(ten, sizeof(ten));
int n;
cout << "So dau sach : ";
cin >> n;
cin.ignore(1);
khosach[sosach++] = new sach(ma, ten, n);
cout << "Sach nay da duoc nhap vao \n";

- Lệnh in ra danh sách các sách: in ra các cuốn sách có (tên sách, mã sách
và số đầu sách còn lai trong kho)
cout << left<< setw(20)<< "Ma sach"<<left<<setw(20)<<"Ten
sach"<<left<<setw(20)<<"so dau sach"<<endl;
cout<<"-------------------------------------------------------------"<<endl;
for (int i = 0; i < sosach; i++){
cout <<left<<setw(20)<< khosach[i]->ma << left<<setw(20) <<
khosach[i]->ten <<left<<setw(20)<< khosach[i]->sodausach<<endl;
- Lệnh mượn sách:
o Nhập mã bạn đọc muốn mượn, gán với biến mabd đã khai báo
o Nhập mã sách muốn mượn, gán với biến mas đã khai báo

char mabd[80];
cin.getline(mabd, sizeof(k));
cout << "Ma ban doc : ";
cin.getline(mabd, sizeof(mabd));
bandoc* bd = bandoc::timbd(mabd);
if (bd == 0)
{
cout << "Ma ban doc nay chua duoc dang ki \n ";
return;
}
char mas[80];
cout << "Ma sach muon : ";
cin.getline(mas, sizeof(mas));
bd->muon(mas);

- Lệnh trả sách:


o Nhập mã bạn đọc muốn trả, gán với biến mabd đã khai báo
o Nhập mã sách muốn trả, gán với biến mas đã khai báo
char mabd[80], mas[80];
cout << "Ma ban doc : ";
cin.getline(mabd, sizeof(mabd));
bandoc* bd = bandoc::timbd(mabd);
if (bd == 0)
{
cout << "Ma ban doc nay chua duoc dang ki \n";
return;
}
cout << "Ma sach tra : ";
cin.getline(mas, sizeof(mas));
int id;
cout << "Ma dau sach : ";
cin >> id;
dausach* ds = sach::timdausach(mas, id);
if (ds == 0)
{
cout << " ko co dau sach nay \n ";
return;
}
bd->tra(ds);

Link Github : https://github.com/hungsvvl/libary-manager

You might also like