Professional Documents
Culture Documents
BG Cau Truc Du Lieu Va Giai Thuat - Tiến Sĩ Ngô Hữu Phúc
BG Cau Truc Du Lieu Va Giai Thuat - Tiến Sĩ Ngô Hữu Phúc
BG Cau Truc Du Lieu Va Giai Thuat - Tiến Sĩ Ngô Hữu Phúc
Tham khảo:
Elliz Horowitz - Fundamentals of data structures,
Chapter 1: Introduction
đó trả lời câu hỏi “làm như thế nào?” → đó là cách tiếp cận
đến giải thuật và cấu trúc dữ liệu.
Các bài toán trong thực tế không dễ giải bằng cách hiểu
Yêu cầu:
Hai nước láng giềng (cùng biên giới) thì phải được tô bằng
ít nhất.
Hai nước láng giềng của nhau thì hai đỉnh ứng với nó được nối với
Bài toán lúc này trở thành bài toán tô màu cho đồ thị như sau:
Mỗi đỉnh đều phải được tô màu.
Hai đỉnh có cạnh nối thì phải tô bằng hai màu khác nhau.
Cần tìm một phương án tô màu sao cho số màu được sử dụng là ít
nhất.
Hãy thiết kế một bảng đèn hiệu điều khiển giao thông tại ngã năm này
Phân chia các lối đi tại ngã năm này thành các nhóm
Mỗi nhóm gồm các lối đi có thể cùng đi đồng thời nhưng không xảy ra tai nạn
Mỗi nhóm sẽ tương ứng với một pha điều khiển của đèn
hiệu, vì vậy ta phải tìm kiếm lời giải với số nhóm là ít nhất
để giao thông không bị tắc nghẽn vì phải chờ đợi quá lâu.
BA, BC, BD, DA, DB, DC, EA, EB, EC, ED.
Hai lối đi nào nếu đi đồng thời sẽ xảy ra đụng nhau (tức là hai hướng đi cắt
qua nhau) ta nối lại bằng một đoạn thẳng.
Ta sẽ có một sơ đồ như hình 2. Như vậy, trên sơ đồ này, hai lối đi có cạnh nối
lại với nhau là hai lối đi không thể cho đi đồng thời.
hình hoá bài toán giao thông ở trên theo mô hình toán là đồ thị.
Mỗi lối đi trở thành một đỉnh của đồ thị, hai lối đi không thể cùng đi đồng
thời được nối nhau bằng một đoạn ta gọi là cạnh của đồ thị.
Bây giờ ta phải xác định các nhóm, với số nhóm ít nhất, mỗi nhóm gồm
các lối đi có thể đi đồng thời, nó ứng với một pha của đèn hiệu điều
khiển giao thông.
Giả sử rằng, ta dùng màu để tô lên các đỉnh của đồ thị này sao cho:
Các lối đi cho phép cùng đi đồng thời sẽ có cùng một màu: Dễ dàng nhận
thấy rằng hai đỉnh có cạnh nối nhau sẽ không được tô cùng màu.
Số nhóm là ít nhất: ta phải tính toán sao cho số màu được dùng là ít nhất.
Hai đỉnh có cạnh nối với nhau (hai còn gọi là hai đỉnh kề nhau) không
cùng màu.
Nhận xét:
Hai bài toán thực tế “tô màu bản đồ thế giới” và “đèn giao
thông” xem ra rất khác biệt nhau nhưng sau khi mô hình
hóa, chúng thực chất chỉ là một, đó là bài toán “tô màu đồ
thị”.
Cách này được xem là "Cố gắng tô màu cho đồ thị bằng ít
màu nhất một cách nhanh chóng".
Một giải pháp như thế gọi là một HEURISTIC.
Ý tưởng của Heuristic này là hết sức đơn giản: dùng một
màu để tô cho nhiều đỉnh nhất có thể được (các đỉnh được
xét theo một thứ tự nào đó), khi không thể tô được nữa với
màu đang dùng thì dùng một màu khác. Như vậy ta có thể
"hi vọng" là số màu cần dùng sẽ ít nhất
14 @Copyrights by Dr. Ngo Huu Phuc, Le Quy Don Technical University
Ví dụ về HEURISTIC
Tô theo GREEDY
(xét lần lượt theo số thứ tự các đỉnh)
1: đỏ; 2: đỏ; 3: xanh;4: xanh; 5: vàng
Tối ưu
(thử tất cả các khả năng)
1,3,4 : đỏ; 2,5 : xanh
Chú ý: 4 màu là 1 lời giải, nhưng chưa kết luận được có tối
ưu hay không.
Giải thuật không thay đổi khi viết trên các ngôn ngữ khác nhau.
Giải thuật phải giải quyết được các vấn đề tổng quát cũng như
các vấn đề riêng của một bài toán.
...,'Z',”AA”...}.
liệu d.
Ví dụ: mảng được định nghĩa các ô nhớ liên tục trong bộ
nhớ…
Liệt kê ra các bước cần phải thực hiện bằng ngôn ngữ tự
nhiên.
Nút chỉ các điều kiện có dạng hình thoi. Trong các đường nối với nút
điều kiện này có hai đường đi ra ứng với hai trường hợp điều kiện đúng
hoặc điều kiện sai.
Khối hình bình hành để in các giá trị.
Các đường nối các nút với nhau.
Cấu trúc tuần tự: Liệt kê các công việc, các thao tác theo thứ tự. Để hỗ
trợ cho việc theo dõi được thuận tiện cũng có thể thêm số thứ tự.
Khi đề xuất một thuật toán ngoài việc quan tâm đến tính đúng
đắn của thuật toán, người ta còn phải quan tâm đến một số
vấn đề:
Độ phức tạp thuật toán - thời gian tính toán của thuật toán
Trước khi nghiên cứu độ phức tạp thuật toán, ta xem xét một
số khái niệm về cách xác định độ phức tạp cho thuật toán.
Khái niệm về độ tăng của hàm: Một trong các khái niệm
sao cho
O(max{g1(x),g2(x)})
38 @Copyrights by Dr. Ngo Huu Phuc, Le Quy Don Technical University
1.4. Độ phức tạp của thuật toán (tiếp)
Độ phức tạp của thuật toán
Các thuật ngữ O(1)-độ phức tạp hằng số; O(n) độ phức tạp
tuyến tính; O(nb) – độ phức tạp hàm mũ.
Độ phức tạp tính toán sẽ được tính theo số lần thực hiện
các phép toán sơ cấp:cộng, trừ, nhân, chia, phép gán,..
Mảng có thể coi là cấu trúc dữ liệu cho phép truy cập
Như vậy, một mảng được hình thành bởi một cặp (value,
index);
chỉ số có dạng {i1, i2, i3,….., in}, mảng được gọi là mảng n
chiều.
• Như vậy, nếu phần tử thứ i ánh xạ tới vị trí a thì phần tử
thứ (i+1) ánh xạ tới vị trí (a+1).
representation.
display(a,5); {
inverse(a,5); int i,temp;
display(a,5); for(i=0;i<(n/2);i++)
getch(); } {
void read(int c[],int n) { temp=inver_a[i];
int i;
inver_a[i]=inver_a[n-1-i];
printf("Nhap cac phan tu cho mang \n");
inver_a[n-1-i]=temp;
for(i=0;i<n;i++) scanf("%d",&c[i]);
}
fflush(stdin); }
}
16 @Copyright Dr. Ngo Huu Phuc, Le Quy Don Technical University
2.4. Một số ví dụ về mảng (5/14)
Ví dụ 5: Nối 2 mảng đã sắp xếp (1/3) sort(a,5);
#include <stdio.h> printf("Mang thu nhat duoc sap:\n");
#include <conio.h> display(a,5);
void read(int *,int);
sort(b,5);
void display(int *,int);
printf("Mang thu hai duoc sap:\n");
void sort(int *,int);
display(b,5);
void merge(int *,int *,int *,int,int);
merge(a,b,c,5,5); // noi hai mang
void main() {
int a[5],b[5],c[10]; printf("Cac phan tu trong mang moi, sau khi noi
\n");
printf("Nhap cac phan tu cua mang 1th \n");
display(c,10);
read(a,5);
getch();
printf("Cac phan tu da nhap cua mang 1th \n");
display(a,5); }
printf("Nhap cac phan tu cua mang 2rd \n");
read(b,5);
printf("Cac phan tu da nhap cua mang 2rd \n");
display(b,5);
{ if(arr[j]>arr[j+1])
int i; {
for(i=0;i<n;i++) temp=arr[j];
printf("%d ",d[i]); arr[j]=arr[j+1];
printf("\n"); arr[j+1]=temp;
}
}
}
}
}
18 @Copyright Dr. Ngo Huu Phuc, Le Quy Don Technical University
2.4. Một số ví dụ về mảng (10/14)
Ví dụ 5: Nối 2 mảng đã sắp xếp(3/3) while(ptra<k1)
void merge(int a[],int b[],int c[],int k1, int k2) {
{ c[ptrc]=a[ptra];
int ptra=0,ptrb=0,ptrc=0;
ptra++;ptrc++;
while(ptra<k1 && ptrb<k2)
}
{
while(ptrb<k2)
if(a[ptra] < b[ptrb])
{
{
c[ptrc]=a[ptra]; c[ptrc]=b[ptrb];
ptra++; ptrb++;
} ptrc++;
else }
{ }
c[ptrc]=b[ptrb];
ptrb++;
}
ptrc++;
}
int a[ROW][COL]; }
read(a,ROW,COL); void display(int d[ROW][COL],int row,int column) {
printf("\nMa tran da nhap \n"); int i,j;
display(a,ROW,COL); for(i=0;i<row;i++)
trans(a,ROW,COL); {
printf("Ma tran sau khi da chuyen vi\n");
for(j=0;j<column;j++) printf("%d ",d[i][j]);
display(a,ROW,COL);
printf("\n");
getch();
}
}
}
23 @Copyright Dr. Ngo Huu Phuc, Le Quy Don Technical University
2.4. Một số ví dụ về mảng (12/14)
Ví dụ 9: Chuyển vị ma trận (2/2)
void trans(int mat[][COL],int row ,int column)
{
int i,j,temp;
for(i=0;i<row;i++)
for(j=i+1;j<column;j++)
{
temp=mat[i][j];
mat[i][j]=mat[j][i];
mat[j][i]=temp;
}
}
}
}
26/26 @Copyright Dr. Ngo Huu Phuc, Le Quy Don Technical University
Cấu trúc dữ liệu và giải thuật
Bài 3. Đệ quy (Recursion)
Tham khảo:
1. Kyle Loudon – Mastering Algorithms with C
Chapter 3 – Recursion
2. Hanoi Tower (Web page)
2 @Copyright by PhD. Ngo Huu Phuc, Le Quy Don Technical University
3.1. Khái niệm về đệ quy (1/6)
Với phần lập trình viên, để giải quyết bài toán lớn, có
nó.
Với mỗi bước, hàm thay đổi thông tin đầu vào và cho kết
quả ngày càng gần với mục tiêu của bài toán.
• Giai thừa của n được viết: n!, tích các phần tử từ n đến 1.
• Ví dụ, 5! = (5)(4)(3)(2)(1).
• Có thể thực hiện tính giai thừa bằng vòng lặp thông thường
để tính tích.
• Một cách tiếp cận khác, sử dụng đệ quy, khi đó công thức
được thực hiện, các bài toán nhỏ trên được xem
xét theo thứ tự ngược lại.
Bước này dừng lại khi quá trình đến bài toán gốc. Quá
Character
Character
Digit
Tính chất trên không quá quan trọng vì phần lớn các bộ xử
lý hiện nay thực hiện được điều này tự động.
tài liệu về thuật toán. Bên cạnh đó, rất nhiều web
sites cũng đề cập tới bài toán này.
Để trả lời câu hỏi trên, cần đưa ra cách đệ quy tối ưu
39/39 @Copyright by PhD. Ngo Huu Phuc, Le Quy Don Technical University
Cấu trúc dữ liệu và giải thuật
Bài 4: Kỹ thuật quay lui (Backtracking)
Tham khảo:
1. Lecture 11 – Backtracking.htm
vector:
trong đó, mỗi phần tử a[i] chọn từ tập hữu hạn S[i]
(các khả năng của a[i]).
Nếu không, xóa phần tử a[k] và làm lại bài toán từ tập
S[k].
4 @Copyright PhD. Ngo Huu Phuc, Le Quy Don Technical University
4.1. Khái niệm kỹ thuật quay lui (3/6)
Gọi S[1], tập các khả năng của a tại bước đầu tiên.
k=1
While k > 0 do
While S[k]<>[] do (*advance*)
a[k] = an element in S[k]
If (a[1], a[2],…, a[k]) is solution, print it!
k=k+1
Tính S[k], tập khả năng của a tại bước k.
k = k - 1 (*backtrack*)
void N_Queens(int a[][N], int n, int row); // Lay cac cot da danh dau trong hang row
void main() { int getMarkedCol(int a[][N], int n, int row) {
int a[N][N] ; int j;
int i=0, j=0; for(j=0; j<n; j++)
for(i=0; i<N; i++) if(a[row][j] == TRUE) return j;
for(j=0; j<N; j++) a[i][j]=0;
printf("Loi: Khong co cot danh dau trong hang
N_Queens(a,N,0); %d.\n", row);
getch(); return -1;
}
}
int colchange[] = {-1, -2, -2,-1, 1, 2, 2, 1}; void printVariant(int a[][M], int n) {
void main() { // in ket qua
int a[M][M] ; int i, j;
int i=0, j=0; for(i=0; i<n; ++i) {
int m,n; for(j=0; j<n; j++)
for(i=0; i<M; i++)
printf("%2d ", a[i][j]);
for(j=0; j<M; j++)
printf("\n");
{
}
}
19 @Copyright Dr. Ngo Huu Phuc, Le Quy Don Technical University
4.3. Knight Tour Problem (5/5)
void knight(int a[][M], int n, int row, int col, int num) printf("\nNhan Enter de chon ket qua khac, nhan
{ ESC de thoat!\n");
dụng tỷ số này?
1 3 7 7/3
2 6 16 8/3
3 7 19 19/7
4 5 15 3
29/29 @Copyright Dr. Ngo Huu Phuc, Le Quy Don Technical University
Cấu trúc dữ liệu và giải thuật
Bài 5. Phương pháp sắp xếp đơn giản
Nội dung:
6.1. Khái niệm và vai trò của sắp xếp (13)
6.2. Sắp xếp chèn (6)
6.3. Sắp xếp chọn (4)
6.4. Sắp xếp nổi bọt (4)
Tham khảo:
1. Lecture 16 Introduction to Sorting.htm
2. Data Structures and Algorithms Sorting.htm
3. Tham khảo bài giảng của TS Nguyễn Nam Hồng
5.1.6. Phân tích hiệu quả của giải thuật sắp xếp.
Đưa một dãy các đối tượng về dạng thứ bậc nào đó.
So sánh.
Với kết quả của quá trình sắp xếp, một số vấn đề
Tính ổn định:
Lấy phần tử đầu tiên của vùng chưa sắp và chèn vào vùng đã sắp.
Thực hiện tương tư cho đến khi vùng chưa sắp không còn phần tử
nào.
16 Ngo Huu Phuc, Le Quy Don Technical University
5.2. Sắp xếp chèn (3/6)
Giả sử: Sắp xếp dãy
các số nguyên theo
Chuỗi đã sắp Phần tử sẽ
được chèn
phương pháp chèn.
8 5 2 6 9 4 6
Giá trị 5 nhỏ hơn 8, 5 sẽ thay thế cho 8, dãy đã sắp sẽ có kích thước
tăng lên 1 (có 2 phần tử đã được sắp.
getch(); } }
6 4 2 9 3
Thuật toán thích hợp với sắp xếp dãy, với mỗi phần tử
trong dãy có kích thước lớn, vì đòi hỏi ít phép tráo đổi.
Với dữ liệu đã gần được sắp, thuật toán không cho
Thuật toán dừng khi không còn cặp nào sai vị trí.
3 2 2 2 2 2 2
2 3 3 3 3 3 1
4 4 4 1 1 1 3
1 1 1 4 4 4 4
5 5 5 5 5 5 5
2 1 1
1 2 2
3 3 3
4 4 4
5 5 5
Các giải thuật này được thực hiện với dữ liệu cho phép truy
Trong bài 6, giới thiệu một số giải thuật sắp xếp cho hiệu
Tham khảo:
1. Intro to Algorithms Chapter 8 QuickSort.htm
2. Lecture 5 – quicksort.htm
3. Quick Sort.htm
4. Bài giảng của TS Nguyễn Nam Hồng
2 Ngo Huu Phuc, Le Quy Don Technical University
6.1. Thuật toán QuickSort (1/6)
Giải thuật Quick-sort là
phương pháp sắp xếp dựa
trên chiến lược chia để trị. x
Giải thuật gồm các bước:
Phép chia: chọn ngẫu nhiên
một phần tử x làm khóa,
chia tập dữ liệu S ban đầu
thành 3 phần: x
L chứa các phần tử nhỏ hơn x
E chứa các phần tử bằng x L E G
G chứa các phần tử lớn hơn x
Bước lặp: sắp xếp 2 tập L
và G
Hiệu chỉnh lại các tập L, E x
và G
3 Ngo Huu Phuc, Le Quy Don Technical University
6.1. Thuật toán QuickSort (2/6)
Các bước cơ bản của thuật toán:
sao cho, tất cả các phần tử bên trái nhỏ hơn tất
Sắp xếp 2 tập con một cách độc lập và nối chúng
4 2 → 2 4 7 9 → 7 9
2→2 9→9
Chỉ số i tăng đến khi tại vị trí i đó, phần tử này có giá trị lớn hơn
khóa. Chỉ số j giảm đến khi giá trị tại vị trí j nhỏ hơn khóa.
So sánh giữa i và j, nếu i<j, đổi chỗ 2 phần tử này cho nhau.
Nếu không, đổi chỗ khóa (phần tử đầu tiên) với phần tử j.
Khi đó, có thể chia mảng đã cho thành 2 mảng con, mảng thứ
nhất từ vị trí 1 đến vị trí j-1, mảng thứ 2 từ vị trí j+1 đến n. Sau
đó có thể lặp lại quá trình trên cho các mảng con.
Tiếp theo, mỗi tập con lại được chia thành 2 tập con có
kích thước gần bằng n/4, thực hiện điều này đến khi kích
thước mỗi mảng bằng 1.
Mỗi lần, thời gian thực hiện cho quá trình chia
Tham khảo:
1. Bucket sort.htm
2. Merge Sort.htm
3. Radix sort.htm
4. ShellSort.htm
5. Bài giảng của TS Nguyên Nam Hồng
STT 0 1 2 3 4 5 6 7 8 9 10 11 12
Ban
81 94 11 96 12 35 17 95 28 58 41 75 15
đầu
KC
35 17 11 28 12 41 75 15 96 58 81 94 95
=5
KC
28 12 11 35 15 41 58 17 94 75 81 96 95
=3
KC
11 12 15 17 28 35 41 58 75 81 94 95 96
=1
ht = N/2, hk = hk+1/2 …
O(N2)
~ O(N)
bình: ~ O(N7/6)
Ý tưởng:
chưa?
Kiểm tra từng phần tử S[i] trong danh sách S và đưa vào
thùng thứ i.
Như vậy, ta có m thùng với các giá trị đã được đưa vào theo
đúng trật tự.
Sau đó, sắp lại các phần tử theo từng thùng, dựa trên chỉ số
của thùng.
Phương pháp này không cần phép toán so sánh.
2
0 1 2
0 1 2 3 4
0 2 3 4
0 0 0 1 1 2 2 2 2 3 3 4 4
21 Ngo Huu Phuc, Le Quy Don Technical University
7.3. Bucket sort (3/6)
#include <stdio.h> printlist(list,n);
#include <conio.h> getch();
#define MAX 100 }
int inputdata(int* list,int n);
int inputdata(int* list,int n)
void printlist(int* list,int n);
{
void bucketsort(int *list, int n, int m);
int i, m;
void main() {
int temp;
int list[MAX], n;
// So thung printf("Nhap cac phan tu duong cho mang\n");
int m; do {
printf("Nhap so phan tu cho mang, toi da = scanf("%d",&temp);
100\n");
} while (temp<0);
scanf("%d",&n);
m = temp;
m = inputdata(list,n);
list[0] = temp;
printf("Mang da nhap:\n");
for(i=1;i<n;i++)
printlist(list,n);
bucketsort(list,n,m); {
{ bucket[list[i]]++;
int i; i = 0;
printf("Cac phan tu cua mang: \n"); for(j=0; j<=m; j++)
for(i=0;i<n;i++) for(k=0; k<bucket[j]; k++)
printf("%d\t",list[i]); {
printf("\n");
list[i] = j;
}
i++;
}
}
23 Ngo Huu Phuc, Le Quy Don Technical University
7.3. Bucket sort (5/6)
Đánh giá độ phức tạp của phương pháp:
Để khởi tạo m thùng, thời gian cần thiết: O(m)
Để đưa các phần tử từ danh sách vào thùng, thời gian
cần thiết: O(n)
Để đưa các phần tử từ các thùng vào danh sách cần:O(n)
Lưu ý: mặc dù, trong đoạn này vẫn có 2 vòng lặp lồng nhau, tuy
nhiên, số phần tử được xét vẫn chỉ là n phần tử.
Nếu như m nhỏ hơn n (thông thường), độ phức tạp của
Bucket Sort là O(n).
Tổng quát hóa, độ phức tạp của phương pháp là: O(n+m)
12 58 37 64 52 36 99 63 18 9 20 88 47
58
12 37 18 9
20 52 63 64 36 47 88 99
20 12 52 63 64 36 37 47 58 18 88 9 99
27 Ngo Huu Phuc, Le Quy Don Technical University
7.4. Radix sort (3/6)
Example: Second pass
20 12 52 63 64 36 37 47 58 18 88 9 99
12 36 52 63
9 18 20 37 47 58 64 88 99
9 12 18 20 36 37 47 52 58 63 64 88 99
28 Ngo Huu Phuc, Le Quy Don Technical University
7.4. Radix sort (4/6)
Nếu cố định p lần dùng Bucket Sort ( p = 6 nếu giá trị
lớn nhất không quá 999 999), độ phức tạp của Radix
Sort là O(n)
Với mỗi lần dùng Bucket Sort, độ phức tạp là O(n).
52 và 58
Số 52 xuất hiện trước số 58 trong danh sách, số 52 sẽ có
trong danh sách kết quả trước số 58.
Tham khảo:
1. Data structures and Algorithms Searching.htm
2. Kyle Loudon Mastering Algorithms, Chapter 12 Sorting and Searching
3. Lecture 19 Sequential and Binary Search.htm
4. Sedgewick Algorithms, Elementary Searching Methods
5. Bài giảng của TS Nguyễn Nam Hồng
giá trị cần tìm ( theo khóa nào đó ). Quá trình sẽ dừng
nếu tìm thấy đối tượng cần tìm hoặc đã đi hết danh
sách.
17 23 5 11 2 29 3
Ví dụ: tìm vị trí phần tử có giá trị bằng 11, việc tìm kiếm
bắt đầu từ phần tử có giá trị 17, đến 23, đến 5, đến 11:
đưa ra thông báo đã tìm thấy, trả về vị trí thứ 4 trong
danh sách.
Ví dụ: tìm vị trí phần tử có giá trị bằng 7, việc tìm kiếm bắt
đầu từ phần tử có giá trị 17, qua cả danh sách, nhưng
không tìm thấy, trả về thông tin: không tìm thấy.
2 3 5 11 17 23 29
Muốn tìm khóa 7, tìm kiếm nhị phân xét các giá trị
lần lượt là 11,3,5, và dừng vị không tìm thấy.
Ưu điểm:
sánh.
Nhược điểm:
Dãy đã cho phải được sắp xếp theo một khóa nào đó.
Trong thực tế, nếu biết thêm thông tin rằng các phần
Nếu quá trình tìm kiếm chưa kết thúc, tiếp tục tìm
Tìm kiếm nội suy hiệu quả với danh sách có số phần tử
lớn.
Tìm kiếm nội suy vẫn được dùng trong tìm kiếm dữ liệu
được lưu trữ trên ổ cứng hoặc các thiết bị truy cập chậm
24
khác.
PhD. Ngo Huu Phuc, Le Quy Don Technical University
Cấu trúc dữ liệu và giải thuật
Bài 9: Ngăn xếp - Stacks
Push
Đưa một phần tử vào đỉnh
của stack.
Pop
Lấy từ đỉnh của stack một
phần tử.
Peek
Xem đỉnh của stack chứa nội
dung là gì?
Giới hạn.
Kích thước tối đa của stack được định nghĩa trước,
không thể thay đổi (nếu dùng mảng).
Nếu stack đã đầy, không PUSH được phần tử mới vào
stack. Nếu stack rỗng, thao tác POP cho kết quả rỗng.
Stack.
chưa đầy.
Kết quả: Phần tử ở đỉnh của Stack được trả lại cho
Chức năng: Lấy giá trị tại đỉnh của Stack nhưng
Kết quả: Giá trị tại đỉnh của Stack được trả về cho lời
17 18 134 216
23 25 47 55
25 23 55 47
18 17 216 134
phân.
Đọc từ Stack cho đến hết, kết quả được nối với nhau để
Đọc từ Stack cho đến hết, kết quả được nối với nhau để
push(stack,t+'0'); mString[i]='\0';
else return mString;
push(stack,t-10+'A'); }
a=a/16;
n++;
}
kt=0; {
else printf("Khong the them vao STACK\n");
char ch=pop(bStack,top); getch();
} }
} }
if(!isEmpty(bStack,*top))
kt=0;
return kt;
}
{ {
printf("STACK rong\n"); if(top==MAXSIZE-1)
value = 0; return 1;
} else
return value; return 0;
}
}
void makeEmpty(char bStack[], int *top)
{ *top=-1; }
xuất vào năm 1920 của nhà toán học người Balan có tên
Jan Łukasiewicz. (Trong một số tài liệu còn gọi là ký pháp
Łukasiewicz).
Với ký pháp này cho phép thấy kết quả ngay sau
phép toán.
Với cách viết này, việc tính toán dựa trên thứ tự của
biểu thức, kết hợp với thứ tự ưu tiên của phép toán.
Bước 1 : Đọc lần lượt các phần tử của biểu thức E1 (từ trái qua phải)
Nếu gặp phép toán thì lấy hai phần tử liên tiếp trong Stack thực
hiện phép toán, kết quả được đẩy vào trong Stack.
Bước 2 : Lập lại bước 1 cho đến khi hết tất cả các phần tử trong biểu
thức E1. lúc đó đỉnh của Stack chứa giá trị của biểu thức cần tính
{ continue ;
char item = p->stack[p->top]; }
p->top--; if (isdigit(*(p->s)) || isalpha (*(p->s)))
return item; {
} while (isdigit(*(p->s)) || isalpha(*(p->s)))
}
{
*(p->t) = *(p->s);
p->s++;
p->t++ ; }
19 PhD. Ngo Huu Phuc, Le Quy Don Technical University
}
11.3. Chương trình minh họa (4/5)
if (*(p->s) == '(') else
{ push(p, *(p->s));
push(p,*(p->s)); p->s++;
p->s++;
}
}
if (*(p->s) == ')')
if (*(p->s) == '*' || *(p->s) == '+' || *(p->s) == '/' || *(p-
>s) == '%' || *(p->s) == '-' || *(p->s) == '$') {
{ opr = pop(p);
if (p->top != -1) while (opr != '(')
{ {
opr = pop(p); *(p->t) = opr;
while ( priority(opr)>= priority (*(p->s)))
p->t++;
{
opr = pop(p); }
*(p->t) = opr;
p->s++;
p->t++;
}
opr = pop(p);
} }
push(p, opr);
push(p, *(p->s)); }
Với các tham số dạng giá trị, tạo bản copy của nó khi
lưu.
Với các biến dạng tham chiếu, lưu trữ địa chỉ của chúng.
Với các biến cục bộ (khai báo trong đoạn chương trình),
Sau đó, sử dụng nhiều thao tác pop để phục hồi thông tin.
long factorial(int n)
{
if((n==0)||(n==1)) return 1;
else return n*factorial(n-1);
}
Sử dụng vòng lặp thứ 2 để lấy giá trị từ Stack và thực hiện phép
nhân.
Lưu ý, địa chỉ lưu vị trí dòng lệnh gọi được đặt tại phép nhân.
Sử dụng vòng lặp thứ 2 để lấy giá trị từ Stack và thực hiện phép
cộng.
Lưu ý, địa chỉ lưu vị trí dòng lệnh gọi được đặt tại phép cộng.
} }
}
22 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
12.4. Khử đệ quy cho bài toán QuickSort (6/6)
void quicksort(int n) { if (last-splitpoint<splitpoint-first) {
StackClass<index> stack; point.left=first;
index point; point.right=splitpoint-1;
int first,last,splitpoint; stack.push(point);
point.left=0;
first=splitpoint+1; }
point.right=n-1;
else {
stack.push(point);
point.left=splitpoint+1;
while (!stack.isEmpty()) {
stack.pop(&point); point.right=last;
scanf("%d",&n); }
stack.push(value); stack.push(value);
StackValue temp; value = CreateValue(temp.nonN-1 ,
while (!stack.isEmpty()) { temp.nonN==2?1:0,temp.nonA,temp.nonC,temp.n
onB);
stack.pop(&value);
stack.push(value);
temp=value;
if((temp.nonN==1)&&(temp.nonT>0)) }
printf("\nChuyen dia %d tu %c sang }
%c",temp.nonT,temp.nonA,temp.nonC);
}
else
27/27 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
Cấu trúc dữ liệu và giải thuật
Bài 13. Hàng đợi - Queues
Tham khảo:
1. Data structures and Algorithms Stacks.htm, Kyle Loudon Mastering
Algorithms,
2. Chapter 6 Stacks and Queues, Elliz Horowitz – Fundamentals of Data
Structures.
3. Chapter 3 Stacks and Queues, Deshpande Kakle – C and Data Structures.
4. Bài giảng TS Nguyễn Nam Hồng.
2 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
13.1. Khái niệm về hàng đợi (1/8)
Ví dụ về hàng đợi
4 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
13.1. Khái niệm về hàng đợi (3/8)
Đối với hàng đợi, số lượng ứng dụng có nhiều hơn cả ngăn
xếp.
Ví dụ như máy tính thực hiện nhiệm vụ, có nhiều hàng đợi
được sử dụng:
hàng đợi máy in,
việc truy xuất đĩa,
sử dụng CPU.
chuyển đổi từ Infix sang Prefix.
Phần tử đầu hàng đợi được phục vụ trước, phần tử này thường
gọi là front hay head. Phần tử mới thêm vào được gọi là rear
hay tail.
5 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
13.1. Khái niệm về hàng đợi (4/8)
Định nghĩa:
Một hàng đợi các phần tử kiểu T là một chuỗi nối tiếp các phần
tử của T và kèm theo một số tác vụ sau:
1. Tạo mới một đối tượng hàng rỗng.
2. Thêm một phần tử mới vào hàng, giả sử hàng đợi chưa đầy
(phần tử dữ liệu mới luôn được thêm vào cuối hàng).
3. Loại một phần tử ra khỏi hàng, giả sử hàng chưa rỗng (phần tử
bị loại là phần tử tại đầu hàng, thường là phần tử vừa được xử lý
xong).
4. Xem phần tử tại đầu hàng (phần tử sắp được xử lý).
case 3: case 6:
if(queue.Peek(&value)) printf("Lam rong QUEUE? Co chac khong? (C/K)");
printf("Gia tri tai front QUEUE: %d\n",value); fflush(stdin);
else scanf("%c",&ch);
printf("Queue rong\n"); if(ch=='c' || ch=='C')
break;
queue.makeEmpty();
case 4:
break;
if(queue.isEmpty()==1)
}
printf("QUEUE rong\n");
} while (key!=7); }
else
15 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
13.2. Xây dựng và sử dụng Queue (3/7)
Một số nhận xét cho dạng Queue nói trên:
Đáp ứng được các tiêu chí về Queue.
Tuy nhiên, với kích thước Queue là 10 (theo ví dụ) chỉ có thể
thêm tối 10 phần tử.
Ngoài ra, vì front chỉ tăng, do đó, trong Queue vẫn còn ô nhớ
nhưng không sử dụng được.
Giải quyết vấn đề trên:
Sử dụng Queue có dạng vòng.
Khi đó, làm thế nào để biết Queue đã đầy hay rỗng?
Giá trị khởi tạo cho front và rear như thế nào?
Xây dựng Queue như thế nào?
16 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
13.2. Xây dựng và sử dụng Queue (4/7)
Ví dụ về Queue
có dạng vòng
}; else return 0;
{ { if(!isEmpty()) {
if(!isEmpty()) *value = entry[(front+1)%MaxEntry];
{ return 1; }
front = (front+1)%MaxEntry; else return 0; }
*value = entry[front]; template<class QueueEntry>
return 1;
void QueueClassC<QueueEntry>::makeEmpty()
}
{ front=rear=0;
else
}
return 0; }
Độ phức tạp của thuật toán O(n log n) trong trường hợp tốt nhất.
Bước 1 : Đọc lần lượt các phần tử của biểu thức E1 (từ phải qua trái)
Nếu gặp phép toán thì lấy hai phần tử liên tiếp trong stack thực hiện phép
Bước 2 : Lập lại bước 1 cho đến khi hết tất cả các phần tử trong biểu thức
E1. Lúc đó đỉnh của stack chứa giá trị của biểu thức cần tính.
4 4 4 9 9 9 9 9 9 45 45 45 51 51 51
5 5 2 2 2 5 5 6 6 8 8
+ 3 3 * + 7
+
51 51 51 3.4
8 15 15
7 /
+
30 PhD Ngo Huu Phuc, Le Quy Don Technical University
13.3. Chương trình minh họa (1/4)
#include "stdio.h" strcpy(input,InfixToPrefix(input));
#include "conio.h" show(input);
#include "string.h" printf("\nGia tri bieu thuc: %d",calculate(input));
#include "ctype.h"
getch();
#define MaxEntry 100
}
#include "StackClass.h"
int priority(char c) {
#include "QueueClass.h"
if (c == '$')
typedef char StackEntry;
typedef char QueueEntry; return 3;
a=a+b; }
1 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
Bài 15: Danh sách liên kết
Nội dung:
15.1. Giới thiệu chung.
15.2. Danh sách liên kết đơn.
15.2.1. Khái niệm về danh sách liên kết đơn.
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn.
15.3. Danh sách liên kết vòng.
15.3.1. Khái niệm về danh sách liên kết vòng.
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng.
15.4. Danh sách liên kết kép.
15.4.1. Khái niệm về danh sách liên kết kép.
15.4.2. Các thao tác cơ bản của danh sách liên kết kép.
15.5. Một số ví dụ về danh sách liên kết.
Tham khảo:
1. Deshpande Kakde: C and Data structures.chm, Chapter 20: Linked Lists
2. Elliz Horowitz – Fundamentals of Data Structures.chm, Chapter 4: Linked Lists
3. Kyle Loudon: Mastering Algorithms with C.chm, Chapter 5 Linked Lists.
4. Bài giảng TS Nguyễn Nam Hồng
2 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.1. Giới thiệu chung (1/2)
Với CTDL dạng mảng, bộ nhớ được sử dụng là một dãy liền
Thời gian cho việc thêm hay bớt phần tử trong mảng khá lâu vì
thể áp dụng được cho nhiều bài toán, chúng ta cũng thấy khả năng
dư thừa bộ nhớ xuất hiện.
3 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.1. Giới thiệu chung (2/2)
Để khắc phục nhược điểm trên, có thể sử dụng danh sách liên
Trong cấu trúc này, không cần xác định kích thước cho các
phần tử trước.
Ta có thể định nghĩa phần tử bất cứ lúc nào, sau đó liên kết
Như vậy, mỗi phần tử sẽ bao gồm thông tin cần lưu trữ và liên
4 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (1/23)
Trong rất nhiều trường hợp cần sử dụng đến danh sách liên kết
động, danh sách liên kết động cần dùng đến khi kích thước
danh sách chưa biết tại thời điểm biên dịch chương trình.
Khi đó, danh sách có thể mở rộng hoặc thu hẹp lại tại thời
điểm chạy chương trình.
Cấu trúc dữ liệu linked list sử dụng mô hình liên kết động.
Một số dạng của danh sách liên kết:
Danh sách liên kết đơn.
Danh sách liên kết vòng.
Danh sách liên kết kép.
5 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (2/23)
15.2.1. Khái niệm về danh sách liên kết đơn
60 800 45 90 55 0 NULL
1000 800 90
6 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (3/23)
15.2.1. Khái niệm về danh sách liên kết đơn
start
60 800 45 90 55 0 NULL
1000 800 90
7 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (4/23)
15.2.1. Khái niệm về danh sách liên kết đơn
Khai báo cấu trúc một Node của danh sách:
template <class ListEntry>
struct node
{
ListEntry data;
struct node *link;
};
8 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (5/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
1. Khởi tạo danh sách.
2. LAdd: Thêm một node vào đầu danh sách.
3. LInsert: Chèn một node vào danh sách.
4. LAppend: Thêm một node vào cuối danh sách.
5. LFind: Tìm một node trong danh sách.
6. LDelete: Xóa một node trong danh sách.
7. LLength: Số phần tử trong danh sách.
8. LMakeEmpty: Làm rỗng danh sách.
9. LGet: Lấy thông tin về một phần tử trong danh sách.
9 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (6/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
class LList {
public:
LList();
int LAdd(ListEntry entry);
int LInsert(ListEntry value, ListEntry entry);
int LAppend(ListEntry entry);
int LFind(ListEntry value);
int LDelete(ListEntry value);
int LLength();
void LMakeEmpty();
int LGet(int pos, ListEntry *value);
template<typename ListEntry> friend void LPrint(LList<ListEntry> list);
private:
node<ListEntry> *start;
};
10 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (7/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Khởi tạo danh sách:
template <class ListEntry>
LList<ListEntry>::LList() start
{
start = NULL;
NULL
}
11 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (8/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Thêm một node đầu danh sách:
Nếu danh sách rỗng, cấp phát ô nhớ và start temp 1
cho start trỏ vào ô nhớ đó.
Nếu danh sách không rỗng:
Cấp phát ô nhớ cho biến temp. data link NULL
Phần liên kết của temp trỏ vào đầu danh
sách. 2
Con trỏ start trỏ vào temp.
temp start
12 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (9/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Thêm một node đầu danh sách: else {
template <class ListEntry> temp=(node<ListEntry> *)
int LList<ListEntry>::LAdd(ListEntry entry) {
malloc(sizeof(node<ListEntry>));
int kt=0;
if(temp==NULL) {
node<ListEntry> *temp;
printf("Loi cap phat bo nho!\n");
if(start==NULL) {
start=(node<ListEntry> *) return kt; }
malloc(sizeof(node<ListEntry>)); kt=1;
if(start==NULL) { temp->data=entry;
printf("Loi cap phat bo nho!\n"); temp->link=start;
return kt; }
start=temp;
kt=1;
}
start->data=entry;
return kt;
start->link=NULL;
} }
13 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (10/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Chèn 1 node vào danh sách:
Nhập thông tin về node đứng trước node thêm mới.
Sử dụng con trỏ temp để đến được node đứng trước đó.
Cấp phát ô nhớ cho biến temp1.
Phần liên kết của temp1 trỏ vào phần liên kết của con trỏ temp.
Phần liên kết của con trỏ temp trỏ vào temp1.
start temp
15 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (12/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Thêm một node cuối danh sách:
Nếu danh sách rỗng, cấp phát ô nhớ và start temp 1
cho start trỏ vào ô nhớ đó.
Nếu danh sách không rỗng:
Sử dụng con trỏ temp đi đến cuối danh data link NULL
sách.
Cấp phát ô nhớ cho temp->link.
2
Phần liên kết của temp->link trỏ vào
NULL.
start temp
1 data link 2
16 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (13/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Thêm một node cuối danh sách: else {
template <class ListEntry> temp=start;
int LList<ListEntry>::LAppend(ListEntry entry) { while(temp->link!=NULL)
int kt=0;
temp=temp->link;
node<ListEntry> *temp;
temp->link=(node<ListEntry> *)
if(start==NULL) {
start=(node<ListEntry> *) malloc(sizeof(node<ListEntry>));
malloc(sizeof(node<ListEntry>)); if(temp->link==NULL) {
if(start==NULL) { printf("Loi cap phat bo nho!\n");
printf("Loi cap phat bo nho!\n"); return kt; }
return kt; } kt=1; temp=temp->link;
kt=1;
temp->data=entry;
start->data=entry;
temp->link=NULL; }
start->link=NULL;
} return kt;
}
17 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (14/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Tìm một node trong danh sách:
Sử dụng con trỏ temp để duyệt qua
danh sách.
Sử dụng thêm biến pos để lưu vị trí
của node trong danh sách. Nếu danh
sách rỗng hoặc không tìm thấy trả về
0, ngược lại trả về vị trí của node.
18 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (15/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Tìm một node trong danh sách:
template <class ListEntry>
int LList<ListEntry>::LFind(ListEntry value) {
int pos=0;
node<ListEntry> *temp=start;
while(temp!=NULL) {
pos++;
if(temp->data==value)
break;
temp=temp->link;
}
if(temp!=NULL)
return pos;
else return 0;
}
19 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (16/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Xóa một node trong danh sách: prev curr
Sử dụng 2 con trỏ prev và curr để tìm 1
node cần xóa. Con trỏ prev trỏ vào node start
trước node cần xóa. Con trỏ curr trỏ vào
node cần xóa.
NULL data link data link NULL
Nếu curr = start, cho start trỏ vào
start->link và giải phóng ô nhớ của curr.
Nếu không, prev->link trỏ tới
curr->link và giải phóng ô nhớ của curr.
2
start prev curr
20 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (17/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Xóa một node trong danh sách : if(curr->data==value) kt=1;
int LList<ListEntry>::LDelete(ListEntry value) { else kt=2;
node<ListEntry> *prev, *curr; if(kt==1) {
int kt=0; if(prev==NULL) {
if(start==NULL) return kt;
start=start->link;
else {
free(curr); }
prev=NULL;
else {
curr=start;
prev->link=curr->link;
while(!kt && curr->link!=NULL) {
free(curr);
if(curr->data==value) {
kt=1; }
break; } }
else { }
prev=curr; return kt;
curr=curr->link; } } }
21 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (18/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Lấy thông tin một node ở vị trí nào đó:
Sử dụng con trỏ temp để duyệt qua
danh sách.
Duyệt cho đến khi đến node thứ pos,
lấy thông tin tại node đó và trả lại cho
nơi gọi hàm.
start temp
22 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (19/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Lấy thông tin một node ở vị trí nào đó :
template <class ListEntry>
int LList<ListEntry>::LGet(int pos, ListEntry *value) {
int i=0;
node<ListEntry> *temp=start;
while(temp!=NULL && i<pos) {
temp=temp->link;
if(temp!=NULL)
i++;
}
if(i!=pos) return 0;
else {
*value=temp->data;
return 1;
}
}
23 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (20/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Xác định số phần tử trong danh sách:
Sử dụng con trỏ temp để duyệt qua
danh sách, cho đến khi temp=NULL.
Sử dụng một biến length để đếm số
phần tử đã duyệt.
24 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (21/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Xác định số phần tử trong danh sách:
template <class ListEntry>
int LList<ListEntry>::LLength() {
int length=0;
node<ListEntry> *temp=start;
while(temp!=NULL)
{
length++;
temp=temp->link;
}
return length;
}
25 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (22/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Làm rỗng danh sách:
Xóa từng phần tử trong danh sách.
Sử dụng con trỏ temp để xác định
phần tử cần xóa (từ đầu danh sách).
Dịch chuyển con trỏ start sang phần
tử kế tiếp.
Thu hồi ô nhớ ứng với con trỏ temp.
Thực hiện cho đến khi start=NULL.
temp start
26 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.2. Danh sách liên kết đơn (23/23)
15.2.2. Các thao tác cơ bản của danh sách liên kết đơn
Làm rỗng danh sách :
template <class ListEntry>
void LList<ListEntry>::LMakeEmpty()
{
node<ListEntry> *temp;
while(start!=NULL)
{
temp=start;
start=start->link;
free(temp);
}
}
27 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (1/20)
15.3.1. Khái niệm về danh sách liên kết vòng
Được định nghĩa giống danh sách
liên kết đơn, tuy nhiên, phần tử
Data Add
cuối thay vì trỏ vào NULL thì trỏ Link
vào phần tử đầu tiên. Có 1 node
Danh sách liên kết vòng được
dùng khi có nhiều thao tác với dữ
liệu.
60 800 45 90 55 1000
1000 800 90
28 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (2/20)
15.3.1. Khái niệm về danh sách liên kết vòng
Để quản lý danh sách liên kết, thông thường cần:
• Start là con trỏ chỉ đến một phần tử của danh sách liên kết.
• Phần tử “cuối” của danh sách, liên kết với phần tử do start quản lý.
• Như vậy, từ bất kỳ vị trí nào trong danh sách cũng có thể duyệt hết danh sách.
Lưu ý: khi thêm hay bớt phần tử nào trong danh sách cần đảm bảo rằng
sau thao tác đó vẫn giữ được tính liên kết vòng.
start
60 800 45 90 55 1000
1000 800 90
29 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (3/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
template <class ListEntry>
class CLList {
public:
CLList();
int CLAdd(ListEntry entry);
int CLInsert(ListEntry value, ListEntry entry);
int CLAppend(ListEntry entry);
int CLFind(ListEntry value);
int CLDelete(ListEntry value);
int CLLength();
void CLMakeEmpty();
int CGet(int pos, ListEntry *value);
template<typename ListEntry> friend void LPrint(CLList<ListEntry> list);
private:
node<ListEntry> *start;
};
30 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (4/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Khởi tạo danh sách LKV:
template <class ListEntry>
CLList<ListEntry>::CLList() start
{
start = NULL;
NULL
}
31 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (5/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Thêm một node đầu danh sách LKV:
Nếu danh sách rỗng, cấp phát ô nhớ và start 1
cho start trỏ vào ô nhớ đó, phần liên
kết trỏ vào start.
Nếu danh sách không rỗng: data link
Sử dụng biến temp để duyệt qua danh
sách, khi temp→link <> start.
Cấp phát ô nhớ cho temp→link. 2
Phần liên kết của ô nhớ mới trỏ vào
start.
temp->link
Start trỏ vào vùng ô nhớ mới trên.
start
temp 10 link
30 link 20 link
32 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (6/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Thêm một node đầu danh sách LKV: else {
template < class ListEntry> temp=start;
int CLList<ListEntry>::CLAdd(ListEntry entry) while (temp->link!=start)
{ int kt=0; temp=temp->link;
node<ListEntry> *temp; temp->link=(node<ListEntry> *)
if (start==NULL) { malloc(sizeof(node<ListEntry>));
start=(node<ListEntry> *) if (temp->link==NULL) {
malloc(sizeof(node<ListEntry>)); printf("Loi cap phat bo nho!\n");
if (start==NULL) { return kt; }
printf("Loi cap phat bo nho!\n"); kt=1; temp=temp->link;
return kt; } temp->data=entry;
kt=1; temp->link=start;
start->data=entry; start=temp; }
start->link=start; return kt;
} }
33 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (7/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Chèn 1 node vào danh sách LKV:
Nhập thông tin về node đứng trước node thêm mới.
Sử dụng con trỏ temp để đến được node đứng trước đó.
Cấp phát ô nhớ cho biến temp1.
Phần liên kết của temp1 trỏ vào phần liên kết của con trỏ temp.
Phần liên kết của con trỏ temp trỏ vào temp1.
temp1
34 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (8/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Chèn 1 node vào danh sách LKV : temp1=(node<ListEntry> *)
template <class ListEntry> malloc(sizeof(node<ListEntry>));
int CLList<ListEntry>::CLInsert(ListEntry value, if(temp1->link==NULL) {
ListEntry entry) {
printf("Loi cap phat bo nho!\n");
int kt=0;
return kt; }
node<ListEntry> *temp, *temp1;
kt=1;
if (start==NULL) printf("Danh sach rong!");
else { temp1->data=entry;
temp=start; temp1->link=temp->link;
while (temp->link!=start) { temp->link=temp1;
if (temp->data==value) break; }
else temp=temp->link;
}
}
return kt;
if (temp->data==value) {
}
35 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (9/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Thêm một node cuối danh sách LKV:
Nếu danh sách rỗng, cấp phát ô nhớ và start 1
cho start trỏ vào ô nhớ đó, phần liên
kết trỏ vào start.
Nếu danh sách không rỗng: data link
Sử dụng biến temp để duyệt qua danh
sách, khi temp→link <> start.
2
Cấp phát ô nhớ cho temp→link.
Phần liên kết của ô nhớ mới trỏ vào
start. temp->link
start
temp 40 link
30 link 20 link
36 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (10/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Thêm một node cuối danh sách LKV: else {
template <class ListEntry> temp=start;
int CLList<ListEntry>::CLAppend(ListEntry entry) { while (temp->link!=start)
int kt=0; temp=temp->link;
node<ListEntry> *temp; temp->link=(node<ListEntry> *)
if (start==NULL) { malloc(sizeof(node<ListEntry>));
start=(node<ListEntry> *) if (temp->link==NULL) {
malloc(sizeof(node<ListEntry>)); printf("Loi cap phat bo nho!\n");
if (start==NULL) { return kt; }
printf("Loi cap phat bo nho!\n"); kt=1;
temp=temp->link;
return kt; }
temp->data=entry;
kt=1;
temp->link=start;
start->data=entry;
}
start->link=start;
return kt;
}
}
37 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (11/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Tìm một node trong danh sách LKV:
Sử dụng con trỏ temp để duyệt qua
danh sách, khi temp→link <> start.
Sử dụng thêm biến pos để lưu vị trí
của node trong danh sách. Nếu danh
sách rỗng hoặc không tìm thấy trả về
0, ngược lại trả về vị trí của node.
Tìm vị trí của phần tử
có giá trị 30!
start temp pos = 3
38 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (12/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Tìm một node trong danh sách LKV:
template <class ListEntry>
int CLList<ListEntry>::CLFind(ListEntry value) {
int pos=0;
node<ListEntry> *temp=start;
if (temp!=NULL) {
do {
pos++;
if (temp->data==value) break;
temp=temp->link;
} while (temp!=start);
if (temp->data!=value) pos=0;
}
return pos;
}
39 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (13/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Xóa một node trong danh sách LKV: prev
Sử dụng 2 con trỏ prev và curr để tìm node 1
cần xóa. Con trỏ prev trỏ vào node trước start
node cần xóa. Con trỏ curr trỏ vào node cần
xóa.
Nếu curr=curr->link, danh sách có 1 phần NULL data link NULL
tử, cho start trỏ NULL, giải phóng ô nhớ.
Nếu curr <> start, cho prev->link trỏ vào
curr->link và giải phóng ô nhớ của curr.
Nếu curr=start, thay đổi start, prev->link
trỏ curr->link và giải phóng ô nhớ của curr.
2
start prev
40 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (14/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Xóa một node trong danh sách LKV: if(kt==1) {
template <class ListEntry> if (curr==curr->link) {
int CLList<ListEntry>::CLDelete(ListEntry value) { start=NULL; free(curr); }
node<ListEntry> *prev, *curr; else {
int kt=0; if (curr!=start) {
if (start==NULL) return kt; prev->link=curr->link; free(curr); }
else { else {
prev=NULL; prev=start; curr=start->link;
curr=start; while (curr!=start) {
while (!kt && curr->link!=start) { prev=curr;
if (curr->data==value) { curr=curr->link; }
kt=1; break; } prev->link=curr->link;
else { start=prev->link;
prev=curr; free(curr);
curr=curr->link; } }
} }
if (curr->data==value) }
kt=1; }
else return kt;
kt=2; }
41 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (15/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Lấy thông tin một node ở vị trí nào đó LKV:
Sử dụng con trỏ temp để duyệt qua danh
sách.
Duyệt cho đến khi đến node thứ pos, lấy
thông tin tại node đó và trả lại cho nơi gọi
hàm.
42 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (16/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Lấy thông tin một node ở vị trí nào đó : if (i!=pos)
template <class ListEntry> return 0;
int CLList<ListEntry>::CGet(int pos, ListEntry *value) else
{ {
int i=0; *value=temp->data;
node<ListEntry> *temp=start; return 1;
if (temp!=NULL) { }
if (i<pos) { }
i++; else
temp=temp->link; } return 0;
while (temp!=start && i<pos) { }
temp=temp->link;
if (temp!=start)
i++;
}
43 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (17/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Xác định số phần tử trong danh sách:
Sử dụng con trỏ temp để duyệt qua
danh sách, khi temp<> start.
Sử dụng một biến length để đếm số
phần tử đã duyệt.
44 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (18/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Xác định số phần tử trong danh sách LKV:
template <class ListEntry>
int CLList<ListEntry>::CLLength()
{ int length=0;
node<ListEntry> *temp=start;
if(temp!=NULL)
do
{
length++;
temp=temp->link;
}
while(temp!=start);
return length;
}
45 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (19/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Làm rỗng danh sách:
Để làm rỗng danh sách, cách đơn giản
có thể sử dụng là xóa từng phần tử
bằng hàm Delete.
Ngoài ra, có thể viết lại hàm xóa từng
phần tử.
Lưu ý: tính vòng của danh sách.
temp start
46 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.3. Danh sách liên kết vòng (20/20)
15.3.2. Các thao tác cơ bản của danh sách liên kết vòng
Làm rỗng danh sách LKV:
template <class ListEntry>
void CLList<ListEntry>::CLMakeEmpty()
{
node<ListEntry> *temp;
while(start!=NULL)
{
temp=start;
start=start->link;
CLDelete(temp->data);
}
}
47 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (1/22)
15.4.1. Khái niệm về danh sách liên kết kép
Với các danh sách liên kết đơn, một số vấn đề xuất hiện:
Với danh sách liên kết đơn, chỉ cho phép duyệt danh sách theo một
chiều.
Để xóa một node cần lưu node đứng trước đó.
Nếu một liên kết bị hỏng, các phần tử sau đó không dùng được.
Để giải quyết vấn đề trên, có thể thêm cho mỗi phần tử một liên kết
nữa, liên kết này có chiều ngược lại. Khi thêm mỗi node một liên
kết như vậy, danh sách liên kết được gọi là có liên kết kép.
NULL
0 60 800 1000 45 90 800 55 0
NULL
1000 800 90
49 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (3/22)
15.4.1. Khái niệm về danh sách liên kết kép
Trong thực tế, có thể tổ chức thành danh sách liên kết kép vòng (có header hoặc không có
header).
Với trường hợp không có header, liên kết right của phần tử cuối trỏ vào phần tử đầu tiên
(do start quản lý), liên kết left của phần tử đầu tiên trỏ tới phần tử cuối cùng.
Nếu có header, với việc thêm phần tử này, các thao tác cơ bản sẽ dễ xác định điều kiện
dừng.
start
50 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (4/22)
15.4.1. Khái niệm về danh sách liên kết kép
Khai báo cấu trúc một Node của danh sách liên kết kép:
template <class ListEntry>
struct dnode
{
ListEntry data;
struct dnode *left, *right;
};
51 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (5/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
template <class ListEntry>
class DLList {
public:
DLList();
int DLAdd(ListEntry entry);
int DLInsert(ListEntry value, ListEntry entry);
int DLAppend(ListEntry entry);
int DLFind(ListEntry value);
int DLDelete(ListEntry value);
int DLLength();
void DLMakeEmpty();
int DGet(int pos, ListEntry *value);
template<typename ListEntry> friend void LPrint(DLList<ListEntry> list);
private:
dnode<ListEntry> *start, *end;
};
52 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (6/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Khởi tạo danh sách LK kép:
template <class ListEntry>
DLList<ListEntry>::DLList() start end
{
start = NULL;
NULL
end = NULL;
}
53 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (7/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Thêm một node đầu danh sách LK kép:
Nếu danh sách rỗng, cấp phát ô nhớ và start end 1
cho start và end trỏ vào ô nhớ đó.
Nếu danh sách không rỗng:
Cấp phát ô nhớ cho con trỏ temp. NULL left data right NULL
Tạo các liên kết left và right tương ứng
(liên kết temp->left trỏ vào NULL, liên
kết temp->right trỏ vào start). 2
Start trỏ vào vùng ô nhớ mới trên.
55 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (9/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Chèn 1 node vào danh sách LK kép:
Nhập thông tin về node đứng trước node thêm mới.
Sử dụng con trỏ curr để xác định node đứng trước node cần thêm.
Cấp phát ô nhớ cho biến temp.
Tạo các liên kết left và right của temp thông qua curr và curr->right.
Lưu ý: có thể không xác định được node đứng trước, khi đó không thực hiện gì cả.
temp
curr curr->right
4 1000 45 90 1
2 3
? 60 800 800 55 ?
1000 800 90
56 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (10/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Chèn 1 node vào danh sách LK kép : temp->data=entry;
template <class ListEntry> temp->right=NULL;
int DLList<ListEntry>::DLInsert(ListEntry value, temp->left=end;
ListEntry entry) { end->right=temp;
int kt=0; end=temp;
dnode<ListEntry> *temp, *curr; kt=1; }
if (start==NULL) printf("Danh sach rong!"); }
else { else {
curr=start; temp=(dnode<ListEntry> *)
while(curr!=NULL) { malloc(sizeof(dnode<ListEntry>));
if (curr->data==value) break; if (temp==NULL) {
else curr=curr->right; } printf("Loi cap phat bo nho!\n");
if (curr==NULL) { return kt; }
if (end->data==value) { temp->data=entry;
temp=(dnode<ListEntry> *) temp->right=curr->right;
malloc(sizeof(dnode<ListEntry>)); temp->left=curr;
if (temp==NULL) { temp->right->left=temp;
printf("Loi cap phat bo nho!\n"); temp->left->right=temp;
return kt; } kt=1; }
}
return kt;
57
}
@copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (11/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Thêm một node cuối danh sách LK kép:
Nếu danh sách rỗng, cấp phát ô nhớ và cho
start end 1
start và end trỏ vào ô nhớ đó.
Nếu danh sách không rỗng:
Cấp phát ô nhớ cho con trỏ temp.
Tạo các liên kết left và right tương ứng (liên kết
NULL left data right NUL
temp->right trỏ vào NULL, liên kết temp->left L
trỏ vào end).
End->right trỏ vào temp. 2
End trỏ vào vùng ô nhớ mới trên.
end temp
4
3 1
0 60 800 1000 45 90 800 55 0
2 NULL
1000 800 90
58 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (12/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Thêm một node cuối danh sách LK kép: else
template <class ListEntry> { temp=(dnode<ListEntry> *)
int DLList<ListEntry>::DLAppend(ListEntry entry){ malloc(sizeof(dnode<ListEntry>));
int kt=0; if (temp==NULL)
dnode<ListEntry> *temp; {
if (start==NULL) { printf("Loi cap phat bo nho!\n");
start=(dnode<ListEntry> *) return kt;
malloc(sizeof(dnode<ListEntry>)); }
if (start==NULL) { kt=1;
printf("Loi cap phat bo nho!\n"); temp->data=entry;
return kt; } temp->right=NULL;
kt=1; temp->left=end;
start->data=entry; end->right=temp;
start->left=start->right=NULL; end=temp;
end=start; }
} return kt;
}
59 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (13/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Tìm một node trong danh sách LK kép:
Sử dụng con trỏ temp để duyệt qua
danh sách, khi temp<> NULL.
Sử dụng thêm biến pos để lưu vị trí
của node trong danh sách. Nếu danh
sách rỗng hoặc không tìm thấy trả về
0, ngược lại trả về vị trí của node. Tìm vị trí của phần tử
có giá trị 45!
start temp pos=2 end
61 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (15/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Xóa một node trong danh sách LK kép:
Sử dụng con trỏ curr để xác định vị trí curr start 1
cần xóa.
Nếu vị trí cần xóa là start:
Chuyển con trỏ start sang phải.
NULL
0 60 800 0 45 ?
Thay đổi liên kết left của start về NULL.
Giải phóng ô nhớ do curr quản lý.
1000 800
Nếu vị trí cần xóa không phải là start:
Thay đổi các liên kết dựa vào left và right.
2
Giải phóng ô nhớ do curr quản lý.
curr
1
1000 2 800 90
62 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (16/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Xóa một node trong danh sách LK kép: if (kt!=1 && curr==NULL)
template <class ListEntry> kt=2;
int DLList<ListEntry>::DLDelete(ListEntry value) if (kt==1)
{ {
dnode<ListEntry> *curr; if (curr==start)
int kt=0; {
if (start==NULL) start=start->right;
return kt; start->left=NULL;
else { free(curr);
curr=start; }
while (!kt && curr!=NULL) else
{ {
if (curr->data==value) curr->left->right=curr->right;
{ curr->right->left=curr->left;
kt=1; free(curr);
break; }
} }
else return kt;
curr=curr->right; }
} }
63 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (17/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Lấy thông tin một node ở vị trí nào đó LK kép:
Sử dụng con trỏ temp để duyệt qua danh
sách.
Duyệt cho đến khi đến node thứ pos, lấy
thông tin tại node đó và trả lại cho nơi gọi
hàm.
Cho trước
start temp pos=2 end
65 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (19/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Xác định số phần tử trong danh sách LK kép:
Sử dụng con trỏ temp để duyệt qua danh sách,
khi temp<> start.
Sử dụng một biến length để đếm số phần tử đã
duyệt.
length = 2
start temp end
67 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Danh sách liên kết kép (21/22)
15.4.2. Các thao tác cơ bản của danh sách liên kết kép
Làm rỗng danh sách liên kết kép:
Để làm rỗng danh sách, cách đơn giản
có thể sử dụng là xóa từng phần tử
bằng hàm Delete.
Ngoài ra, có thể viết lại hàm xóa từng
phần tử.
start end
69 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
15.4. Một số dạng và ví dụ sử dụng DSLK (1/)
Trong thực tế, có thể sử dụng DSLK cho nhiều ứng dụng như:
Xây dựng Stack, Queue sử dụng danh sách liên kết.
Biểu diễn mảng, ma trận thưa bằng danh sách liên kết.
...
70 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University Tháng 09 năm 2009
Cấu trúc dữ liệu và giải thuật
Bài 17. Cấu trúc dữ liệu dạng cây
Tham khảo:
1. Deshpande Kakde: C and Data structures.chm, Chapter 21: Trees
2. Elliz Horowitz – Fundamentals of Data Structures.chm, Chapter 5: Trees.
3. Kyle Loudon: Mastering Algorithms with C.chm, Chapter 9. Trees.
4. Bài giảng TS Nguyễn Nam Hồng
Các node còn lại được phân chia rời nhau thành n tập dạng T1,
B C D
G H I E F
B C D
G H I E F
Cạnh (cung).
Gốc (root)
Node. node
Cạnh (edge, arc)
Lá. A
B C D
G H I E F
Lá (leaf)
Với một cây có n node, như vậy có n! cách duyệt cây khác
nhau. Tuy nhiên, đa số các phép duyệt cây đó không hữu ích.
Đối với cây tổng quát, có 2 cách duyệt cây thông thường:
...
R: Duyệt các cây con còn lại của node đang xét.
1. Thăm node đang xét trước các node con của nó.
2. Các node con được thăm theo thứ tự từ trái qua phải.
3. Với mỗi node con, việc thăm được thực hiện theo dạng tiền
thứ tự.
3 4 5 7 8
G H I E F
Thứ tự đã duyệt: A B G H I C E F D
15 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
17.2.1. Duyệt cây theo chiều sâu (4/7)
Duyệt trung thứ tự (Inorder): LNR
1. Thăm con thứ nhất của node đang xét dạng trung thứ tự.
3. Thăm các con còn lại của node đang xét dạng trung thứ tự.
1 3 4 6 8
G H I E F
Thứ tự đã duyệt: G B H I A E C F D
17 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
17.2.1. Duyệt cây theo chiều sâu (6/7)
Duyệt hậu thứ tự (Postorder): LRN
1. Thăm con thứ nhất của node đang xét dạng hậu thứ tự.
2. Thăm các con còn lại của node đang xét dạng hậu thứ tự.
1 2 3 5 6
G H I E F
Thứ tự đã duyệt: G H I B E F C D A
19 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
17.2.2. Duyệt cây theo chiều rộng (1/2)
Thăm các node bắt đầu từ mức thấp nhất cho đến các mức cao.
Thứ tự đã duyệt: A B C D G H I E F
1
A
2
B 3 C 4 D
5 6 7 8 9
G H I E F
Tham khảo:
1. Deshpande Kakde: C and Data structures.chm, Chapter 21: Trees
2. Elliz Horowitz – Fundamentals of Data Structures.chm, Chapter 5: Trees
3. Kyle Loudon: Mastering Algorithms with C.chm, Chapter 9 Trees.
4. Bài giảng TS Nguyễn Nam Hồng.
Các node khác trên cây được chia thành 2 tập T1 và T2, trong đó
chúng cũng là cây nhị phân.
Cây con T1 được gọi là cây con bên trái.
A A
B B
C C
C D F G
Ví dụ về cây nhị
phân đầy đủ có độ
cao là 3
6 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
19.1. Khái niệm về cây nhị phân (5/7)
Cây nhị phân hoàn chỉnh (Complete binary tree):
Cây nhị phân hoàn chỉnh có độ cao là k, với độ cao k-1 là đầy đủ.
Tại độ cao là k, các node được đưa vào cây từ trái sang phải.
Nhận xét:
Các node tại mức k-2 về trước có đủ 2 con.
Các node ở mức k-1 có thể có 2 con hoặc 1 con. Nếu có 1 con trì có con trái
(các node cùng mức trước đó đã có 2 con)
A A
B E B E
C D C D F
Ví dụ về cây nhị phân hoàn chỉnh
A A
B E B E
C D F G C D F
A A
B E B E
C D C D F
Nếu cây nhị phân hoàn chỉnh được biểu diễn dưới dạng mảng, giá trị của
phần tử thứ i sẽ được chứa trong mảng tại vị trí thứ i (1<=i<=n).
Khi đó, phần tử “cha” của i sẽ là i/2 và 2 con của node i sẽ là 2i và 2i+1.
1 A A
2 B
3 C B C
4 D D E
5 E
1 A A
2 B
3 C B C
4 D
5 E
6 F D E F G
7 G
8 H I
9
10 H
11 I Ô nhớ lãng phí
B C
D NULL NULL
NULL NULL
1. Thăm node đang xét trước các node con của nó.
Thứ tự đã duyệt: A B D E H I C F G
18 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
19.3. Duyệt cây nhị phân (4/9)
Duyệt trung thứ tự (Inorder): LNR
1. Thăm cây con bên trái của node đang xét trước.
Thứ tự đã duyệt: D B H E I A F C G
20 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
19.3. Duyệt cây nhị phân (6/9)
Duyệt hậu thứ tự (Postorder): LRN
1. Thăm cây con bên trái của node đang xét trước.
2. Thăm cây con bên phải của node đang xét trước.
Thứ tự đã duyệt: D H I E B F G C A
22 @copyright by PhD Ngo Huu Phuc, Le Quy Don Technical University
19.3. Duyệt cây nhị phân (8/9)
Thăm các node bắt đầu từ mức thấp nhất cho đến các mức cao.
Tại mỗi mức, thăm từ con trái trước, sau đó thăm con phải.
2 3
B C
4 5 6 7
D E F G
8 9
H I
Thứ tự đã duyệt: A B C D G H I E F
Tham khảo:
1. Deshpande Kakde: C and Data structures.chm, Chapter 20: Linked Lists
2. Elliz Horowitz – Fundamentals of Data Structures.chm, Chapter 4: Linked Lists
3. Kyle Loudon: Mastering Algorithms with C.chm, Chapter 5 Linked Lists.
4. Bài giảng TS Nguyễn Nam Hồng
Cây nhị phân tìm kiếm là cây rỗng hoặc cây mà node có
chứa các khóa.
Các khóa của node trên cây con bên trái nhỏ hơn khóa của
root, khóa của các node trên cây con bên phải lớn hơn khóa
của root.
Cây con bên trái và phải cũng là cây nhị phân tìm kiếm.
50
30 60
25 40 70
35 65
Hãy đưa ra cách tổ chức để thực hiện hiệu quả các công việc
trên.
Xóa: mất nhiều thời gian tìm vị trí cần xóa và dồn mảng->O(n)
Xóa: mất nhiều thời gian tìm vị trí cần xóa và dồn mảng->O(n)
Xóa: nhanh trong tổ chức các nút, nhưng chậm trong tìm kiếm
ID Họ và tên Điểm
0012345 Nguyễn Văn A 10
0033333 Nguyễn Văn B 9
0056789 Nguyễn Văn C 8
… … …
9801010 Nguyễn Thị A 7
9802020 Nguyễn Thị B 8
… … …
9903030 Trần Văn A 9
9908080 Trần Văn B 10
0 … …
• Để lưu trữ một bản ghi, … … …
tính Hash(ID) cho bản 3 Trần Văn B 10
… … …
ghi và lưu trữ nó tại vị trí
67 Nguyễn Văn B 9
Hash(ID) của mảng. … … …
Thêm: O(1)
Xóa: O(1)
Hàm băm là giải thuật nhằm sinh ra các giá trị băm tương ứng với
Giá trị băm đóng vai gần như một khóa để phân biệt các khối dữ
liệu.
Hàm băm thường được dùng trong bảng băm nhằm giảm chi phí
tính toán khi tìm một khối dữ liệu trong một tập hợp.
Bảng băm
quy ước là bỏ bớt chẳng hạn các chữ số hàng lẻ (1,3,5…), số còn
lại sẽ là 821. Vậy H(x) = H(842615) = 821.
Nhận xét: Hàm cắt bỏ thỏa mãn tính chất thứ nhất của hàm băm
nhưng tính chất thứ hai là khó thực hiện (khó có phân bố đều).
Khóa có giá trị nguyên và bảng băm B có m phần tử, ta lấy phần
dư của phép chia x/m làm giá trị hàm băm. Để đảm bảo tính chất
thứ hai của hàm băm nên chọn m là số nguyên tố.
Nhận xét: Các cách lấy phần dư cho khả năng tránh hiện tượng
chọn, sau đó kết hợp các phần đó lại theo một quy ước nào đó.
Ví dụ: Số các hàng lẻ: 465 và số các hàng chẵn: 821, vậy
H(x)=465+821=1286.
Nhận xét: Tính chất thứ nhất của hàm băm được thỏa mãn. Do
các chữ số của khóa đều có sử dụng, nên tính chất thứ hai có thể
thỏa mãn tốt hơn với trường hợp dùng hàm băm cắt bỏ.
H(‘0012345’) = 134
H(‘0033333’) = 67
H(‘0056789’) = 764
…
H(‘9903030’) = 3
H(‘9908080’) = 3
• Xử lý thế nào khi hai khóa khác nhau lại ánh xạ đến một
địa chỉ?
theo rồi chèn phần tử bị đụng độ vào đó. Khi mảng đầy thì
resize lại mảng.
sách một vị trí còn trống kế tiếp, phương pháp dây chuyền liên
kết các danh sách có các khóa khác nhau nhưng có cùng giá trị
hàm băm thành một danh sách.
49 49 49
58 58
9
18 18 18 18
89 89 89 89 89