1.1.Các thao tác trên danh sách liên kếtDanh sách liên kết là 1 cấu trúc dữ liệu có kiểu tuần tự, mỗi phần tử trong danh sách liên kết có chứa thông tin, qua đó ta có thể truy cập tới ph
Trang 1TRƯỜNG ĐẠI HỌC PHENIKAA KHOA KHOA HỌC CƠ BẢN
⸎⸎⸎⸎⸎
BÀI TẬP LỚN KẾT THÚC HỌC PHẦN
Môn Cấu trúc dữ liệu và thuật toán
Lớp Cấu trúc dữ liệu và thuật
toán_1.1(13.14FS).2_LT
Trang 21.1 Các thao tác trên danh sách liên kết
Danh sách liên kết là 1 cấu trúc dữ liệu có kiểu tuần tự, mỗi phần tử trong danh sách liên kết có chứa thông tin, qua đó ta có thể truy cập tới phần tử này
Danh sách liên kết có thể được xem như là 1 sự bố trí tuần tự các phần
tử trong 1 tập Bắt đầu từ 1 nút, ta coi đó là phần tử đầu tiên trong danh sách Từ nút này, theo liên kết mà nó trỏ tới, ta có nút thứ 2, được coi là phần tử thứ 2 trong danh sách, v.v cứ tiếp tục như vậy cho đến hết danh sách Nút cuối cùng có thể có liên kết là một liên kết null, tức là không trỏ tới nút nào, hoặc nó có thể trỏ về nút đầu tiên để tạo thành 1 vòng
• Các thao tác trên danh sách liên kết
- Tạo, cấp phát, và giải phóng bộ nhớ cho 1 nút
listnode p; // Khai báo biến p
p = (listnode)malloc(sizeof(struct node));//cấp phát bộ nhớ cho p
free(p); //giải phóng bộ nhớ đã cấp phát cho nút p;
Trang 3- Chèn một nút vào đầu danh sách
B1 Tạo và cấp phát bộ nhớ cho một nút mới ,đc trỏ tới bởi q
B2 Sau khi gán các giá trị thích hợp cho phần tử của nút mới, cho con trỏ tiếp của nút mới trỏ đến phần tử đầu tiên của nút
B3 Để p trỏ đến đầu nút danh sách ,ta cần cho p trỏ đến nút mới tạo
void Insert_Begin(listnode *p, int x){
Trang 4- Chèn một nút vào cuối danh sách
B1 Dịch chuyển con trỏ tới nút cuối của danh sách Để làm được việc này, ta phải khai báo một biến con trỏ mới r Ban đầu, biến này, cùng với
p, trỏ đến đầu danh sách Lần lượt dịch chuyển r theo các nút kế tiếp chotới khi đến cuối danh sách
B2 Cho con trỏ tiếp của nút cuối (được trỏ tới bởi r) trỏ đến nút mới tạo
là q, và con trỏ tiếp của q trỏ tới null
void Insert_End(listnode *p, int x){
Trang 5}
}
- Chèn một nút vào trước nút r trong danh sách
B1 Cho con trỏ tiếp của nút mới trỏ đến nút kế tiếp của nút r
B2 Cho con trỏ tiếp của nút r trỏ đến q
void Insert_Middle(listnode *p, int position, int x){
int count=1, found=0;
Trang 6- Xóa một nút ở đầu danh sách
B1 Dùng một con trỏ tạm q trỏ đến đầu danh sách
B2 Dịch chuyển con trỏ p qua phần tử đầu tiên đến phần tử kế tiếp
Trang 7B3 Ngắt liên kết của biến tạm q với nút tiếp theo, giải phóng bộ nhớ Cho q.
Trang 8- Xóa một nút ở cuối danh sách
B1 Dịch chuyển con trỏ tới nút gần cuối của danh sách Để làm được việc này, ta phải dùng 2 biến tạm là q và r Lần lượt dịch chuyển q và r
từ đầu danh sách tới cuối danh sách, trong đó q luôn dịch chuyển sau r một nút Khi r tới nút cuối cùng thì q là nút gần cuối cùng nhất
B2 Cho con trỏ tiếp của nút gần cuối cùng nhất (đang được trỏ bởi q) trỏtới null Giải phóng bộ nhớ cho nút cuối cùng (đang được trỏ bởi r)
Trang 10B3 Ngắt liên kết của nút q và giải phóng bộ nhớ cho q
void Remove_Middle(listnode *p, int position){ int count=1, found=0;
Trang 11}
if (found==0)
printf(“Khong tim thay vi tri can xoa !”);
}
- Duyệt toàn bộ danh sách
Thao tác duyệt danh sách cho phép duyệt qua toàn bộ các phần tử của danh sách, từ phần tử đầu tiền cho tới phần tử cuối cùng
Để thực hiện thao tác này, ta cần một biến tạm r trỏ tới đầu danh sách
Từ vị trí này, theo liên kết của các nút, thực hiện duyệt qua từng phần tử trong danh sách Trong quá trình duyệt, tại mỗi nút ta có thể thực hiện các thao tác cần thiết như lấy thông tin phần tử, sửa thông tin, so sánh, v.v
r = p;
while (r-> next != null){
//thực hiện các thao tác cần thiết
r = r-> next;
Trang 13struct node *head = NULL;
struct node *current = NULL;
//hien thi danh sach
Trang 14//chen link tai vi tri dau tien
void insertFirst(int key, int data)
{
//tao mot link
struct node *link = (struct node*) malloc(sizeof(struct node));
Trang 15//xoa phan tu dau tien
struct node* deleteFirst()
{
//luu tham chieu toi first link
struct node *tempLink = head;
//danh dau next toi first link la first
Trang 16struct node *current;
for(current = head; current != NULL; current = current->next)
Trang 17//tim mot link voi key da cho
struct node* find(int key){
//bat dau tim tu first link
struct node* current = head;
//neu list la trong
Trang 18//xoa mot link voi key da cho
struct node* deleteKey(int key){
//bat dau tu first link
struct node* current = head;
struct node* previous = NULL;
Trang 19//neu list la trong
Trang 21void sort(){
int i, j, k, tempKey, tempData ;
struct node *current;
struct node *next;
int size = length();
Trang 22// ham dao nguoc list
void reverse(struct node** head_ref) {
struct node* prev = NULL;
struct node* current = *head_ref;
Trang 23struct node* next;
while (current != NULL) {
Trang 24insertFirst(4,1);
insertFirst(5,40);
insertFirst(6,56);
printf("Danh sach ban dau: ");
//in danh sach
printList();
while(!isEmpty()){
struct node *temp = deleteFirst();
printf("\nGia tri bi xoa:");
printf("(%d,%d) ",temp->key,temp->data); }
printf("\nDanh sach sau khi da xoa gia tri: "); printList();
Trang 25}else {
Trang 26printf("Khong tim thay phan tu.");
}else {
printf("Khong tim thay phan tu.");
}
Trang 271.2 Các thao tác trên hàng đợi
- Thao tác khởi tạo hàng đợi
Thao tác này thực hiện việc gán giá trị 0 cho biến head, giá trị MAX -1 cho biến tail, và giá
trị 0 cho biến count, cho biết hàng đợi đang ở trạng thái rỗng
void QueueInitialize(queue *q){
Trang 28- Thao tác kiểm tra hàng đợi rỗng
Hàng đợi rỗng nếu có số phần tử nhỏ hơn hoặc bằng 0 int QueueEmpty(queue q){
return (q.count <= 0);
}
- Thao tác thêm 1 phần tử vào hàng đợi
void Put(queue *q, int x){
Trang 29}
return;
}
Để thêm phần tử vào cuối hàng đợi, điểm cuối tăng lên 1 (nếu điểm cuối
đã ở vị trí cuối mảng thì quay vòng điểm cuối về 0) Trước khi thêm phần tử vào hàng đợi, cần kiếm tra xem hàng đợi đã đầy chưa (hàng đợi đầy khi giá trị biến count = MAX)
- Lấy phần tử ra khỏi hàng đợi
Để lấy phần tử ra khỏi hàng đợi, tiến hành lấy phần tử tại vị trí điểm đầu
và cho điểm đầu tăng lên 1 (nếu điểm đầu đã ở vị trí cuối mảng thì quay vòng điểm đầu về 0) Tuy nhiên, trước khi làm các thao tác này, ta phải kiểm tra xem hàng đợi có rỗng hay không
Trang 30return x;
}
- Cài đặt hàng đợi bằng danh sách liên kết
Để cài đặt hàng đợi bằng danh sách liên kết, ta cũng sử dụng 1 danh sách liên kết đơn và 2 con trỏ head và tail lưu giữ nút đầu và nút cuối của danh sách Việc bổ sung phần tử mới sẽ được tiến hành ở cuối danh sách và việc lấy phần tử ra sẽ được tiến hành ở đầu danh sách
Khai báo 1 hàng đợi bằng danh sách liên kết như sau:
Trang 31-Thao tác khởi tạo hàng đợi
Thao tác này thực hiện việc gán giá trị null cho nút đầu và cuối của hàngđợi, cho biết hàng
đợi đang ở trạng thái rỗng
void QueueInitialize(queue *q){
q-> head = q-> tail = NULL;
return;
}
Thao tác kiểm tra hàng đợi rỗng
Hàng đợi rỗng nếu nút đầu trỏ đến NULL
int QueueEmpty(queue q){
return (q.head == NULL);
}
Thao tác thêm 1 phần tử vào hàng đợi
void Put(queue *q, int x){
q-> tail = q-> tail-> next;
if (q-> head == NULL) q-> head = q-> tail;
return;
Trang 32Để thêm phần tử vào cuối hàng đợi, tạo và cấp phát bộ nhớ cho 1 nút mới Gán giá trị thích hợp cho nút này, sau đó cho con trỏ tiếp của nút cuối hàng đợi trỏ đến nó Nút này bây giờ trở thành nút cuối của hàng đợi Nếu hàng đợi chưa có phần tử nào thì nó cũng chính là nút đầu của hàng đợi
- Lấy phần tử ra khỏi hàng đợi
Để lấy phần tử ra khỏi hàng đợi, tiến hành lấy phần tử tại vị trí nút đầu
và cho nút đầu chuyển về nút kế tiếp Tuy nhiên, trước khi làm các thao tác này, ta phải kiểm tra xem hàng đợi có rỗng hay không
Trang 33- Thao tác khởi tạo ngăn xếp
Thao tác này thực hiện việc gán giá trị -1 cho biến top, cho biết ngăn xếpđang ở trạng thái rỗng
- Thao tác bổ sung 1 phần tử vào ngăn xếp
void Push(stack *s, int x){
Trang 34- Thao tác khởi tạo ngăn xếp
Thao tác này thực hiện việc gán giá trị null cho nút đầu ngăn xếp, cho biết ngăn xếp đang ở trạng thái rỗng
Trang 35-Thao tác bổ sung 1 phần tử vào ngăn xếp
void Push(stack *s, int x){
Trang 361.4 Duyệt cây nhị phân
Trang 37đủ làm không gian tìm kiếm của chúng ta, số chỉ có thể tồn tại trong không gian tìm kiếm Bây giờ chúng ta so sánh số lượng được tìm kiếm hoặc phần tử được tìm kiếm với phần tử ở giữa củakhông gian tìm kiếm hoặc trung vị và nếu bản ghi đang được tìm kiếm ít hơn, chúng tôi sẽ tìm kiếm ở nửa bên trái, còn lại chúng tôi sẽ tìm kiếm ở nửa bên phải, trong trường hợp bằng nhau,
chúng tôi đã tìm thấy phần tử Trong tìm kiếm nhị phân, chúng tôi
bắt đầu với phần tử 'n' trong không gian tìm kiếm và sau đó nếu
phần tử ở giữa không phải là phần tử mà chúng tôi đang tìm kiếm,
chúng tôi giảm không gian tìm kiếm thành 'n / 2'và chúng tôi tiếp
tục giảm không gian tìm kiếm cho đến khi chúng tôi tìm thấy bản ghi mà chúng tôi đang tìm kiếm hoặc chúng tôi chỉ nhận được một phần tử trong không gian tìm kiếm và được thực hiện với toàn bộ việc giảm bớt này
Thao tác tìm kiếm trong cây tìm kiếm nhị phân sẽ rất giống
Trang 38sẽ làm là chúng ta sẽ bắt đầu từ gốc và sau đó chúng ta sẽ so sánh giá trị được tìm kiếm với giá trị của gốc nếu nó bằng nhau, chúng
ta đã hoàn thành việc tìm kiếm nếu nó ít hơn chúng ta biết rằng chúng ta cần phải chuyển đến cây con bên trái vì trong cây tìm kiếm nhị phân, tất cả các phần tử trong cây con bên trái nhỏ hơn
và tất cả các phần tử trong cây con bên phải lớn hơn Tìm kiếm một phần tử trong cây tìm kiếm nhị phân về cơ bản là quá trình này, trong đó ở mỗi bước, chúng ta sẽ đi theo hướng sang trái hoặc phải và do đó ở mỗi bước, chúng ta loại bỏ một trong các cây con Nếu cây cân bằng, chúng ta gọi là cây cân bằng nếu đối với tất cả các nút, sự khác biệt giữa chiều cao của cây con bên trái
và bên phải không lớn hơn một, chúng ta sẽ bắt đầu với không
gian tìm kiếm làcác nút 'n' và khi chúng ta loại bỏ một trong các cây con, chúng ta sẽ loại bỏ các nút 'n / 2' để không gian tìm kiếm của chúng ta sẽ giảm xuống còn 'n / 2' và sau đó trong bước tiếp theo, chúng ta sẽ giảm không gian tìm kiếm xuống 'n / 4' và
chúng tôi sẽ tiếp tục giảm như vậy cho đến khi chúng tôi tìm thấy phần tử hoặc cho đến khi không gian tìm kiếm của chúng tôi giảmxuống chỉ còn một nút Tìm kiếm ở đây cũng là tìm kiếm nhị phân
và đó là lý do tại sao có tên là cây tìm kiếm nhị phân
• Chèn một nút vào BST
Cho BST, thao tác chèn phải có kết quả là BST
• Làm sao biết vị trí đặt nút 'D'?
Trang 41- Sắp xếp chọn
Đây là một trong những giải thuật sắp xếp đơn giản nhất Ý tưởng của giải thuật như sau: Lựa chọn phần tử có giá trị nhỏ nhất, đổi chỗ cho phần tử đầu tiên Tiếp theo, lựa chọn phần tử có giá trị nhỏ thứ nhì, đổi chỗ cho phần tử thứ 2 Quá trình tiếp tục cho tới khi toàn bộ dãy được sắp
Trang 42Như vậy, toàn bộ dãy đã được sắp
Giải thuật được gọi là sắp xếp chọn vì tại mỗi bước, một phần tử được chọn và đổi chỗ cho phần tử ở vị trí cần thiết trong dãy
Thủ tục thực hiện sắp xếp chọn trong C như sau:
Trang 43Trong thủ tục trên, vòng lặp đầu tiên duyệt từ đầu đến cuối dãy Tại mỗi
vị trí i, tiến hành duyệt tiếp từ i tới cuối dãy để chọn ra phần tử nhỏ thứ i
và đổi chỗ cho phần tử ở vị trí i Thời gian thực hiện thuật toán tỷ lệ với N2 , vì vòng lặp ngoài (biến chạy i) duyệt qua N phần tử, và vòng lặp trong duyệt trung bình N/2 phần tử Do đó, độ phức tạp trung bình của thuật toán là O(N * N/2) = O(N2 /2) = O(N2 )