Chương 4 cung cấp kiến thức về danh sách liên kết. Chương này gồm có những nội dung chính sau: Định nghĩa danh sách liên kết, danh sách liên kết đơn, danh sách liên kết kép. Mời các bạn cùng tham khảo để nắm bắt các nội dung chi tiết.
Trang 1CHƯƠNG IV:
DANH SÁCH
LIÊN KẾT
Danh sách liên kết
14
I Định nghĩa
! Danh sách liên kết là tập hợp các phần tử được liên kết móc nối liên tiếp với nhau, có kiểu truy cập tuần tự Mỗi một phần tử là một nút (Node)
! Số phần tử là biến động
! Nút gồm hai phần:
– Data - Dữ liệu : là các thành phần dữ liệu mà một nút đó lưu trữ
– Linked - Liên kết : là con trỏ kiểu nút đang định nghĩa được dùng để liên kết với các nút khác
! Ví dụ:
Danh sách liên kết
22
• Danh sách liên kết đơn : mỗi một phần tử của
danh sách liên kết duy nhất với một phần tử đứng
sau nó
• Danh sách liên kết kép : mỗi một phần tử của
danh sách liên kết với cả phần tử đứng trước và
đứng sau trong danh sách
*Phân loại danh sách liên kết
• Danh sách liên kết vòng : là danh sách mà phần
tử cuối liên kết với phần tử đầu của danh sách
Có hai loại danh sách liên kết vòng: vòng đơn và vòng kép
Danh sách liên kết
23
II Danh sách liên kết đơn
1 Khai báo CTDL
! Khai báo CTDL DSLK Đơn:
– Định nghĩa cấu trúc một nút
– Định nghĩa cấu trúc danh sách
! Cấu trúc mỗi nút gồm:
– Info: kiểu cấu trúc
– Next: kiểu con trỏ
! Cấu trúc danh sách LK đơn gồm:
– Head
– Tail
Info Next
(Dữ liệu) (liên kết)
Danh sách liên kết
24
! Cú pháp định nghĩa một nút:
struct < Node >
{ Data info ; //Khai báo dữ liệu - struct
struct Node * next ; //khai báo liên kết
} ;
! Trong đó:
– Data: là kiểu dữ liệu đã được khai báo để mô tả thành
phần dữ liệu chứa trong nút, là kiểu được định nghĩa sẵn
Danh sách liên kết
25
! Cú pháp định nghĩa danh sách:
struct LIST
{
Node Head; // Phần tử dầu danh sách
Node Tail;//Phần tử cuối danh sách
} ; LIST Q; //tạo danh sách có tên là Q
Danh sách liên kết
26
Ví dụ
! Cho thông tin của Sinh viên gồm: mã sinh
viên, Họ tên, Điểm trung bình
! Khai báo CTDL dạng danh sách liên kết
đơn để lưu được danh sách SV trên
MSV hoten dtb
next
MSV hoten dtb
next
struct SV
{
int MSV;
char hoten[25];
float dtb;
} ;
struct Node
{
SV info;
struct Node *next;
};
struct LIST
{ Node Head ; Node Tail ; };
2 Các thao các trên danh sách đơn
! Các thao tác cơ bản sau:
– Tạo danh sách
– Duyệt danh sách
– Chèn phần tử
– Hủy phần tử
– Tìm kiếm phần tử
– Sắp xếp danh sách
– …
Trang 2a Tạo danh sách:
! Để tạo danh sách, gồm các bước:
– Khởi tạo danh sách rỗng
– Tạo nút mới chứa thông tin nào đó
void init ( List &Q )
{
Q.Head = NULL;
Q.Tail = NULL;
}
Danh sách liên kết
30
Node* get_node( Data x )
{ Node *p;
p = new Node;
if ( p == NULL ) {
printf(“Ko du bo nho”);
exit(1);
}
p -> info = x;
p -> next = NULL;
return p;
}
=> Tạo phần tử mới: new_element = get_node(x);
Danh sách liên kết
31
b Chèn một phần tử mới vào danh sách:
! Các trường hợp sau – Chèn vào đầu danh sách
– Chèn vào cuối danh sách
– Chèn vào sau một phần tử q đã biết
Danh sách liên kết
32
Chèn thêm phần tử mới vào đầu danh sách:
new_element
Thuật toán
Nếu danh sách rỗng
Phần tử đầu là phần tử mới chèn vào
Phần tử cuối cũng là phần tử đầu
Ngược lại (danh sách khác rỗng)
Phần tử mới trỏ tới phần tử đầu
Phần tử đầu là phần tử mới được chèn vào
Danh sách liên kết
33
3 3f 4 4f 8 …
10 9f
P P->Next = Q.Head
2f
Q.Head = P
Danh sách liên kết
34
Chèn thêm phần tử mới vào cuối danh sách:
new_element
Thuật toán Nếu danh sách rỗng:
Phần tử đầu là phần tử mới chèn vào Phần tử cuối là phần tử đầu
Ngược lại (danh sách khác rỗng):
Phần tử cuối là phần tử mới được chèn vào
Danh sách liên kết
35
5
4 4f 8 5f
Q.Tai
l
6 N
9f
P
N 9f
Q.Tail->Next
Q.Tail=P
Chèn thêm phần tử mới sau phần tử q đã biết
Thuật toán Nếu không có phần tử q " không chèn được Nếu có tồn tại phần tử q
-Phần tử mới móc tới phần tử sau phần tử q -Phần tử q móc tới phần tử mới
-Nếu phần tử q là phần tử cuối thì phần tử mới chèn là phần tử cuối mới
5
4 4f 8
7
P
9f
q 5f
5f N
P->Next = q->Next q->Next = P
Trang 3B Tìm kiếm phần tử k trong danh sách
# Ý tưởng: Danh sách đơn chỉ cho truy xuất tuần
tự tới từng phần tử " Á p dụng thuật toán tìm
tuyến tính để xác định có phần tử k trong danh
sách hay ko?
# Để duyệt dùng một con trỏ p để duyệt qua các
nút của danh sách
56
X = 8
địa chỉ của nút tìm thấy là 4f
8
Danh sách liên kết
39
*Thuật toán
! Bước 1: p = Q.Head; //p trỏ từ đầu danh
sách
! Bước 2: kiểm tra danh sách còn phần tử và chưa
tìm thấy phần tử thì chuyển sang phần tử tiếp theo
Lặp trong khi (p!=NULL) và (p->info !=k) thì
p = p -> next;
! Bước 3:
– Nếu p != NULL:
! Tìm thấy
! p trỏ đến phần tử cần tìm
– Ngược lại: ko tìm thấy (trả lại NULL)
Danh sách liên kết
40
Cài đặt:
Node *Search( LIST Q, Data k )
{ Node *p;
p = Q.Head;
while ( p != NULL ) {
if ( p -> info ==k ) break;
p = p -> next;
} return p;
}
Danh sách liên kết
41
Cài đặt:
Node *Search( LIST Q, Data k )
{
Node *p;
p = Q.Head;
while (( p != NULL ) && (p -> info !=k ))
p = p -> next;
return p;
}
Danh sách liên kết
42
c Duyệt danh sách
! Là kỹ thuật để xét qua được hết các phần
tử của danh sách
! Thực hiện
– Khai báo một con trỏ p có kiểu Node – Cho con trỏ p duyệt từ đầu đến cuối để đi
qua từng phần tử của Danh sách
! Thuật toán
Danh sách liên kết
43
Cài đặt
void Duyet_DS ( LIST Q)
{ Node *p;
p = Q.Head;
while ( p != NULL ) {
<< thực hiện thao tác xử lý>>
p = p -> next;
} }
Danh sách liên kết
44
Ví dụ:
! Viết hàm in danh sách sinh viên ra màn
hình
! Viết hàm in họ tên của tsinh viên có điểm
trên 8
! Viết hàm cho biết Họ tên của Sv có ĐTB
cao nhất
d Hủy một phần tử khỏi danh sách
# Các trường hợp
• Hủy phần tử đầu
• Hủy phần tử đứng sau phần tử q xác định
• Hủy phần tử có giá trị xác định k
# Chú ý: khi tạo nút thì dùng hàm cấp phát bộ nhớ
=> Khi hủy thì phải dùng hàm để giải phóng bộ nhớ
Hủy một phần tử đầu danh sách:
Q.Tail Q.Head
Thuật toán:
Nếu danh sách rỗng (không có phần tử đầu) => không thực hiện xóa được
Nếu có phần tử đầu Lưu tạm thời phần tử đầu – lưu vào p
Chuyển phần tử đầu sang phần tử tiếp theo Xóa phần tử đầu đã được lưu tạm – xóa p
Kiểm tra: Nếu danh sách chỉ có một phần tử, khi xóa phần
tử đi thì phần tử cuối cùng không còn
Trang 4Thuật toán:
2f
P
P = Q.Head
Q.Head = Q.Head->Next
Danh sách liên kết
48
Hủy một phần tử đứng sau phần tử q:
# Thuật toán
# Nếu có phần tử q
$ Lưu phần tử đứng sau phần tử q – lưu vào p
$ Nếu có phần tử p (q không phải là phần tử cuôi)
$ Tách phần tử p ra khỏi danh sách
$ Nối phần tử q với phần tử sau p
$ Giải phóng phần tử p
Danh sách liên kết
49
2f
3f 4f
p
p = q->Next
q->Next = p->Next
Q.Head
Danh sách liên kết
50
Hủy một phần tử có khóa k
! Thuật toán:
! Bước 1:
Tìm phần tử p có khóa k và phần tử q đứng trước
! Bước 2:
Nếu (p!= NULL) thì // tìm thấy k
Hủy p ra khỏi xâu tương tự hủy phần tử sau q;
Ngược lại
Báo không có k;
Danh sách liên kết
51
* Cài đặt:
int RemoveNode( LIST &Q, Data k ) {
NODE *p = Q.Head, *q = NULL;
while( p != NULL) {
if (p->info == k) break;
q = p;
p = p->next;
}
if (p == NULL) return 0; //Không tìm thấy k
Danh sách liên kết
52
if (q != NULL) {
if(p == Q.Tail) Q.Tail = q;
q->next = p->next;
delete p;
} else //p là phần tử đầu xâu
{ Q.Head = p -> next;
if ( Q.Head == NULL) Q.Tail = NULL; }
return 1;
}
Danh sách liên kết
53
e Sắp xếp danh sách
! Để sắp xếp có hai phương án:
– Hoán vị nội dung của phần tử
– Thay đổi mối liên kết của phần tử
Hoán vị nội dung của phần tử:
# Là thực hiện thay đổi trực tiếp thành phần infor trong mỗi nút còn thứ tự liên kết của các nút là
không thay đổi
4f
4
3f
N
6 5f
7
4f
4
3f
N
7
5f
6
Cài đặt sắp xếp danh sách bằng cách thay đổi nội dung:
void ListSortInterchange( LIST &Q )
{ Node *p, *q; //p và q la hai bien dieu khien Data tg;
for(p = Q.Head;p != NULL; p = p->next) for(q = p -> next;q!=NULL; q = q->next )
if ( p->info> q->info )
}
Trang 5Ví dụ
! Viết hàm sắp xếp danh sách sinh viên theo
thứ tự ĐTB tăng dần
Danh sách liên kết
57
void ListSortInterchange( LIST &Q )
{ Node *p, *q; //p và q la hai bien dieu khien
Data tg;
p = Q.Head;
while ( p != NULL ) {
q = p -> next;
while( q != NULL ) {
if ( p->info.DTB > q->info.TDB )
{ tg = p->info; p->info = q->info; q->info = tg; }
q = q->next;
}
p = p->next;
} }
Danh sách liên kết
58
Thay đổi mối liên kết
! Là thực hiện thay đổi trực tiếp thành phần
các nút là thay đổi
! Ý tưởng: Tạo một danh sách mới là danh sách có thứ tự lần lượt lấy từ danh sách cũ (đồng thời huỷ danh sách cũ )
Danh sách liên kết
59
4f
4
3f
N
6 5f
7
6
Q.Head
Q.Tail 5f
3f
Q.Head
5f
4f Q.Tail
N
7 5f
Danh sách liên kết
60
! Thuật toán sắp xếp bằng thay đổi mối liên kết :
phần tử min là phần tử nhỏ nhất ;
Tail);
cũ Q (Head, Tail);
Danh sách liên kết
61
Bài tập thực hành
! Cho thông tin của cán bộ gồm: mã cán bộ, họ tên, ngày sinh, hệ số lương, phụ cấp và thành tiền (= hệ số lượng *
1050000 + phụ cấp)
với các yêu cầu:
– In lại ds cán bộ đã có
– Tìm kiếm cán bộ có mã là k
! Áp dụng các yêu cầu trên
Danh sách liên kết
62
Bài tập thực hành (y/c chi tiết)
! Khai Báo CTDL DSLK đơn chứa DS Cán bộ gồm: MCB, Hoten,
Tuoi, HSL, PC, TT(=HSL* 1.050.000 + PC)
! Định nghĩa các CTC sau:
! Áp dụng lần lượt các CTC trên dưới dạng menu lựa chọn:
– 0 Thoát
– 1 Tạo DSCB 2 In lại DSCB
– 3 Tìm kiếm CB theo mã nào đó 4 Đếm số CB lương từ 2 đến 3 triệu
– 5 Tính tổng lương của các cán bộ 6 …
Nhập một cán bộ In một cán bộ
Khởi tạo DSLK rỗng Tạo một nút chứa 1 cán bộ nào đó
Chèn thêm 1 cán bộ vào đầu DSLK đơn In DSCB
Tìm kiếm một CB có mã nào đó Tính tổng lương của các CB
Đếm số CB có lương từ 2 đến 3 triệu SX DSCB tăng dần theo tổng lương
Chèn thêm CB vào sau CB có mã là x
III Danh sách liên kết kép
1 Khai báo CTDL:
– Data : là một cấu trúc để mô tả thông tin được lưu trữ trong nút
– Linked : gồm hai con trỏ để chỉ liên kết
với phần tử trước và phần tử sau trong danh sách.
! Định nghĩa một nút trong danh sách struct DNode
{
Data infor; //Khai báo các thành phần dữ liệu
struct DNode *next; khai báo liên kết sau struct DNode *previous; khai báo liên kết trước
};
! Định nghĩa danh sách
struct DLIST
{
DNode *Head;
DNode *Tail;
};
DLIST DQ; //Tạo danh sách kép lấy tên là DQ
DNode *new_element;
Trang 6MSV
hoten
dtb
next
MSV hoten dtb next
MSV hoten dtb next
* Ví dụ:
struct SV
{
int MSV;
char hoten[25];
float dtb;
} ;
struct DNode
{
SV info;
struct SV *next;
struct SV *previous;
} ;
struct DList
{ DNode *Head; DNode *Tail; } ;
DList DQ;
Danh sách liên kết
66
2 Các thao các trên danh sách kép
# Đối với danh sách kép có các thao tác cơ bản sau:
• Tạo danh sách
• Chèn một phần tử
• Hủy một phần tử
• Tìm kiếm phần tử
• Duyệt danh sách
• Sắp xếp danh sách theo thành phần dữ liệu
Danh sách liên kết
67
a Tạo danh sách
! Gồm:
– Khởi tạo danh sách rỗng – Tạo nút chứa thông tin cần lưu trữ
Danh sách liên kết
68
Thủ tục tạo một nút mới
DNode *Get_Node( Data x )
{
DNode *p;
p = new DNode();
if (p == NULL)
{
printf(”Ko du bo nho”);
exit(1);
}
p -> infor = x;
p -> next = NULL;
p -> previous = NULL;
return p;
}
Danh sách liên kết
70
Chèn thêm phần tử vào danh sách:
! Các trường hợp:
– Chèn mới vào đầu danh sách
– Chèn mới vào cuối danh sách
– Chèn mới vào sau một phần tử q đã biết
– Chèn mới vào trước một phần tử q đã biết
Danh sách liên kết
Chèn thêm vào trước phần tử q:
# Nếu phần tử q khác rỗng " có tồn tại phần tử q
p = q -> previous //Tạo phần tử p đứng trước q
new_element -> next = q
new_element -> previous = p
q -> previous = new_element
Cài đặt:
void AddBefore(DLIST &DQ, DNode *q,
DNode *new_element)
{ DNode *p = q -> previous;
if ( q != NULL ) {
new_element -> next = q;
new_element -> previous = p;
q -> previous = new_element;
if (p != NULL) p -> next = new_element;
if (q == DQ.Head) DQ.Head = new_element;
} }
Hủy một phần tử khỏi danh sách
# Có các trường hợp:
• Hủy phần tử đầu
• Hủy phần tử cuối
• Hủy phần tử đứng sau phần tử q
• Hủy phần tử đứng trước phần tử q
• Hủy một phần tử có khóa là k
Trang 7DQ.Tail
DQ.Head
DQ.Tail
DQ.Head
DQ.Tail
Danh sách liên kết
82
Hủy một phần tử đứng trước phần tử q:
DQ.Tail
DQ.Head
Danh sách liên kết
89
Cài đặt xóa phần tử đứng trước phần tử q
void RemoveBefore (DLIST &DQ, Dnode *q)
{ Dnode *p;
if ( q = = NULL ) MoveLast(DQ);
else //Co phan tu q
{
if (q = = DQ.Head) //Phần tử q là phần tử đầu
{ printf("\n Ko xoa duoc do dang o vi tri dau"); exit(1);
}
p = q -> previous; //lay phan tu can huy bo
q -> previous = p -> previous;
if ( p = = DQ.Head ) DQ.Head = q;
else p -> previous -> nex t = q;
free(p);
} }
Danh sách liên kết
90
*Tìm kiếm phần tử có khóa xác định:
tử trước và sau nên, thực hiện tìm
kiếm bằng cách:
– xuất phát từ đầu danh sách
– xuất phát từ cuối danh sách
Danh sách liên kết
e Sắp xếp các phần tử của danh sách:
void Sort_Inter(D_LIST &DQ)
{ Dnode *p,*q;
p = DQ.Head;
q = p -> next;
while (p!=NULL) {
q = p -> next;
while ( q != NULL) {
if ( p -> info > q -> info )
Hoandoi( p->info , q->info );
q = q -> next;
}
p = p -> next;
} }
Danh sách liên kết
95
Bài tập áp dụng
! Cho thông tin của cán bộ gồm: mã cán bộ, họ tên, ngày
sinh, hệ số lương, phụ cấp và thành tiền (=hệ số lượng *
1050000 + phụ cấp)
! Thực hiện các yêu cầu sau:
– Áp dụng mảng Định nghĩa các thao tác sau:
! In lại toàn bộ danh sách cán bộ
! SX DSCB tăng dần theo tổng lương
– Viết chương trình thực hiện các chức năng trên
Danh sách liên kết
96
Bài tập thực hành (y/c chi tiết)
! Khai Báo CTDL DSLK chứa Cán bộ gồm: MCB, Hoten, Tuoi, HSL,
PC, TT(=HSL* 1.050.000 + PC)
! Định nghĩa các CTC sau:
! Áp dụng lần lượt các CTC trên dưới dạng menu lựa chọn:
– 0 Thoát
– 1 Tạo DSCB 2 In lại DSCB
– 3 Tìm kiếm CB theo mã nào đó 4 Đếm số CB lương từ 2 đến 3 triệu
– 5 Tính tổng lương của các cán bộ 6 …
Nhập một cán bộ Tính tổng lương của các CB
In một cán bộ SX DSCB tăng dần theo tổng lương
Nhập 1 DSCB In DS CB có tuổi dưới 20
In DSCB Cho biết Họ tên của CB có lương cao nhất
Tìm kiếm một CB có mã nào đó Chèn thêm một cán bộ cvaof sau cán bộ có mã là x nào đó
Đếm số CB có lương từ 2 đến 3 triệu
*Bài tập áp dụng
! Thông tin về nhân viên gồm: mã số nhân viên, họ tên, ngày sinh, tổng lương
! Thực hiện các yêu cầu sau:
– a Khai báo CTDL của DSLK đôi để quản lý DSNV
– Thực hiện các thao tác trên danh sách
! b Thêm một nhân viên vào đầu danh sách liên kết kép trên
! c In lại toàn bộ danh sách nhân viên ra màn hình
! d Tính tổng lương của tất cả các nhân viên trong DS
! e Hủy một nhân viên khỏi đầu danh sách
! f In danh sách nhân viên có mức lương cao nhất
! g Sắp xếp DSNV theo tứ tự giảm dần của lương
98
Cài đặt chương trình
! Cho thông tin của Nhân viên gồm: mã nhân viên, họ tên, tuổi, HSL, PC (Luong = (HSL+PC)*1050000)
! Cài đặt CT bằng ngôn ngữ C để quản lý DSNV dưới dạng DSLK Đơn với các yêu cầu sau:
– Tạo danh sách
– Chèn thêm vào danh sách một số phần tử (đầu và cuối)
– Hiển thị lại toàn bộ danh sách
– Hiển thị ds cán bộ có lương trên 2 triệu
– Tìm kiếm một nhân viên theo mã nào đó
– Chèn thêm một nhân viên vào sau nhân viên q nào đó
– Tính tổng lương của các nhân viên
– Sắp xếp danh sách NV theo lương giảm dần
– …
! Áp dụng chương trình bằng các menu lựa chọn 99
Trang 8Bài tập áp dụng
! Cài đặt chương trình BTL dưới dạng DSLK với một số yêu cầu cơ
bản:
– Tạo danh sách
– Hiển thị toàn bộ danh sách
– Chèn đầu
– Xóa đầu
– Tìm kiếm
– Sắp xếp
! Nộp bài
– Hạn: Muộn nhất 20h -ngày 30/1/2013
– Gửi mail: trinhxuan@gmail.com
– subject: CTDL - CT6 - Họ tên sinh viên - Lớp
– Đính kèm file
! File đặt theo nguyên tắc: Tên sinh viên_Lớp.CPP
Danh sách liên kết
10
0
Bài tập về nhà – 6/10/2014
! Mỗi nhóm chọn ra một đối tượng cần quản lý của bài toán
! Cài đặt quản lý đối tượng đó bằng mảng, gồm tối thiểu:
– Nhập & in DS - Tìm kiếm – Tính tổng & tính tổng có điều kiện - Sắp xếp – Đếm & đếm có điều kiện - Phần tử lớn nhất và nhỏ nhất
=> Áp dụng các yêu cầu trên
! Yêu cầu nộp bài:
– Mỗi nhóm nộp 1 bài, trong đầu chương trình ghi rõ thông tin nhóm và bài toán của mình
– Gửi mail: trinhxuan@gmail.com trước 17h – ngày 12/10/2014
– Subject: CTDL – BTL – Mảng - <Thứ tự nhóm>
– Tên file: CTDL_<Thứ tự nhóm> Danh sách liên kết
10
1
Bài tập về nhà buổi 4 Đọc DS LK đơn
! Khai báo CTDL
! Khởi tạo danh sách
! Thêm phần tử
! Xóa
! Duyệt
! Sắp xếp
Mỗi phần xác định:
! Ý tưởng
! Các bước
! Minh họa
! Cài đặt
Danh sách liên kết
10
2
Bài tập về nhà buổi 5
Đọc DS LK kép
! Khai báo CTDL
! Khởi tạo danh sách
! Thêm phần tử
! Xóa
! Duyệt
! Sắp xếp
Mỗi phần xác định:
! Ý tưởng
! Các bước
! Minh họa
! Cài đặt
Danh sách liên kết
10
3
Bài tập về nhà buổi 6 Đọc Stack - Queue
– Định nghĩa – Cài đặt – Ứng dụng
! Queue
– Định nghĩa
– Cài đặt
– Ứng dụng
Danh sách liên kết
10
4