Dhhh ctdlgt bai giang cau truc du lieu

80
BỘ GIAO THÔNG VẬN TẢI TRƢỜNG ĐẠI HỌC HÀNG HẢI BỘ MÔN: KHOA HỌC MÁ Y TÍNH KHOA: CÔNG NGHỆ THÔNG TIN BÀI GIẢNG CẤU TRÚC DỮ LIỆU TÊN HỌC PHẦN : CẤU TRÚC DỮ LIỆU MÃ HỌC PHẦN : 17207 TRÌNH ĐỘ ĐÀO TẠO : ĐẠI HỌC CHÍNH QUY DÙNG CHO SV NGÀNH : CÔNG NGHỆ THÔNG TIN HẢI PHÒNG - 2008

Transcript of Dhhh ctdlgt bai giang cau truc du lieu

Page 1: Dhhh ctdlgt bai giang cau truc du lieu

BỘ GIAO THÔNG VẬN TẢI

TRƢỜNG ĐẠI HỌC HÀNG HẢI

BỘ MÔN: KHOA HOC MAY TINH

KHOA: CÔNG NGHỆ THÔNG TIN

BÀI GIẢNG

CẤU TRÚC DỮ LIỆU

TÊN HỌC PHẦN : CẤU TRÚC DỮ LIỆU

MÃ HỌC PHẦN : 17207

TRÌNH ĐỘ ĐÀO TẠO : ĐẠI HỌC CHÍNH QUY

DÙNG CHO SV NGÀNH : CÔNG NGHỆ THÔNG TIN

HẢI PHÒNG - 2008

Page 2: Dhhh ctdlgt bai giang cau truc du lieu

i

MỤC LỤC

CHƢƠNG 1. CÁC KHÁI NIỆM MỞ ĐẦU .............................................................................. 1

1.1. Giải thuật và cấu trúc dữ liệu. ......................................................................................... 1

1.2. Cấu trúc dữ liệu và các vấn đề liên quan. ....................................................................... 1

1.3. Ngôn ngữ diễn đạt giải thuật. .......................................................................................... 2

1.4. Kiểu dữ liệu, cấu trúc dữ liệu, kiểu dữ liệu trừu tƣợng ................................................... 3

CHƢƠNG 2. CÁC KIỂU DỮ LIỆU TRỪU TƢỢNG CƠ BẢN ............................................... 6

2. 1. Ngăn xêp - Stack ............................................................................................................ 6

2.1.1 Khái niệm .................................................................................................................. 6

2.1.2 Các thao tác cua ngăn xêp ......................................................................................... 6

2.1.3 Vi du về hoạt động cua một stack ............................................................................. 7

2.1.4 Cài đăt stack băng mảng ............................................................................................ 7

2.1.5 Ưng dung cua stack ................................................................................................. 10

2.2. Hàng đợi - Queue .......................................................................................................... 12

2.2.1 Khái niệm ................................................................................................................ 12

2.2.2 Các thao tác cơ bản cua một hàng đợi ..................................................................... 13

2.2.3 Cài đăt hàng đợi sư dung mảng ............................................................................... 13

2.2.4 Vi du về hoạt động cua hàng đợi vơi cài đăt băng mảng vong tron ........................ 16

2.2.5 Ưng dung cua hang đơi ........................................................................................... 16

2.3. Danh sach liên kêt – Linked list .................................................................................... 17

2.3.1 Đinh nghia ............................................................................................................... 17

2.3.2 Các thao tac trên danh sach liên kêt. ....................................................................... 17

2.3.3 Cài đăt danh sách liên kêt sư dung con tro .............................................................. 18

2.3.4 Các kiểu danh sách liên kêt khác ............................................................................. 25

2.3.5 Môt sô vi du sƣ dung câu truc danh sach liên kêt .................................................... 26

2.3.6. Cài đăt stack và queue băng con tro ....................................................................... 26

2.4. Bài tập áp dung ............................................................................................................. 26

CHƢƠNG 3. CÂY (TREE). ..................................................................................................... 28

3.1. Đinh nghia ..................................................................................................................... 28

3.1.1. Đô thi (Graph) ........................................................................................................ 28

3.1.2. Cây (tree) ................................................................................................................ 29

3.3. Cây tim kiêm nhi phân (Binary Search Tree - BST) .................................................... 31

3.3.1. Đinh nghia .............................................................................................................. 31

3.3.2. Khơi tao cây rông ................................................................................................... 32

3.3.3. Chen thêm một nút mơi vào cây ............................................................................. 32

3.3.4. Xoa bo khoi cây một nút ........................................................................................ 33

3.3.5. Tim kiêm trên cây ................................................................................................... 34

3.3.6. Duyêt cây ................................................................................................................ 35

3.3.7. Cài đăt cây BST ...................................................................................................... 36

3.4.Cây cân băng – AVL ..................................................................................................... 39

CHƢƠNG 4. BẢNG BĂM (HASH TABLE) .......................................................................... 54

4. 1. Đinh nghia bảng băm ................................................................................................... 54

4.1.1.Đinh nghia : ............................................................................................................. 54

4.1.2.Kich thƣơc cua bảng băm : ...................................................................................... 55

4.1.3. Phân loại : ............................................................................................................... 55

4.1.4.Các phép toán trên bảng băm : ................................................................................ 57

4.2.Hàm băm và các loại hàm băm : .................................................................................... 57

4.2.1.Hàm băm (Hash Function): ..................................................................................... 57

4.2.2.Một số loại hàm băm : ............................................................................................. 58

Page 3: Dhhh ctdlgt bai giang cau truc du lieu

ii

4.3.Xung đột và cách xư lý xung đột ................................................................................... 61

4.3.1. Đinh nghia : ............................................................................................................ 61

4.3.2.Hệ số tải (Load Factor - ) : .................................................................................... 61

4.3.3.Một số phƣơng pháp xư lý xung đột : ..................................................................... 61

4.3.4. Đánh giá : ............................................................................................................... 71

4.4.4.Kêt luận : ..................................................................................................................... 72

4.5. Bài tập áp dung ............................................................................................................. 72

TÀI LIỆU THAM KHẢO. ....................................................................................................... 75

Page 4: Dhhh ctdlgt bai giang cau truc du lieu

iii

Tên học phần: Cấu trúc dữ liệu Loại học phần: 2

Bộ môn phụ trách giảng dạy: Khoa học Máy tinh Khoa phụ trách: CNTT

Mã học phần: 17207 Tổng số TC: 3

TS tiêt Lý thuyêt Thực hành/Xemina Tự học Bài tập lơn Đô án môn học

60 30 30 0 0 0

Điều kiện tiên quyết:

Sinh viên phải học xong các học phần sau mơi đƣợc đăng ký học phần này:

Toán cao cấp, Toán rời rạc, Ngôn ngữ C, Tin học đại cƣơng.

Mục tiêu của học phần:

Cung cấp kiên thức và ren luyện kỹ năng thực hành cấu trúc dữ liệu cho sinh viên.

Nội dung chủ yếu

- Những vấn đề cơ bản về cấu trúc dữ liệu;

- Các cấu trúc dữ liệu cơ bản

- Danh sách liên kêt;

- Ngăn xêp, hàng đợi;

- Cấu trúc cây;

- Bảng băm, ...

Nội dung chi tiết của học phần:

TÊN CHƢƠNG MỤC

PHÂN PHỐI SỐ TIẾT

TS LT TH/Xemina BT KT

Chƣơng I : Khái niệm liên quan đến CTDL 2 2 0

1.1. Giải thuật và cấu trúc dữ liệu.

1.2. Giải thuật và các vấn đề liên quan.

1.3. Ngôn ngữ diễn đạt giải thuật.

1.4. Kiểu dữ liệu, cấu trúc dữ liệu, kiểu dữ liệu trừu

tƣợng.

Chƣơng II : Các kiểu dữ liệu trừu tƣợng cơ bản 12 6 6

2.1. Danh sách

2.1.1. Khái niệm danh sách

2.1.2. Các phép toán trên danh sách

2.1.3. Cài đăt danh sách

2.1.4. Các dạng danh sách liên kêt (DSLK): DSLK

đơn, vong, kép, …

2.2. Ngăn xêp (stack)

2.2.1. Khái niệm

2.2.2. Cài đăt ngăn xêp bởi mảng, DSLK

2.2.3. Ưng dung

2.3. Hàng đợi (queue)

2.3.1. Khái niệm

2.3.2. Cài đăt hàng đợi bởi mảng, DSLK

2.3.3. Ưng dung

2.4. Bài tập áp dung

Chƣơng III: Cây (tree). 18 9 8 1

3.1. Khái niệm.

3.2. Cây tổng quát.

Page 5: Dhhh ctdlgt bai giang cau truc du lieu

iv

TÊN CHƢƠNG MỤC

PHÂN PHỐI SỐ TIẾT

TS LT TH/Xemina BT KT

3.2.1. Biểu diễn cây tổng quát.

3.2.2. Duyệt cây tổng quát.

3.2.3. Vài vi du áp dung.

3.3. Cây nhi phân.

3.3.1. Đinh nghia và tinh chất

3.3.2. Lƣu trữ cây.

3.3.3. Duyệt cây.

3.3.4. Cây nhi phân nối vong.

3.4. Các phép toán thực hiện trên cây nhi phân.

3.4.1. Dựng cây

3.4.2. Duyệt cây để tim kiêm

3.4.3. Sắp xêp cây nhi phân

3.5. Cây tim kiêm nhi phân (binary search tree)

3.5.1. Khái niệm, cài đăt.

3.5.2. Cây AVL

3.6. Bài tập

Chƣơng IV: Bảng băm (hash table) 14 7 6 1

4.1. Khái niệm

4.2. Các loại hàm băm

4.3. Các phƣơng pháp giải quyêt xung đột

4.4. Đánh giá hiệu quả các phƣơng pháp băm

4.5. Bài tập áp dung

Nhiệm vụ của sinh viên :

Tham dự các buổi thuyêt trinh cua giáo viên, tự học, tự làm bài tập do giáo viên giao,

tham dự các bài kiểm tra đinh kỳ và cuối kỳ.

Tài liệu học tập :

1. Đinh Mạnh Tƣờng, Cấu trúc dữ liệu và thuật toán, Nhà xuất bản ĐH QG Hà Nội,

2004.

2. Đỗ Xuân Lôi, Cấu trúc dữ liệu và giải thuật, Nhà xuất bản ĐH QG Hà Nội, 2004.

3. Robert Sedgewick, Cẩm nang thuật toán, NXB Khoa học kỹ thuật, 2000.

Hình thức và tiêu chuẩn đánh giá sinh viên:

- Hinh thức thi cuối kỳ : Thi viêt.

- Sinh viên phải đảm bảo các điều kiện theo Quy chê cua Nhà trƣờng và cua Bộ

Thang điểm: Thang điểm chữ A, B, C, D, F

Điểm đánh giá học phần: Z = 0,3X + 0,7Y.

Bài giảng này là tài liệu chính thức và thống nhất cua Bộ môn Khoa học máy tinh,

Khoa Công nghệ thông tin và đƣợc dùng để giảng dạy cho sinh viên.

Ngày phê duyệt: / /20

Trƣởng Bộ môn: ThS. Nguyễn Hữu Tuân (ký và ghi rõ họ tên)

Page 6: Dhhh ctdlgt bai giang cau truc du lieu

1

CHƢƠNG 1. CÁC KHÁI NIỆM MỞ ĐẦU

1.1. Giải thuật và cấu trúc dữ liệu.

Ðể giải một bài toán trong thực tê băng máy tinh ta phải bắt đầu từ việc xác đinh bài

toán. Nhiều thời gian và công sức bo ra để xác đinh bài toán cần giải quyêt, tức là phải trả lời

rõ ràng câu hoi "phải làm gi?" sau đo là "làm nhƣ thê nào?". Thông thƣờng, khi khởi đầu, hầu

hêt các bài toán là không đon giản, không rõ ràng. Ðể giảm bơt sự phức tạp cua bài toán thực

tê, ta phải hinh thức hoa no, nghia là phát biểu lại bài toán thực tê thành một bài toán hinh

thức (hay con gọi là mô hinh toán). Co thể co rất nhiều bài toán thực tê co cùng một mô hình

toán.

Vi du : Tô màu bản đô thê giơi.

Ta cần phải tô màu cho các nƣơc trên bản đô thê giơi. Trong đo mỗi nƣơc đều đƣợc tô

một màu và hai nƣơc láng giềng (cùng biên giơi) thi phải đƣợc tô băng hai màu khác nhau.

Hãy tim một phƣơng án tô màu sao cho số màu sư dung là it nhất.

Ta co thể xem mỗi nƣơc trên bản đô thê giơi là một đỉnh cua đô thi, hai nƣơc láng

giềng cua nhau thi hai đỉnh ứng vơi no đƣợc nối vơi nhau băng một cạnh. Bài toán lúc này trở

thành bài toán tô màu cho đô thi nhƣ sau: Mỗi đỉnh đều phải đƣợc tô màu, hai đỉnh co cạnh

nối thi phải tô băng hai màu khác nhau và ta cần tim một phƣơng án tô màu sao cho số màu

đƣợc sư dung là it nhất.

Ðối vơi một bài toán đã đƣợc hinh thức hoá, chúng ta co thể tim kiêm cách giải trong

thuật ngữ cua mô hinh đo và xác đinh co hay không một chƣong trinh co sẵn để giải. Nêu

không co một chƣơng trinh nhƣ vậy thi it nhất chúng ta cũng co thể tim đƣợc những gi đã biêt

về mô hinh và dùng các tinh chất cua mô hinh để xây dựng một giải thuật tốt.

Khi đã co mô hinh thich hợp cho một bài toán ta cần cố gắng tim cách giải quyêt bài

toán trong mô hinh đo. Khởi đầu là tim một giải thuật, đo là một chƣỗi hữu hạn các chỉ thi

(instruction) mà mỗi chỉ thi co một ý nghia rõ ràng và thực hiện đƣợc trong một lƣợng thời

gian hữu hạn.

Nhƣng xét cho cùng, giải thuật chỉ phản ánh các phép xư lý, con đoi tƣợng để xư lý

trong máy tinh chinh là dữ liệu (data ), chúng biểu diễn các thông tin cần thiêt cho bài toán:

các dữ liệu vào, các dữ liệu ra, dữ liệu trung gian, … Không thể noi tơi giải thuật mà không

nghi tơi: giải thuật đo đƣợc tác động trên dữ liệu nào, con xét tơi dữ liệu thi phải biêt dữ liệu

ấy cần đƣợc giải thuật gi tác động để đƣa ra kêt quả mong muốn.. Nhƣ vậy, giữa cấu trúc dữ

liệu và giải thuật co mối liên quan mật thiêt vơi nhau.

1.2. Cấu trúc dữ liệu và các vấn đề liên quan.

Trong một bài toán, dữ liệu bao gôm một tập các phần tư cơ sở, đƣợc gọi là dữ liệu

nguyên tư. Dữ liệu nguyên tư co thể là một chữ số, một ký tự, … cũng co thể là một số, một

xâu, … tùy vào bài toán. Trên cơ sở các dữ liệu nguyên tư, các cung cách khả di theo đo lien

kêt chúng lại vơi nhau, sẽ đãn đên các cấu trúc dữ liệu khác nhau.

Lựa chọn một cấu trúc dữ liệu thich hợp để tổ chức dữ liệu vào và trên cơ sở đo xây

dựng đƣợc giải thuật xư lý hữu hiệu đƣa tơi kêt quả mong muốn cho bài toán (dữ liệu ra), là

một khâu quan trọng.

Cách biểu diễn một cấu trúc dữ liệu trong bộ nhơ đƣợc gọi là cấu trúc lƣu trữ. Đây chinh

là cách cài đăt cấu trúc ấy trên máy tinh và trên cơ sở các cấu trúc lƣu trữ này mà thực hiện

các phép xư lý. Co thể co nhiều cấu trúc lƣu trữ khác nhau cho cùng một cấu trúc dữ liệu và

ngƣợc lại.

Khi đề cập tơi cấu trúc lƣu trũ, cần phân biệt: cấu trúc lƣu trữ tƣơng ứng vơi bộ nhơ

trong – lƣu trữ trong; cấu trúc lƣu trữ ứng vơi bộ nhơ ngoài – lƣu trữ ngoài. Chúng co đăc

điểm và cách xư lý riêng.

Page 7: Dhhh ctdlgt bai giang cau truc du lieu

2

1.3. Ngôn ngữ diễn đạt giải thuật.

Việc sư dung một ngôn ngữ lập trinh bậc cao để diễn đạt giải thuật, nhƣ Pascal, C, C++,

… sẽ găp một số hạn chê sau:

- Phải luôn tuân thu các quy tắc chăt chẽ về cú pháp cua ngôn ngữ khiên cho việc trinh

bày về giải thuật và cấu trúc dữ liệu co thiên hƣơng năng nề, go bo.

- Phải phu thuộc vào cấu trúc dữ liệu tiền đinh cua ngôn ngữ nên co lúc không thể hiện

đƣợc đầy đu các ý về cấu trúc mà ta muỗn biểu đạt

Một khi đã co mô hình thích hợp cho bài toán, ta cần hinh thức hoá một giải thuật, một

cấu trúc dữ liệu trong thuật ngữ cua mô hinh đo. Khởi đầu là viêt những mệnh đề tổng quát

rôi tinh chê dần thành những chuỗi mệnh đề cu thể hơn, cuối cùng là các chỉ thi thich hợp

trong một ngôn ngữ lập trinh.

Ở bƣơc này, noi chung, ta co một giải thuật, một cấu trúc dữ liệu tƣơng đoi rõ ràng, no

gần giống nhƣ một chƣơng trinh đƣợc viêt trong ngôn ngữ lập trinh, nhƣng no không phải là

một chƣơng trinh chạy đƣợc vi trong khi viêt giải thuật ta không chú trọng năng đên cú pháp

cua ngôn ngữ và các kiểu dữ liệu con ở mức trừu tƣợng chứ không phải là các khai báo cài đăt

kiểu trong ngôn ngữ lập trinh.

Chẳng hạn vơi giải thuật tô màu đô thi GREEDY, giả sư đô thi là G, giải thuật sẽ xác

đinh một tập hợp Newclr các đỉnh cua G đƣợc tô cùng một màu, mà ta gọi là màu mơi C ở

trên. Ðể tiên hành tô màu hoàn tất cho đô thi G thi giải thuật này phải đƣợc gọi lăp lại cho đên

khi toàn thể các đỉnh đều đƣợc tô màu.

void GREEDY ( GRAPH *G, SET *Newclr )

{

Newclr = ; /*1*/

for (mỗi đỉnh v chƣa tô màu cua G) /*2*/

if (v không đƣợc nối vơi một đỉnh nào trong Newclr) /*3*/

{

đánh dấu v đã đƣợc tô màu; /*4*/

thêm v vào Newclr; /*5*/ }

} Trong thu tuc băng ngôn ngữ giả này chúng ta đã dùng một số từ khoá cua ngôn ngữ C

xen lẫn các mệnh đề tiêng Việt. Ðiều đăc biệt nữa là ta dùng các kiểu GRAPH, SET co vẻ xa

lạ, chúng là các "kiểu dữ liệu trừu tƣợng" mà sau này chúng ta sẽ viêt băng các khai báo thích

hợp trong ngôn ngữ lập trinh cu thể. Di nhiên, để cài đăt thu tuc này ta phải cu thể hoá dần

những mệnh đề băng tiêng Việt ở trên cho đên khi mỗi mệnh đề tƣơng ứng vơi một doạn mã

thich hợp cua ngôn ngữ lập trinh. Chẳng hạn mệnh đề if ở /*3*/ co thể chi tiêt hoá hơn nữa

nhƣ sau:

void GREEDY ( GRAPH *G, SET *Newclr )

{

Newclr= ; /*1*/

for (mỗi đỉnh v chƣa tô màu cua G) /*2*/

{

int found=0; /*3.1*/

for (mỗi đỉnh w trong Newclr) /*3.2*/

if (co cạnh nối giữa v và w) /*3.3*/

found=1; /*3.4*/

if (found==0)/*3.5*/

{

đánh dấu v đã đƣợc tô màu; /*4*/

thêm v vào Newclr; /*5*/

}

Page 8: Dhhh ctdlgt bai giang cau truc du lieu

3

}

}

GRAPH và SET ta coi nhƣ tập hợp. Co nhiều cách để biểu diễn tập hợp trong ngôn ngữ

lập trinh, để đơn giản ta xem các tập hợp nhƣ là một danh sách (LIST) các số nguyên biểu

diễn chỉ số cua các đỉnh và kêt thúc băng một giá tri đăc biệt NULL. Vơi những qui ƣơc nhƣ

vậy ta co thể tinh chê giải thuật GREEDY một bƣơc nữa nhƣ sau:

void GREEDY ( GRAPH *G, LIST *Newclr )

{

int found;

int v,w ;

Newclr= ;

v= đỉnh đầu tiên chƣa đƣợc tô màu trong G;

while (v<>null)

{

found=0;

w=đỉnh đầu tiên trong newclr;

while( w<>null) && (found=0)

{

if ( co cạnh nối giữa v và w )

found=1;

else w= đỉnh kê tiêp trong newclr;

}

if (found==0 )

{

Ðánh dấu v đã đƣợc tô màu;

Thêm v vào Newclr;

}

v= đỉnh chƣa tô màu kê tiêp trong G;

}

}

1.4. Kiểu dữ liệu, cấu trúc dữ liệu, kiểu dữ liệu trừu tƣợng

Khái niệm trừu tƣợng hóa

Trong tin học, trừu tƣợng hoa nghia là đơn giản hoa, làm cho no sáng sua hơn và dễ hiểu

hơn. Cu thể trừu tƣợng hoa là che di những chi tiêt, làm nổi bật cái tổng thể. Trừu tƣợng hoa

co thể thực hiện trên hai khia cạnh là trừu tƣợng hoa dữ liệu và trừu tƣợng hoa chƣơng trinh.

Trừu tƣợng hóa chƣơng trình

Trừu tƣợng hoa chƣơng trinh là sự đinh nghia các chƣơng trinh con để tạo ra các phép

toán trừu tƣợng (sự tổng quát hoa cua các phép toán nguyên thuy). Chẳng hạn ta co thể tạo ra

một chƣơng trinh con Matrix_Mult để thực hiện phép toán nhân hai ma trận. Sau khi

Matrix_mult đã đƣợc tạo ra, ta co thể dùng no nhƣ một phép toán nguyên thuy (chẳng hạn

phép cộng hai số).

Trừu tƣợng hoa chƣơng trinh cho phép phân chia chƣơng trinh thành các chƣơng trinh

con. Sự phân chia này sẽ che dấu tất cả các lệnh cài đăt chi tiêt trong các chƣơng trinh con. Ở

cấp độ chƣơng trinh chinh, ta chỉ thấy lời gọi các chƣơng trinh con và điều này đƣợc gọi là sự

bao gói.

Vi du nhƣ một chƣơng trinh quản lý sinh viên đƣợc viêt băng trừu tƣợng hoa co thể

là:

void main()

{

Nhap(Lop);

Page 9: Dhhh ctdlgt bai giang cau truc du lieu

4

Xu_ly (Lop);

Xuat (Lop);

}

Trong chƣơng trinh trên, Nhap, Xu_ly, Xuat là các phép toán trừu tƣợng. Chúng che dấu

bên trong rất nhiều lệnh phức tạp mà ở cấp độ chƣơng trinh chinh ta không nhin thấy đƣợc.

Con Lop là một biên thuộc kiểu dữ liệu trừu tƣợng mà ta sẽ xét sau.

Chƣơng trinh đƣợc viêt theo cách gọi các phép toán trừu tƣợng co lệ thuộc vào cách

cài đăt kiểu dữ liệu không?

Trừu tƣợng hóa dữ liệu

Trừu tƣợng hoa dữ liệu là đinh nghia các kiểu dữ liệu trừu tƣợng

Một kiểu dữ liệu trừu tƣợng là một mô hình toán học cùng với một tập hợp các phép

toán (operator) trừu tƣợng đƣợc định nghĩa trên mô hình đó. Vi du tập hợp số nguyên cùng

vơi các phép toán hợp, giao, hiệu là một kiểu dữ liệu trừu tƣợng.

Trong một ADT các phép toán co thể thực hiện trên các đoi tƣợng (toán hạng) không chỉ

thuộc ADT đo, cũng nhƣ kêt quả không nhất thiêt phải thuộc ADT. Tuy nhiên, phải co it nhất

một toán hạng hoăc kêt quả phải thuộc ADT đang xét.

ADT là sự tổng quát hoá của các kiểu dữ liệu nguyên thƣỷ.

Ðể minh hoạ ta co thể xét bản phác thảo cuối cùng cua thu tuc GREEDY. Ta đã dùng

một danh sách (LIST) các số nguyên và các phép toán trên danh sách newclr là:

- Tạo một danh sách rỗng.

- Lấy phần tư đầu tiên trong danh sách và trả về giá tri null nêu danh sách rỗng.

- Lấy phần tư kê tiêp trong danh sách và trả về giá tri null nêu không con phần tư kê

tiêp.

- h.

Nêu chúng ta viêt các chƣơng trinh con thực hiện các phép toán này, thi ta dễ dàng thay

các mệnh đề hinh thức trong giải thuật băng các câu lệnh đơn giản

Câu lệnh Mệnh đề hinh thức

MAKENULL(newclr) newclr=;

w=FIRST(newclr) w=phần tư đầu tiên trong newclr

w=NEXT(w,newclr) w=phần tư kê tiêp trong newclr

INSERT( v,newclr) Thêm v vào newclr

Ðiều này cho thấy sự thuận lợi cua ADT, đo là ta co thể đinh nghia một kiểu dữ liệu tuỳ

ý cùng vơi các phép toán cần thiêt trên no rôi chúng ta dùng nhƣ là các đoi tƣợng nguyên

thuỷ. Hơn nữa chúng ta co thể cài đăt một ADT băng bất kỳ cách nào, chƣơng trinh dùng

chúng cũng không thay đổi, chỉ co các chƣơng trinh con biểu diễn cho các phép toán cua

ADT là thay đổi.

Cài đặt ADT là sự thể hiện các phép toán mong muốn (các phép toán trừu tƣợng) thành

các câu lệnh cua ngôn ngữ lập trinh, bao gôm các khai báo thich hợp và các thu tuc thực hiện

các phép toán trừu tƣợng. Ðể cài đăt ta chọn một cấu trúc dữ liệu thich hợp co trong ngôn

ngữ lập trinh hoăc là một cấu trúc dữ liệu phức hợp đƣợc xây dựng lên từ các kiểu dữ liệu cơ

bản cua ngôn ngữ lập trinh.

Sự khác nhau giữa kiểu dữ liệu và kiểu dữ liệu trừu tƣợng là gi?

Măc dù các thuật ngữ kiểu dữ liệu (hay kiểu - data type), cấu trúc dữ liệu (data

Page 10: Dhhh ctdlgt bai giang cau truc du lieu

5

structure), kiểu dữ liệu trừu tƣợng (abstract data type) nghe nhƣ nhau, nhƣng chúng co ý

nghia rất khác nhau.

Kiểu dữ liệu là một tập hợp các giá tri và một tập hợp các phép toán trên các giá tri đo.

Vi du kiểu Boolean là một tập hợp co 2 giá tri TRUE, FALSE và các phép toán trên no nhƣ

OR, AND, NOT …. Kiểu Integer là tập hợp các số nguyên co giá tri từ -32768 đên 32767

cùng các phép toán cộng, trừ, nhân, chia, Div, Mod…

Kiểu dữ liệu co hai loại là kiểu dữ liệu sơ cấp và kiểu dữ liệu co cấu trúc hay con gọi là

cấu trúc dữ liệu.

Kiểu dữ liệu sơ cấp là kiểu dữ liệu mà giá tri dữ liệu cua no là đơn nhất. Vi du: kiểu

Boolean, Integer….

Kiểu dữ liệu co cấu trúc hay con gọi là cấu trúc dữ liệu là kiểu dữ liệu mà giá tri dữ liệu

cua no là sự kêt hợp cua các giá tri khác. Vi du: ARRAY là một cấu trúc dữ liệu.

Một kiểu dữ liệu trừu tƣợng là một mô hinh toán học cùng vơi một tập hợp các phép

toán trên no. Co thể noi kiểu dữ liệu trừu tƣợng là một kiểu dữ liệu do chúng ta đinh nghia ở

mức khái niệm (conceptual), no chƣa đƣợc cài đăt cu thể băng một ngôn ngữ lập trinh.

Khi cài đăt một kiểu dữ liệu trừu tƣợng trên một ngôn ngữ lập trinh cu thể, chúng ta phải

thực hiện hai nhiệm vu:

1. Biểu diễn kiểu dữ liệu trừu tƣợng băng một cấu trúc dữ liệu hoăc một kiểu dữ liệu

trừu tƣợng khác đã đƣợc cài đăt.

2. Viêt các chƣơng trinh con thực hiện các phép toán trên kiểu dữ liệu trừu tƣợng mà ta

thƣờng gọi là cài đăt các phép toán.

Bài tập:

1. Tim hiểu các kiểu dữ liệu cơ sở trong C

2. Tim hiểu các cấu trúc dữ liệu mảng, cấu trúc trong C và thực hiện một số bài tập cơ

bản nhƣ nhập, xuất

Page 11: Dhhh ctdlgt bai giang cau truc du lieu

6

CHƢƠNG 2. CÁC KIỂU DỮ LIỆU TRỪU TƢỢNG CƠ BẢN

2. 1. Ngăn xêp - Stack

2.1.1 Khái niệm

Khái niệm: Ngăn xêp (stack) là một tập hợp các phần tư (items) cùng kiểu đƣợc tổ

chƣc môt cach tuân tƣ (chinh vi thê một số tài liệu con đinh nghia ngăn xêp là một danh sách

tuyên tinh cac phân tƣ vơi cac thao tac truy câp han chê tơi cac phân tƣ cua danh sach đo)

trong đo phân tƣ đƣơc thêm vao cuôi cung cua tâp hơp se la phân tƣ bi loai bo đâu tiên khoi

tâp hơp. Các ngăn xêp thƣờng đƣợc gọi là các cấu trúc LIFO (Last In First Out).

Ví dụ vê ngăn xêp: Chông tai liêu cua môt công chƣc văn phong, chông đia … la cac

vi du về ngăn xêp.

Chú ý: Phân tƣ duy nhât co thê truy câp tơi cua môt ngăn xêp la phân tƣ mơi đƣơc

thêm vao gân đây nhât (theo thơi gian) cua ngăn xêp.

2.1.2 Các thao tác của ngăn xếp

Đối vơi một ngăn xêp chỉ co 2 thao tac cơ ban, thao tac thƣ nhât thƣc hiên thêm môt

phân tƣ vao stack goi la push, thao tac thƣ hai la đoc gia tri cua môt phân tƣ va loai bo no khoi

stack goi la pop.

Đê nhât quan vơi cac thƣ viên cai đăt câu truc stack chuân STL (và một số tài liệu

cũng phân chia nhƣ vậy), ta xac đinh cac thao tac đôi vơi môt stack gôm co:

1. Thao tac push(d) sẽ đăt phần tư d lên đinh cua stack.

2. Thao tac pop() loại bo phần tư ở đỉnh stack.

3. Thao tac top() sẽ trả về giá tri phần tư ở đỉnh stack.

4. Thao tac size() cho biêt sô phân tƣ hiên tai đang lƣu trong stack

Ngoài hai thao tác cơ bản trên chúng ta cân co môt sô thao tac phu trơ khac: chăng han

làm thê nào để biêt là một stack không co phần tư nào – tƣc la rông (empty) hay la đây (full)

tƣc la không thê thêm vao bât cƣ môt phân tƣ nao khac nƣa. Đê thƣc hiên điêu nay ngƣơi ta

thƣơng thêm hai thao tac tiên hanh kiêm tra la empty() và full().

Đê đam bao không xay ra tinh trang goi la stack overflow (tràn stack – không thê thêm

vào stack bất cứ phần tư nào) chúng ta co thể cho hàm push tra vê chăng han 1 trong trƣơng

hơp thƣc hiên thanh công va 0 nêu không thanh công.

Page 12: Dhhh ctdlgt bai giang cau truc du lieu

7

2.1.3 Ví dụ về hoạt động của một stack

Giả sư chúng ta co một stack kich thƣơc băng 3 (co thể chứa đƣợc tối đa 3 phân tƣ) và

các phần tư cua stack là các số nguyên trong khoảng từ -100 đên 100. Sau đây la minh hoa

các thao tác đối vơi stack và kêt quả thực hiện cua các thao tác đo.

Thao tac Nôi dung stack Kêt qua

Khơi tao ()

push(55) (55) 1

push(-7) (-7, 55) 1

push(16) (16, -7, 55) 1

pop (-7, 55) 16

push(-8) (-8, -7, 55) 1

push(23) (-8, -7, 55) 0

pop (-7, 55) -8

pop (55) -7

pop () 55

pop () 101

2.1.4 Cài đặt stack băng mảng

Câu truc dƣ liêu stack co thê cai đăt băng cach sƣ dung một mảng và một số nguyên

top_idx đê chƣa chi sô cua phân tƣ ơ đinh stack.

Ngăn xêp rông khi top_idx = -1 và đầy khi top_idx = n-1 trong đo n la kich thƣơc cua

mảng.

Khi thƣc hiên thao tac push chung ta tăng top_idx lên 1 và ghi dữ liệu vào vi tri tƣơng

ứng cua mảng.

Khi thƣc hiên thao tac pop chung ta chi viêc giam chi sô top_idx đi 1.

Vi du về ngăn xêp cài đăt băng mảng:

Giả sư chúng ta sư dung mảng E[0..4] để chứa các phần tư cua stack va biên top_idx

để lƣu chỉ số cua phần tư ở đỉnh stack. Trong bang sau côt cuôi cung kêt qua la gia tri tra vê

cua việc gọi hàm.

Thao tac top_idx E[0] E[1] E[2] E[3] E[4] Kêt qua

khơi tao -1 ? ? ? ? ?

push(55) 0 55 ? ? ? ? 1

push(-7) 1 55 -7 ? ? ? 1

push(16) 2 55 -7 16 ? ? 1

pop 1 55 -7 16 ? ? 16

push(-8) 2 55 -7 -8 ? ? 1

pop 1 55 -7 -8 ? ? -8

pop 0 55 -7 -8 ? ? -7

Chú ý răng trong minh họa này chúng ta thấy răng một số giá tri vẫn con trong mảng

nhƣng chung đƣơc xem nhƣ không co trong stack vi không co thao tac nao truy câp tơi chung .

Noi chúng thi phần tư E[i] đƣơc xem la rac nêu nhƣ i>top_idx. Tại sao chúng ta không xoa bo

các phần tư này (chăng han nhƣ đăt chung băng các giá tri măc đinh nào đo?).

Page 13: Dhhh ctdlgt bai giang cau truc du lieu

8

Cài đăt cua stack băng ngôn ngữ C nhƣ sau (áp dung stack cho bài toán chuyển số từ

cơ sô 10 sang cơ sô 2):

#include <stdio.h>

#include <stdlib.h>

const int MAX_ELEMENT = 100; // so phan tu toi da cua stack la 100

// khai bao stack chua cac so nguyen

typedef struct

{

int * data; // khai bao mang dong

int top_idx;

} stack;

// ham khoi tao stack rong

void init(stack *s);

void push(stack * s, int d);

void pop(stack *s);

int top(const stack *s);

int size(const stack *s);

int empty(const stack *s);

int full(const stack *s);

// ham giai phong bo nho danh cho stack

void clear(stack *s);

int main()

{

int n;

int bit;

stack s;

init(&s);

printf("Nhap so nguyen n = ");

scanf("%d", &n);

while(n)

{

push(&s, n%2);

n /= 2;

}

while(!empty(&s))

{

bit = top(&s);

pop(&s);

printf("%d", bit);

}

clear(&s);

return 0;

Page 14: Dhhh ctdlgt bai giang cau truc du lieu

9

}

void init(stack *s)

{

s->data = (int*)malloc(MAX_ELEMENT * sizeof(int));

s->top_idx = -1;

}

void clear(stack *s)

{

if(s->data != NULL)

free(s->data);

s->top_idx = -1;

}

void push(stack *s, int d)

{

s->data[++s->top_idx] = d;

}

void pop(stack *s)

{

s->top_idx --;

}

int top(const stack *s)

{

return s->data[s->top_idx];

}

int size(const stack *s)

{

return s->top_idx+1;

}

int empty(const stack * s)

{

return (s->top_idx==-1)?(1):(0);

}

int full(const stack * s)

{

return (s->top_idx==MAX_ELEMENT-1)?(1):(0);

}

Câu truc stack co thê cai đăt băng mang theo cac cach khac , hoăc cai đăt băng con tro

(chúng ta sẽ học vê phân cai đăt nay sau phân danh sach liên kêt).

Stack co thê đƣơc cai đăt băng ca mang va danh sach liên kêt vây khi nao chung ta sƣ

dung mảng và khi nào dùng danh sách liên kêt?

Danh sach liên kêt Mảng

Cƣ môi phân tƣ cua stack cân

co thêm 2 byte (1 con tro).

Xin câp phat môt vung nhơ co kich

thƣơc cô đinh va co thê môt phân

Page 15: Dhhh ctdlgt bai giang cau truc du lieu

10

Nêu kich thƣơc cua môt phân

tƣ lơn thi không đang kê

nhƣng nêu la kiêu int thi kich

thƣơc se tăng gâp đôi.

trong sô đo không bao giơ đƣơc dung

đên và nêu nhƣ kich thƣơc cua một

phân tƣ lơn thi vung nhơ lang phi nay

cũng rất lơn.

Không co giơi han vê sô phân

tƣ cua stack.

Kich thƣơc tối đa cua stack đƣợc xác

đinh ngay khi no đƣơc tao ra.

2.1.5 Ưng dụng của stack

Ví dụ 1

Stack co thê dung đê kiêm tra cac căp ky hiêu cân băng trong môt chƣơng trinh (chăng

hạn {}, (), []).

Vi du {()} và {()({})} là các biểu thức đúng con {((} và {(}) không phai la cac biêu

thƣc đung.

Chúng ta thấy răng nêu nhƣ một biểu thức là đúng thi trong quá trinh đọc các ký hiệu

cua biểu thức đo nêu chúng ta thấy một ký hiệu đong (chăng han ), } hay ]) thi ký hiệu này

phải khơp vơi ký hiệu mở đƣợc đọc thấy gần nhất (theo thơi gian), và khi đo việc sư dung

stack cho cac bai toan nhƣ thê nay la hoan toan hơp ly.

Chúng ta co thể sư dung thuật toán sau đây:

while not end of Input

S = next sumbol

if(s is opening symbol)

push(s)

else // s la dâu đong ngoăc

if(stack.empty)

Báo lỗi

else

R = stack.top()

stack.pop()

if(!match(s,r))

Báo lỗi

If(!stack.empty())

Báo lỗi

Vi du:

1. Input: {()}

s = {, push{,

s = (, push (,

s = ), r = pop = (, r,s match

s = }, r = pop = {, r,s match

End of Input, stack rông => biêu thƣc la đung.

Vi du: Input = { ( ) ( { ) } } (sai)

Input = { ( { } ) { } ( ) }

Page 16: Dhhh ctdlgt bai giang cau truc du lieu

11

Ví dụ 2

Sƣ dung Stack đê chuyên đôi cac dạng biểu thức đại số. Trong vi du nay chung ta se

xem xet cac thuât toan sƣ dung stack đê chuyên đôi tƣ biêu đai sô ơ dang trung tô (dạng thông

thƣơng, hay con goi la infix notation) thành các biểu thức ở dạng tiền tố (prefix notation, hay

con gọi là biểu thức Balan – Polish notation) và biểu thức hậu tố (postfix notation, hay biêu

thƣc Balan ngƣơc).

Biêu thƣc đai sô la môt sƣ kêt hơp đung đăn giƣa cac toan hang (operand) và các toán

tƣ (operator). Toán hạng là các số liệu co thể thực hiện đƣợc các thao tác tinh toán toán học.

Toán hạng cũng co thể là các biên số x, y, z hay cac hăng sô. Toán tư là một ký hiệu chỉ ra

thao tac tinh toan toan hoc hay logic giƣa cac toan hang, chăng han nhƣ cac toan hang +, -, *,

/, ^ (toán tư mũ hoa). Viêc đinh nghia cac biêu thƣc đai sô môt cach chăt che vê ly thuyêt la

nhƣ sau:

Môt toan hang la môt biêu thƣc hơp lê

Nêu express ion1 và expression2 là hai biểu thức hợp lệ và op là một toán tư thi

môt kêt hơp hơp lê giƣa biêu thƣc expression 1 vơi biêu thƣc expression 2 sƣ dung

toán tư op sẽ cho ta một biểu thức đại số hợp lệ

Theo đinh nghia trên ta co x + y*z la môt biêu thƣc đai sô hơp lê nhƣng *x y z+

không phai la môt biêu thƣc hơp lê. Trong cac biêu thƣc đai sô ngƣơi ta co thê sƣ dung cac

dâu đong va mơ ngoăc.

Môt biêu thƣc đai sô co thê đƣơc biêu diên băng 3 dạng khác nhau.

Biêu thƣc trung tô (infix notation): đây la dang biêu diên phô biên nhât cua cac biêu

thƣc đai sô, trong cac biêu thƣc trung tô, toán tư năm giữa các toán hạng. Vi du nhƣ 2 + 3 * (7

– 3)

Biêu thƣc tiên tô (prefix notation): dạng biểu diễn này do nhà toán học ngƣời Balan

Jan Lukasiewicz đƣa ra vao nhƣng năm 1920. Trong dang biêu diên nay, toán tư đứng trƣơc

các toán hạng. Vi du nhƣ + * 2 3 7

Biêu thƣc hâu tô (postfix notation): hay con goi la biêu thƣc Balan ngƣơc, toán tư

đƣng sau cac toan hang. Vi du nhƣ 2 3 – 7 *.

Câu hoi ma chung ta co thê đăt ra ngay lâp tƣc ơ đây la : tại sao lại cần sư dung tơi các

dạng biểu diễn tiền tố và hậu tố trong khi chúng ta vẫn quen và vẫn sư dung đƣợc các biểu

thƣc ơ dang trung tô.

Lý do là các biểu thức trung tố không đơn giản và dễ dàng khi tinh giá tri cua chúng

nhƣ chung ta vân tƣơng. Đê tinh gia tri cua môt biêu thƣc trung tô chung ta cân tinh tơi đô ƣu

tiên cua cac toan tƣ cua biêu thƣc va cac qui tăc kêt hơp. Độ ƣu tiên cua các toán tư và các qui

tăc kêt hơp se quyêt đinh tơi qua trinh tinh toan gia tri cua môt biêu thức trung tố.

Chúng ta co bảng độ ƣu tiên cua các toán tư thƣờng găp nhƣ sau :

Toán tư Độ ƣu tiên

()

+ (môt ngôi), - (môt ngôi), !

+ (công), - (trƣ)

<, <=, >, >=

==, !=

Page 17: Dhhh ctdlgt bai giang cau truc du lieu

12

&&

||

Khi đa biêt đô ƣu tiên toan tƣ chung ta co thể tinh toán các biểu thức chẳng hạn 2 + 4

* 5 sẽ băng 22 vi trƣơc hêt cần lấy 4 nhân vơi 5, sau đo kêt qua nhân đƣơc đem công vơi 2 vi

phép nhân co độ ƣu tiên cao hơn phép cộng. Nhƣng vơi biêu thƣc 2*7/3 thi ta không thê tinh

đƣơc vi phep nhân va phep chia co đô ƣu tiên băng nhau, khi đo cân sƣ dung tơi cac qui tăc

kêt hơp cac toan tƣ. Qui tăc kêt hơp se cho chung ta biêt thƣ tƣ thƣc hiên cac toan tƣu co cung

đô ƣu tiên. Chăng han chung ta co qui tăc kêt hơp trai, nghia là các toán tư cùng độ ƣu tiên sẽ

đƣơc thƣc hiên tƣ trai qua phai, hay qui tăc kêt hơp phai. Nêu theo qui tăc kêt hơp trai thi

phép toán trên sẽ co kêt quả là 4 (lây kêt qua nguyên).

Vi những vấn đề liên quan tơi độ ƣu tiên toán tư và các qui luật kêt hợp nên chúng ta

thƣơng sƣ dung cac dang biêu diên tiên tô va hâu tô trong viêc tinh toan cac biêu thƣc đai sô .

Cả biểu thức hậu tố và tiền tố đều co một ƣu điểm hơn so vơi cách biểu diễn trung tố:

đo la khi tinh toan cac biêu thƣc ơ dang tiên tô va hâu tô chung ta không cân phai đê y tơi đô

ƣu tiên toan tƣ va cac luât kêt hơp. Tuy nhiên so vơi biêu thƣc trung tô, các biểu thức tiền tố

và hậu tố kho hiểu hơn và vi thê nên khi biểu diễn chúng ta vẫn sư dung dạng biểu thức trung

tô, nhƣng khi tinh toan se sƣ dung dang tiên tô hoăc hâu tô, điêu nay yêu câu cân co các thuật

toán chuyển đổi từ dạng trung tố sang dạng tiền tố hoăc hậu tố .

Viêc chuyên đôi cua chung ta co thê thƣc hiên băng cach sƣ dung câu truc stack hoă

cây biêu thƣc (chƣơng 5), phân nay chung ta se chi xem xet cac thuât toan sƣ dung stack vi

thuât toan sƣ dung cây biêu thƣc kha phƣc tap.

Thuât toan chuyên đôi biêu thƣc dang trung tô thanh dang hâu tô sƣ dung stack .

Ví dụ 3

Phân tich đô ƣu tiên toan tƣ

Chúng ta co thể sư dung câu truc stack đê phân tich va lƣơng gia cac biêu thƣc toan

học kiểu nhƣ:

5 * (( (9+8) * (4 * 6) ) + 7)

Trƣơc hêt chung ta chuyên chung thanh dang hâu tô (postfix):

5 8 9 + 4 6 * * 7 + *

Sau đo sƣ dung môt stack đê thƣc hiên viêc tinh toan gia tri cua biêu thƣc hâu tô nhân

đƣơc.

Ngoài ra các stack con co thể dùng để cài đăt các thuật toán đệ qui và khư đệ qui các

cài đăt thuật toán.

2.2. Hàng đợi - Queue

2.2.1 Khái niệm

Hàng đợi là một tập hợp các phần tư cùng kiểu đƣợc tổ chức một cách tuần tự (tuyên

tinh) trong đo phân tƣ đƣơc thêm vao đâu tiên se la phân tƣ đƣơc loai bo đâu tiên khoi hang

đơi. Các hàng đợi thƣờng đƣợc gọi là các cấu trúc FIFO (First In First Out).

Các vi du thực tê về hàng đợi mà chúng ta co thể thấy trong cuộc sống hàng ngày đo là

đoan ngƣơi xêp hang chơ mua ve tâu, danh sach cac cuôc hen cua môt giam đôc, danh sach

các công viêc cân lam cua môt ngƣơi …

Page 18: Dhhh ctdlgt bai giang cau truc du lieu

13

Cũng co thể đinh nghia hàng đợi là một danh sách tuyên tinh các phần tư giống nhau

vơi môt sô thao tac han chê tơi cac phân tƣ trên danh sach đo.

2.2.2 Các thao tác cơ bản của một hàng đơi

Tƣơng tƣ nhƣ câu truc ngăn xêp, chúng ta đinh nghia các thao tác trên hàng đợi tuân

theo cai đăt chuân cua hang đơi trong thƣ viên STL va cac tai liêu khac , gôm co:

1. push(d): thêm phân tƣ d vao vi tri ơ cuôi hang đơi.

2. pop(): loại bo phần tư ở đầu hàng đợi.

3. front(): trả về giá tri phần tư ở đầu hàng đợi.

4. back(): trả về giá tri phần tư ở cuối hàng đợi.

5. size(): trả về số phần tư đang ở trong hàng đợi.

6. empty(): kiêm tra hang đơi co rông hay không.

7. full(): kiêm tra hang đơi đây (chỉ cần khi cài đăt hàng đợi băng mảng).

Vi du:

Thao tac Nôi dung Giá trị trả về

Khơi tao ( )

push(7) ( 7 )

push(8) ( 7, 8 )

push(5) ( 7, 8, 5 )

pop() ( 8, 5 ) 7

pop() ( 5 ) 8

2.2.3 Cài đặt hàng đợi sư dụng mảng

Đê cai đăt câu truc hang đơi chung ta co thê sƣ dung mang hoăc sƣ dung con tro (phân

này sẽ học sau phần danh sách liên kêt):

Ta lƣu cac phân tƣ cua hang đơi trong môt mang data . Đầu cua hàng đợi là phần tư

đâu tiên, và đuôi đƣợc chỉ ra băng cách sư dung một biên tail.

push(d) đƣơc thƣc hiên môt cach dê dang : tăng tail lên 1 và chen phần tư vào vi tri

đo

pop() đƣơc thƣc hiên không hiêu qua: tât ca cac phân tƣ đêu se bi dôn vê đâu mang

do đo đô phƣc tap la O(n).

Làm thê nào chúng ta co thể cải thiện tinh hinh này?

Thay vi chi sƣ dung môt biên chi sô tail chung ta sƣ dung hai biên tail và head, khi cân

loại bo (pop) môt phân tƣ khoi hang đơi chung ta se tăng biên head lên 1:

Tuy vây vân con co vân đê , đo la sau n lân push() (n la kich thƣơc mang) mảng sẽ đầy

kê ca trong trƣơng hơp no gần nhƣ rỗng về măt logic. Đê giai quyêt vân đê nay chung ta se sƣ

dung lại các phần tư ở đầu mảng. Khi push() môt phân tƣ mơi tail se đƣơc tăng lên 1 nhƣng

nêu nhƣ no ơ cuôi mang thi se đăt no băng 0.

Vân đê mơi nảy sinh ở đây là làm thê nào chúng ta co thể xác đinh đƣợc khi nào hàng

đơi rông hoăc đây?

Page 19: Dhhh ctdlgt bai giang cau truc du lieu

14

Cách giải quyêt đơn giản là ta sẽ dùng một biên lƣu số phần tư thực sự cua hàng đợi

để giải quyêt cho tất cả các thao tác kiêm tra hang đơi rông, đây hoăc lây sô phân tƣ cua hang

đơi.

#include <stdio.h>

#include <stdlib.h>

const int MAX_ELEMENT = 100; // so phan tu toi da cua queue la 100

// khai bao queue chua cac so nguyen

typedef struct

{

int * data; // khai bao mang dong

int head;

int tail;

int cap; // luu so phan tu cua hang doi

} queue;

// ham khoi tao queue rong

void init(queue *q);

void push(queue * s, int d);

void pop(queue *q);

int front(const queue *q);

int back(const queue *q);

int size(const queue *q);

int empty(const queue *q);

int full(const queue *q);

// ham giai phong bo nho danh cho queue

void clear(queue *q);

int main()

{

int a[] = {3, 5, 1, 8};

int n = 4;

int i;

int d;

queue q;

init(&q);

for(i=0;i<n;i++)

push(&q, a[i]);

while(!empty(&q))

{

d = front(&q);

printf("%d ", d);

pop(&q);

}

clear(&q);

return 0;

Page 20: Dhhh ctdlgt bai giang cau truc du lieu

15

}

void init(queue *q)

{

q->data = (int*)malloc(MAX_ELEMENT * sizeof(int));

q->head = q->tail = -1;

q->cap = 0;

}

void clear(queue *q)

{

if(q->data != NULL)

free(q->data);

q->head = q->tail = -1;

q->cap = 0;

}

void push(queue *q, int d)

{

q->tail = (q->tail + 1) % MAX_ELEMENT;

q->data[q->tail] = d;

if(q->cap==0)

// neu hang doi rong thi sau khi push

// ca head va tail deu chi vao 1 phan tu

q->head = q->tail;

q->cap++;

}

void pop(queue *q)

{

q->head = (q->head + 1)%MAX_ELEMENT;

q->cap--;

if(q->cap==0)

q->head = q->tail = -1;

}

int front(const queue *q)

{

return q->data[q->head];

}

int back(const queue *q)

{

return q->data[q->tail];

}

int size(const queue *q)

{

return q->cap;

}

Page 21: Dhhh ctdlgt bai giang cau truc du lieu

16

int empty(const queue *q)

{

return (q->cap==0)?(1):(0);

}

int full(const queue *q)

{

return (q->cap==MAX_ELEMENT-1)?(1):(0);

}

2.2.4 Ví dụ về hoạt động của hàng đợi vơi cài đặt băng mảng vong tron

Ta gia sƣ mang lƣu cac phân tƣ cua hang đơi la E[0..3], các biên head, tail lƣu vi tri

cua phần tư ở đầu và cuối hàng đợi, côt R la côt kêt qua thƣc hiên cac thao tac trên hang đơi,

các dấu ? tƣơng ƣng vơi gia tri bât ky.

Thao tac head tail E[0] E[1] E[2] E[3] R

Khơi tao -1 -1 ? ? ? ?

push(55) 0 0 55 ? ? ?

push(-7) 0 1 55 -7 ? ?

push(16) 0 2 55 -7 16 ?

pop() 1 2 55 -7 16 ? 55

push(-8) 1 3 55 -7 16 -8

pop() 2 3 55 -7 16 -8 -7

pop() 3 3 55 -7 16 -8 16

push(11) 3 4 11 -7 16 -8

2.2.5 Ưng dụng của hàng đợi

Trong cac hê điêu hanh:

Hàng đợi các công việc hoăc các tiên trinh đang đợi để đƣợc thực hiện

Hàng đợi các tiên trinh chờ các tin hiệu từ các thiêt bi IO

Các file đƣơc gƣi tơi may in

Mô phong cac hê thông hang đơi thơi trong thƣc tê.

Các khách hàng trong các cưa hàng tạp hoa, trong cac hê thông ngân hang

Các đơn đăt hàng cua một công ty

Phong cấp cứu

Các cuộc gọi điện thoại hoăc cac đăt hang ve may bay , các đăt hàng cua khách

hàng …

Các ứng dung khác:

Thƣ tƣ topo: vơi môt tâp cac sƣ kiên, và các căp (a, b) trong đo sƣ kiên a co đô ƣu tiên

cao hơn so vơi sƣ kiên b (bài toán lập lich), duyệt đô thi theo chiều rộng (Breadth First

Search).

Bài tập: Hãy viêt chƣơng trinh chuyển đổi một biểu thức dạng infix (dạng thông

thƣơng) đơn gian (không chƣa cac dâu ()) thành một biểu thức dạng tiền tố (prefix). Vi du này

xem nhƣ môt bai tâp đê sinh viên tƣ lam.

Page 22: Dhhh ctdlgt bai giang cau truc du lieu

17

2.3. Danh sach liên kêt – Linked list

2.3.1 Đinh nghia

Danh sach liên kêt (linked list) là một tập hợp tuyên tinh các phần tư cùng kiểu gọi là

các nút (node), môi nut co cac đăc điêm sau đây:

Môi nut co it nhât hai trƣơng (field) môt trƣơng goi la trƣơng dƣ liêu (data) và

trƣơng con lai la trƣơng liên kêt (link) tro tơi (point to) (thƣơng goi la next).

Trƣơng liên kêt cua phân tƣ thƣ i cua danh sach se tro tơi phần tư thứ (i+1) cua

danh sach

Phân tƣ đâu tiên cua danh sach liên kêt đƣơc goi la head va phân tƣ cuôi cung

đƣơc goi la tail. Head không chƣa dƣ liêu va trƣơng next cua tail se chi vao NULL .

Trƣơng data la trƣơ ng chƣa dƣ liêu ma chung ta thƣc sƣ lƣu trong danh sach liên

kêt

Giá tri NULL và việc thực hiện tro tơi (point to) cua mỗi liên kêt thực sự diễn ra

nhƣ thê nao phu thuôc nhiêu vao viêc cai đăt cu thê danh sach liên kêt.

Co nhiều loại danh sách liên kêt khác nhau tùy thuộc vào cấu trúc cua mỗi phần tư

trong danh sach (sô trƣơng liên kêt vơi cac phân tƣ khac trong danh sach) nhƣng cơ ban nhât

là danh sách liên kêt đơn (single linked list), mỗi phần tư co một trƣờng liên kêt nhƣ trên hinh

vẽ minh họa, và khi chúng ta noi đên danh sách liên kêt, nêu không co cac chu giai đi kem thi

ngâm hiêu đo la danh sach liên kêt đơn.

2.3.2 Các thao tác trên danh sách liên kết.

Tƣơng tƣ nhƣ cac câu truc cơ ban stack va queue, chúng ta đinh nghia các thao tác cua

danh sach liên kêt dƣa trên cai đăt chuân cua câu truc danh sach liên kêt trong thƣ viên STL :

1. push_front(d): thêm môt phân tƣ vao đâu danh sách.

2. push_back(d): thêm môt phân tƣ vao cuôi danh sach.

3. pop_front(): loại bo phần tư ở đầu danh sách.

4. pop_back(): loại bo phần tư cuối danh sách.

5. erase(): xoa bo một phần tư khoi danh sách.

6. insert(): chen một phần tư mơi vào một vi tri cu thể cua danh sách.

7. size(): cho biêt sô phân tƣ trong danh sach.

8. empty(): kiêm tra danh sach rông.

9. begin(): trả về phần tư ở đầu danh sách.

10. end(): trả về phần tư ở cuối danh sách.

11. sort(): săp xêp danh sach theo trƣơng khoa (là một trƣờng con cua trƣờng dữ liệu).

12. merge(): trôn danh sach vơi môt danh sach khac.

13. clear(): xoa bo toàn bộ các phần tư cua danh sách.

14. find(): tim kiêm một phần tư trong danh sách theo khoa tim kiêm.

Page 23: Dhhh ctdlgt bai giang cau truc du lieu

18

Các thao tac khac cung co thê đƣơc cai đăt vơi môt danh sach liên kêt đê lam cho công

viêc cua cac lâp trinh viên trơ nên dê dang hơn:

Di chuyên môt phân tƣ trong danh sach

Đổi hai phần tư cho nhau

2.3.3 Cài đặt danh sách liên kêt sƣ dung con tro

typedef struct Node

{

// truong du lieu

int data;

struct Node * next;

} NodeType;

Khơi tao danh sach:

NodeType * head, * tail;

head = new node;

headnext = NULL;

Các thao tác trên sẽ tạo ra một danh sách liên kêt rông (empty – không chƣa phân tƣ

nào)

Trong cai đăt nay chung ta cho tail chi vao NULL , thƣơng đƣơc đinh nghia la 0. Do đo

trƣơng next cua môt phân tƣ se la 0, và khi đo chúng ta biêt là chúng ta đang ở phần tư tail

cua danh sach.

Ở đây tro tơi (point to) co nghia là chúng ta thực sự sư dung các con tro. Chúng ta sẽ

sơm thây răng chung ta không cân thiêt phai sƣ dung cac con tro thƣc sƣ đê cai đăt môt danh

sách liên kêt.

Chen một nút (node) vào danh sách liên kết

Dê dang nhân thây răng cach đơn gian nhât đê chen môt nut mơi vao môt danh sach

liên kêt la đăt nut đo ơ đâu (hoăc cuôi) cua danh sách. Hoăc cung co thê chung ta muôn chen

các phần tư vào giƣa danh sach.

Chen X vào giữa I và S:

struct node * A;

A = new node;

Akey = X;

Anext = Inext;

Inext = A;

Page 24: Dhhh ctdlgt bai giang cau truc du lieu

19

Đê thƣc hiên điêu nay chung ta cân 2 tham chiêu tơi hai nut trong danh sach va không

cân quan tâm tơi đô dai (sô phân tƣ) cua danh sách. Tuy nhiên thƣc hiên viêc nay vơi cac

mảng chắc chắn sẽ khác nhiều.

Xóa một nút (node) khoi danh sách liên kết

Xoa một nút khoi danh sách liên kêt rất đơn giản chúng ta chỉ cần thay đổi một con

tro, tuy nhiên vân đê la chung ta cân biêt nut nao tro tơi nut ma chung ta đinh xoa. Giả sư

chúng ta biêt nút i tro tơi nút x và chúng ta muốn xoa bo x:

inext = xnext;

Chỉ co một tham chiêu bi thay đổi không phu thuộc vào đô dai cua danh sach (so sanh

vơi cai đăt băng mang, tuy vây vân co vân đê vơi cai đăt trên ).

Di chuyên (move) môt nut trong danh sach liên kêt

Di chuyên môt nut trong danh sach liên kêt bao gôm hai thao tac: xoa bo một nút sau

đo chen vao môt nut. Vi du chúng ta muốn di chuyển nút T từ cuối danh sách lên đầu danh

sách:

Môt lân nƣa chung ta thây răng thao tac di chuyên nay chi đoi hoi thay đôi 3 tham

chiêu va không phu thuôc vao đô dai cua danh sach (so sanh điêu nay vơi cai đăt băng mang).

Cài đăt minh họa đầy đu cua danh sách liên kêt đơn:

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

// khai bao cau truc cho mot nut cua danh sach

typedef struct Node

{

// truong du lieu

int data;

struct Node * next;

Page 25: Dhhh ctdlgt bai giang cau truc du lieu

20

} NodeType;

// khai bao kieu danh sach

typedef struct

{

NodeType * head;

NodeType * tail;

// so phan tu cua danh sach

int spt;

}LList;

// ham khoi tao danh sach

void init(LList * list);

// ham them 1 phan tu vao dau danh sach

void push_front(LList *list, int d);

// ham them mot phan tu vao cuoi danh sach

void push_back(LList *list, int d);

// ham xoa phan tu o cuoi danh sach

int pop_back(LList * list);

// ham xoa phan tu o dau danh sach

int pop_front(LList * list);

// ham tra ve phan tu dau tien

int begin(const LList * list);

// ham tra ve phan tu cuoi cung

int end(const LList * list);

void insertAfter(LList * list, NodeType * p);

void insertBefore(LList * list, NodeType * p);

void eraseAfter(LList * list, NodeType * p);

void eraseBefore(LList * list, NodeType * p);

// ham in danh sach

void printList(LList list);

// ham sap xep danh sach

void sort(LList *list);

// ham tim kiem trong danh sach

NodeType * find(LList *, int d);

// giai phong toan bo danh sach

void clear(LList * list);

// ham tron hai danh sach, ket qua luu trong danh sach thu nhat

void merge(LList *list1, const LList *list2);

// ham kiem tra danh sach lien ket co rong khong

int empty(const LList *list);

int main()

{

LList myList;

LList list2;

Page 26: Dhhh ctdlgt bai giang cau truc du lieu

21

init(&myList);

init(&list2);

push_front(&myList, 10);

push_front(&myList, 1);

push_front(&myList, 12);

push_back(&myList, 20);

push_back(&myList, 23);

push_back(&myList, 25);

sort(&myList);

printList(myList);

push_front(&list2, 14);

push_front(&list2, 9);

merge(&myList, &list2);

printList(myList);

printList(list2);

clear(&myList);

printList(myList);

return 0;

}

void init(LList * list)

{

list->head = list->tail = NULL;

list->spt = 0;

}

void printList(LList list)

{

NodeType * tmp;

tmp = list.head;

while(tmp!=NULL)

{

printf("%d ", tmp->data);

tmp = tmp->next;

}

printf("\n");

}

void push_front(LList * list, int d)

{

NodeType * tmp = (NodeType *)malloc(sizeof(NodeType));

tmp->data = d;

if(list->spt==0)

{

tmp->next = NULL;

list->head = list->tail = tmp;

Page 27: Dhhh ctdlgt bai giang cau truc du lieu

22

}

else

{

tmp->next = list->head;

list->head = tmp;

}

list->spt = list->spt+1;

}

void push_back(LList * list, int d)

{

NodeType * tmp = (NodeType *)malloc(sizeof(NodeType));

tmp->data = d;

tmp->next = NULL;

if(list->spt==0)

list->head = list->tail = tmp;

else

{

list->tail->next = tmp;

list->tail = tmp;

}

list->spt = list->spt+1;

}

NodeType * find(LList * list, int d)

{

NodeType * tmp = list->head;

while(tmp!=NULL)

{

if (tmp->data==d)

break;

tmp = tmp->next;

}

return tmp;

}

int pop_back(LList * list)

{

NodeType * p, * q;

int ret = -1;

if(list->spt>0)

{

p = list->head;

q = NULL;

while(p->next!=NULL)

Page 28: Dhhh ctdlgt bai giang cau truc du lieu

23

{

q = p;

p = p->next;

}

if(q!=NULL)

{

// danh sach chi co 1 phan tu

q->next = NULL;

list->tail = q;

}

else

list->head = list->tail = NULL;

ret = p->data;

free(p);

list->spt = list->spt-1;

}

return ret;

}

int pop_front(LList * list)

{

int ret=-1;

NodeType * tmp;

if(list->spt>0)

{

tmp = list->head;

if(list->spt==1)

{

// danh sach chi co 1 phan tu

ret = list->head->data;

list->head = list->tail = NULL;

}else

list->head = list->head->next;

free(tmp);

list->spt = list->spt - 1;

}

return ret;

}

// sap xep dung thuat toan doi cho truc tiep (interchange sort)

void sort(LList * list)

{

// sƣ dung thuât toan săp xêp nôi bot Bubble sort

NodeType * p, * q;

int tmp;

Page 29: Dhhh ctdlgt bai giang cau truc du lieu

24

p = list->head;

while(p!=NULL)

{

q = p->next;

while(q!=NULL)

{

if(q->data < p->data)

{

tmp = q->data;

q->data = p->data;

p->data = tmp;

}

q = q->next;

}

p = p->next;

}

}

void clear(LList * list)

{

NodeType * p, * q;

if(list->spt>0)

{

p = list->head;

list->head = list->tail = NULL;

list->spt = 0;

while(p)

{

q = p->next;

free(p);

p = q;

}

}

}

void merge(LList *list1, const LList *list2)

{

NodeType * tmp;

tmp = list2->head;

while(tmp)

{

push_back(list1, tmp->data);

tmp = tmp->next;

}

Page 30: Dhhh ctdlgt bai giang cau truc du lieu

25

}

int empty(const LList *list)

{

return (list->spt==0)?(1):(0);

}

int begin(const LList * list)

{

return list->head->data;

}

int end(const LList * list)

{

return list->tail->data;

}

So sanh giƣa danh sach liên kêt va mang

Danh sach liên kêt co sô phân tƣ co thê thay đôi va không cân chỉ rõ kich thƣơc tối đa

cua danh trƣơc. Ngƣơc lai mang la cac câu truc co kich thƣơc cô đinh

Chúng ta co thể sắp xêp lại, thêm va xoa cac phân tƣ khoi danh sach liên kêt chi vơi

môt sô cô đinh cac thao tac. Vơi mang cac thao tac nay thƣơng tƣơng đƣơng vơi kich thƣơc

mảng.

Đê tim đên phân tƣ thƣ i trong môt danh sach liên kêt chung ta cân phai do qua i-1

phân tƣ đƣng trƣơc no trong danh sach (i-1 thao tac) trong khi vơi mang đê lam điêu nay chi

mât 1 thao tac.

Tƣơng tƣ kich thƣơc cua môt danh sach không phai hiên nhiên ma biêt đƣơc trong khi

chúng ta luôn biêt rõ kich thƣơc cua một mảng trong chƣơng trinh (tuy nhiên co môt cach đơn

giản để khắc phuc điều nay).

2.3.4 Các kiểu danh sách liên kết khác

Danh sách liên kêt đôi (double linked list) giống nhƣ một danh sách liên kêt đơn ngoại

trừ việc mỗi nút có thêm một trƣờng previous tro vào nút đứng trƣơc nó.

Vơi danh sách liên kêt đôi các thao tác nhƣ tim kiêm, xóa bo một nút khoi danh sách

thực hiện dễ dàng hơn nhƣng đông thời cũng mất nhiều bộ nhơ hơn và số lƣợng các lệnh để

thực hiện một thao tác trên danh sách liên kêt đôi chắc chắn cũng xấp xỉ gấp đôi so vơi danh

sách liên kêt đôi.

Ngoài ra còn có một loại danh sách liên kêt khác đƣợc gọi là danh sách liên kêt vòng

(circular-linked list).

Nút cuối cùng tro tơi nút đầu tiên

Danh sách liên kêt vòng có thể là danh sách liên kêt đơn hoăc danh sách liên kêt đôi

Nó có thể đƣợc cài đăt vơi một đầu (head) cố đinh hoăc thay đổi

Page 31: Dhhh ctdlgt bai giang cau truc du lieu

26

Việc cài đăt danh sách liên kêt có thể không cần thiêt sư dung tơi các con tro. Thay

vào đo chúng ta co thể sư dung các mảng để cài đăt các danh sách liên kêt, ở đây chúng ta

không đi sâu vào xem xét cu thể cài đăt một danh sách liên kêt băng mảng nhƣ thê nào nhƣng

cũng không quá kho để hình dung cách thức hoạt động cua các danh sách kiểu nhƣ thê.

Kết luận:

Danh sách liên kêt là các cấu trúc dữ liệu rất giống vơi các mảng

Các thao tác chinh thƣờng đƣợc sư dung đối vơi một danh sách liên kêt là thêm, xóa

và tìm kiêm trên danh sách

Thao tác chèn và xóa có thể thực hiện vơi thời gian hăng số

Việc tìm một phần tư trong danh sách liên kêt thƣờng mất thời gian tuyên tính (xấp xỉ

độ dài danh sách) và trƣờng hợp xấu nhất là đúng băng độ dài cua danh sách. Đây cũng chinh

là một trong những nhƣợc điểm lơn nhất cua danh sách liên kêt.

2.3.5 Môt sô vi du sƣ dung câu truc danh sach liên kêt

Các bài toán mà danh sách liên kêt thƣờng đƣợc sư dung là các bài toán trong đo viêc

sƣ dung mang se la không thuân lơi, chăng han môt bai toan yêu câu cac thao tac thêm , xoa

bo xảy ra thƣờng xuyên thi lựa chọn thông minh sẽ là sư dung danh sách liên kêt . Môt vi du

nƣa la khi ta lam viêc vơi cac đô thi thƣa (các cạnh it) lơn (nhƣng sô đinh nhiêu), thay vi dung

môt mang hai chiêu, ta se dung môt mang cac danh sach liên kêt, môi danh sach liên kêt chƣa

các đỉnh liền kề cua một đỉnh cua đô thi.

2.3.6. Cài đặt stack va queue băng con tro

Vê ban chât, các cấu trúc dữ liệu stack và queue là các cấu trúc danh sách liên kêt hạn

chê, các thao tác đƣợc giơi hạn so vơi cấu trúc danh sách liên kêt . Vi thê co thể coi một stack

hay queue la môt danh sach liên kêt, và co thể lợi dung cài đăt băng con tro cua danh sách liên

kêt đê cai đăt cac câu truc stack va queue (sƣ dung con tro ). Phân nay đƣơc đê lai xem nhƣ

môt bai tâp cua sinh viên.

2.4. Bài tập áp dụng

1. Viêt khai báo và các chƣơng trinh con cài đăt danh sách băng mảng. Dùng các

chƣơng trinh con này để viêt:

a. Chƣơng trinh con nhận một đãy các số nguyên nhập từ bàn phim, lƣu trữ no trong

danh sách theo thứ tự nhập vào.

b. Chƣơng trinh con nhận một đãy các số nguyên nhập từ bàn phim, lƣu trữ no trong

Page 32: Dhhh ctdlgt bai giang cau truc du lieu

27

danh sách theo thứ tự ngƣợc vơi thứ tự nhập vào.

c. Viêt chƣơng trinh con in ra màn hinh các phần tư trong danh sách theo thứ tự cua

nó trong danh sách.

2. Tƣơng tự nhƣ bài tập 1. nhƣng cài đăt băng con tro.

3. Viêt chƣơng trinh con sắp xêp một danh sách chứa các số nguyên, trong các

trƣờng hợp:

a. Danh sách đƣợc cài đăt băng mảng (danh sách đăc).

b. Danh sách đƣợc cài đăt băng con tro (danh sách liên kêt).

4. Viêt chƣơng trinh con thêm một phần tư trong danh sách đã co thứ tự sao cho ta vẫn

co một danh sách co thứ tự băng cách vận dung các phép toán co bản trên danh sách

5. Viêt chƣơng trinh con tim kiêm và xoa một phần tư trong danh sách co thứ tự.

6. Viêt chƣơng trinh con nhận vào từ bàn phim một đãy số nguyên, lƣu trữ no trong

một danh sách co thứ tự không giảm, theo cách sau: vơi mỗi phần tư đƣợc nhập vào chƣơng

trinh con phải tim vi tri thich hợp để xen no vào danh sách cho dúng thứ tự. Viêt chƣơng trinh

con trên cho trƣờng hợp danh sách đƣợc cài đăt băng mảng và cài đăt băng con tro và trong

trƣờng hợp tổng quát (dùng các phép toán cơ bản trên danh sách)

7. Viêt chƣơng trinh con loại bo các phần tư trùng nhau (giữ lại duy nhất 1 phần tư)

trong một danh sách co thứ tự không giảm, trong hai trƣờng hợp: cài đăt băng mảng và cài đăt

băng con tro.

8. Viêt chƣơng trinh con nhận vào từ bàn phim một đãy số nguyên, lƣu trữ no trong

một danh sách co thứ tự tang không co hai phần tư trùng nhau, theo cách sau: vơi mỗi phần tư

đƣợc nhập vào chƣơng trinh con phải tim kiêm xem no co trong danh sách chƣa, nêu chƣa co

thi xen no vào danh sách cho đúng thứ tự. Viêt chƣơng trinh con trên cho trƣờng hợp danh

sách đƣợc cài đăt băng mảng và cài đăt băng con tro.

9. Viêt chƣơng trinh con trộn hai danh sách liên kêt chứa các số nguyên theo thứ tự

tăng để đƣợc một danh sách cũng co thứ tự tăng.

10. Viêt chƣơng trinh con xoá khoi danh sách lƣu trữ các số nguyên các phần tư là số

nguyên lẻ, cũng trong hai trƣờng hợp: cài đăt băng mảng và băng con tro.

11. Viêt chƣơng trinh con tách một danh sách chứa các số nguyên thành hai danh

sách: một danh sách gôm các số chẵn con cái kia chứa các số lẻ.

Page 33: Dhhh ctdlgt bai giang cau truc du lieu

28

CHƢƠNG 3. CÂY (TREE).

3.1. Đinh nghia

3.1.1. Đô thị (Graph)

Trƣơc khi xem xet khai niêm thê nao la môt cây (tree) chúng ta nhắc lại khái niệm đô thi

(graph) đa đƣơc hoc trong hoc phân Toan rơi rac : Đô thi G bao gôm hai thành phần chinh : tâp

các đỉnh V (Vertices) và tập các cung E (hay canh Edges ), thƣơng viêt ơ dang G = <V, E>.

Trong đo tâp cac đinh V la tâp cac đôi tƣơng cung loai , đôc lâp, chăng han nhƣ cac điêm trên

măt phăng toa đô , hoăc tâp cac thanh phô , tâp cac trang thai cua môt tro chơi , môt đôi tƣơng

thƣc nhƣ con ngƣơi , … tât ca đêu co thê la cac đinh cua môt đô thi nao đo . Tâp cac cung E la

tâp cac môi quan hê hai ngôi giƣa cac đinh cua đô thi , đôi vơi đinh la cac điêm thi đây co thê

là quan hệ vê khoang cach, tâp đinh la cac thanh phô thi đây co thê la quan hê vê đƣơng đi (co

tôn tai đƣơng đi trƣc tiêp nao giƣa cac thanh phô hay không ), hoăc nêu đinh la cac trang thai

cua một tro chơi thi cạnh co thể là cách biên đổi (transform) để đi từ trạng thái này sang một

trạng thái khác , quá trinh chơi chinh là biên đổi từ trạng thái ban đầu tơi trạng thái đich (co

nghia là đi tim một đƣờng đi).

Ví dụ về đô thị:

Hinh 5.1. Đô thi co 6 đinh va 7 cạnh, tham khao tƣ wikipedia.

Co rất nhiều vấn đề liên quan tơi đô thi , ở phần này chúng ta chi nhắc lại một số khái

niêm liên quan.

Môt đô thi đƣơc goi la đơn đô thi (simple graph) nêu nhƣ không co đƣơng đi giƣa hai

đinh bât ky cua đô thi bi lăp lai , ngƣơc lai nêu nhƣ co đƣơng đi nao đo bi lăp lai hoăc tôn tai

khuyên (self-loop), môt dang cung đi tƣ 1 đinh đên chinh đinh đo, thi đô thi đƣợc gọi là đa đô

thi (multigraph).

Giƣa hai đinh u , v trong đô thi co đƣơng đi trƣc tiêp thi u , v đƣơc goi la liên kê vơi

nhau, cạnh (u, v) đƣơc goi la liên thuôc vơi hai đinh u, v.

Đô thi đƣợc gọi là đô thi co hƣơng (directed graph) nêu nhƣ cac đƣơng đi giƣa hai đinh

bât ky trong đô thi phân biêt hƣơng vơi nhau, khi đo cac quan hê giƣa cac đinh đƣơc goi chinh

xác là các cung , ngƣơc lai đô nêu không phân biêt hƣơng giƣa cac đinh trong cac canh nôi

giƣa hai đinh thi đô thi đƣợc gọi là đô thi vô hƣơng (undirected graph), khi đo ta noi tâp E la

tâp cac canh cua đô thi.

Các cung hay các cạnh cua đô thj co thể đƣợc gán các giá tri gọi là các trọng số

(weight), môt đô thi co thê là đô thi co trọng số hoăc không co trọng số . Vi du nhƣ đối vơi đô

thi mà các đỉnh là các thành phố ta co thể gán trọng số cua các cung là độ dài đƣờng đi nối

giƣa cac thanh phô hoăc chi phi đi trên con đƣơng đo …

Môt đƣơng đi (path) trong đô thi la môt day cac đinh v 1, v2, …, vk, trong đo cac đinh v i,

vi+1 là liền kề vơi nhau . Đƣờng đi co đỉnh đầu trùng vơi đỉnh cuối đƣợc gọi là chu trinh

(cycle).

Giƣa hai đinh cua đô thi co thê co các đƣờng đi trực tiêp nêu chúng liền kề vơi nhau ,

hoăc nêu co môt đƣơng đi giƣa chung (gián tiêp ) thi hai đỉnh đo đƣợc gọi là liên thông

(connected) vơi nhau. Môt đô thi đƣơc goi la liên thông nêu nhƣ hai đinh bât ky cua no đều

Page 34: Dhhh ctdlgt bai giang cau truc du lieu

29

liên thông vơi nhau. Nêu đô thi không liên thông thi luôn co thê chia no thanh cac thanh phân

liên thông nho hơn.

3.1.2. Cây (tree)

Co nhiều cách đinh nghia cây khác nhau nhƣng ở đây chúng ta sẽ đinh nghia khái niệ m

cây theo ly thuyêt đô thi (graph theory).

Cây la môt đô thi vô hƣơng , không co trong sô , liên thông va không co chu trinh . Vi du

hinh vẽ sau là một cây:

Hinh 5.2. Cây, tham khao tƣ wikipedia

Câu truc cây la môt câu truc đƣ ợc sư dung rất rộng rãi trong cuộc sống hàng ngày và

trên may tinh , chăng han câu truc tô chƣc cua môt công ty la môt cây phân câp , câu truc cua

môt web site cung tƣơng tƣ:

Hinh 5.3. Câu truc web site wikipedia, tham khao tƣ wikipedia.

Câu truc tô chƣc thƣ muc cua hê điêu hanh la môt cây …

Trong cây luôn co môt nut đăc biêt goi la gôc cua cây (root), các đỉnh trong cây đƣợc

gọi là các nút (nodes). Tƣ gôc cua cây đi xuông tât ca cac đinh liên kê vơi no , các đỉnh này

gọi là con cua gốc , đên lƣợt các con cua gốc lại co các nút con (child nodes) khác, nhƣ vây

quan hê giƣa hai nut liên kê nhau trong cây la quan hê cha con , môt nut la cha (parent), môt

nút là con (child), nút cha cua cha cua một nút đƣợc gọi là tổ tiên (ancestor) cua nút đo.

Các nút trong cây đƣợc phân biệt làm nhiều loại : các nút co it nhất 1 nút con đƣợc gọi là

các nút trong (internal nodes hay inner nodes), các nut không co nut con đƣơc goi la cac nut la

(leaf nodes ). Các nút lá không co các nút con nhƣng để thuận tiện trong quá trinh cài đăt

ngƣơi ta vân coi cac nut la co hai nut con gia , rông (NULL) đong vai tro linh canh , gọi la cac

nút ngoài (external nodes).

Các nút trong cây đƣợc phân chia thành các tầng (level), nút gốc thuộc tầng 0 (level 0),

sau đo cac tâng tiêp theo se đƣơc tăng lên 1 đơn vi so vơi tâng phia trên no cho đên tâng cuôi

cùng. Độ cao (height) cua cây đƣợc tinh băng số tầng cua cây , đô cao cua cây se quyêt đinh

đô phƣc tap (sô thao tac) khi thƣc hiên cac thao tac trên cây.

Môi nut trong cua cây tông quat co thê co nhiêu nut con , tuy nhiên cac ngh iên cƣu cua

ngành khoa học máy tinh đã cho thấy cấu trúc cây quan trọng nhất cần nghiên cứu chinh là

các cây nhi phân (binary tree ), là các cây là mỗi nút chỉ co nhiều nhất hai nút con . Môt cây

tông quat luôn co thê phân chia thanh cac cây nhi phân.

Page 35: Dhhh ctdlgt bai giang cau truc du lieu

30

Các nút con cua một nút trong cây nhi phân đƣợc gọi là nút con trái (left child) và nút

con phai (right child).

Trong chƣơng nay chung ta se nghiên cƣu môt sô loai cây nhi phân cơ ban va đƣơc ƣng

dung rông rai nhât , đo la cây tim kiêm nhi phân BST (Binary Search Tree), cây biêu thƣc

(expression tree hay syntax tree) và cây cân băng (balanced tree) AVL.

Hoặc một cách định nghĩa khác (đọc thêm)

Cây là một tập hợp các phần tư gọi là nút (nodes) trong đo co một nút đƣợc phân biệt

gọi là nút gốc (root). Trên tập hợp các nút này co một quan hệ, gọi là mối quan hệ cha - con

(parenthood), để xác dinh hệ thống cấu trúc trên các nút. Mỗi nút, trừ nút gốc, co duy nhất

một nút cha. Một nút co thể co nhiều nút con hoăc không co nút con nào. Mỗi nút biểu diễn

một phần tư trong tập hợp dang xét và no co thể co một kiểu nào đo bất kỳ, thƣờng ta biểu

diễn nút băng một ki tự, một chƣỗi hoăc một số ghi trong vong tron. Mối quan hệ cha con

đƣợc biểu diễn theo qui ƣơc nút cha ở dòng trên nút con ở dòng dưới và đƣợc nối bởi một

doạn thẳng. Một cách hinh thức ta co thể dinh nghia cây một cách đệ qui nhƣ sau:

Ðịnh nghĩa - Một nút đơn dộc là một cây. Nút này cũng chinh là nút gốc cua cây.

- Giả sư ta co n là một nút đơn độc và k cây T1,.., Tk vơi các nút gốc tƣơng ứng là n1,..,

nk thi co thể xây dựng một cây mơi băng cách cho nút n là cha cua các nút n1,.., nk. Cây mơi

này co nút gốc là nút n và các cây T1,.., Tk đƣợc gọi là các cây con. Tập rỗng cũng đƣợc coi

là một cây và gọi là cây rỗng ki hiệu .

Ví dụ: Xét muc luc cua một quyển sách. Muc luc này co thể xem là một cây. Xét

cấu trúc thƣ muc trong

tin học, cấu trúc này

cũng đƣợc xem nhƣ

một cây.

Hình III.1 - Cây muc luc một quyển sách

Nêu n1,.., n

k là một chƣỗi các nút trên cây sao cho n

i là nút cha cua nút n

i+1, vơi i=1..k-

1, thi chƣỗi này gọi là một dƣờng di trên cây (hay ngắn gọn là dƣờng di ) từ n1 đên n

k. Ðộ dài

dƣờng di đƣợc dinh nghia băng số nút trên dƣờng di trừ 1. Nhƣ vậy dộ dài dƣờng di từ một

nút đên chinh no băng không.

Nêu co dƣờng di từ nút a đên nút b thi ta noi a là tiền bối (ancestor) cua b, con b gọi là

hậu duệ (descendant) cua nút a. Rõ ràng một nút vừa là tiền bối vừa là hậu duệ của chính nó.

Tiền bối hoăc hậu duệ cua một nút khác vơi chinh no gọi là tiền bối hoăc hậu duệ thực sự.

Trên cây nút gốc không co tiền bối thực sự. Một nút không co hậu duệ thực sự gọi là nút lá

(leaf). Nút không phải là lá ta con gọi là nút trung gian (interior). Cây con cua một cây là một

nút cùng vơi tất cả các hậu duệ cua no.

Chiều cao của một nút là dộ dài dƣờng di lơn nhất từ nút đo tơi lá. Chiều cao của cây

là chiều cao cua nút gốc. Ðộ sâu của một nút là dộ dài dƣờng di từ nút gốc đên nút đo. Các

nút co cùng một dộ sâu i ta gọi là các nút co cùng một mức i. Theo dinh nghia này thi nút gốc

ở mức 0, các nút con cua nút gốc ở mức 1.

Ví dụ: đối vơi cây trong hinh III.1 ta co nút C2 co chiều cao 2. Cây co chiều cao 3. nút

Sách

C1 C2 C3

1.1 1.2 2.2

2.1.1 2.1.2

2.1 3.2

Page 36: Dhhh ctdlgt bai giang cau truc du lieu

31

C3 co chiều cao 0. Nút 2.1 co dộ sâu 2. Các nút C1,C2,C3 cùng mức 1.

Thứ tự các nút trong cây Nêu ta phân biệt thứ tự các nút con cua cùng một nút thi cây gọi là cây co thứ tự, thứ

tự qui ƣơc từ trái sang phải. Nhƣ vậy, nêu kể thứ tự thi hai cây sau là hai cây khác nhau:

Hình III.2: Hai cây co thứ tự khác nhau

Trong trƣờng hợp ta không phân biệt rõ ràng thứ tự các nút thi ta gọi là cây không co

thứ tự. Các nút con cùng một nút cha gọi là các nút anh em ruột (siblings). Quan hệ "trái sang

phải" cua các anh em ruột co thể mở rộng cho hai nút bất kỳ theo qui tắc: nêu a, b là hai anh

em ruột và a bên trái b thi các hậu duệ cua a là "bên trái" mọi hậu duệ cua b.

3.3. Cây tim kiêm nhi phân (Binary Search Tree - BST)

3.3.1. Đinh nghia

Môi nut trong cây bât ky đêu chƣa cac trƣơng thông tin , trên môt cây tim kiêm nhi phân

môi nut la môt struct (bản ghi – record) gôm cac trƣơng: trƣơng dƣ liêu data, trƣơng khoa key

để so sánh vơi các nút khác, các liên kêt tơi các nút con cua nút left và right.

Đê tâp trung vao cac vân đê thuât toa n ta bo qua trƣơng dƣ liêu , chỉ xem nhƣ mỗi nút

trên cây tim kiêm nhi phân gôm co môt trƣơng khoa key và hai trƣờng liên kêt left và right.

Vơi cac gia thiêt trên ta đinh nghia cây tim kiêm nhi phân nhƣ sau :

Cây tim kiêm nhi phân la môt cây nhi phân (binary tree ) mà mỗi nút x trong cây thoa

mãn bất đẳng thức kép sau:

( _ ( )) ( ) ( _ ( ))key left child x key x key right child x

Trong đo left_child(x), right_child(x) là các nút con trái và phải cua nút x , key() là hàm

trả về giá tri khoa ở nút tƣơng ứng.

Vi du:

Hinh 5.4. Cây tim kiêm nhi phân BST, tham khao tƣ wikipedia.

Nhận xét:

- Trên cây BST không có hai nút cùng khoá.

- Cây con cua một cây BST là cây BST.

Ƣu điêm chinh cua cây tim kiêm nhi phân la : no cung câp thuât toan săp xêp va tim

kiêm dƣa trên kiêu duyêt thƣ tƣ giƣa (in-order) môt cach rât hiêu qua, và là cấu trúc dữ liệu cơ

bản cho các cấu trúc dữ liệu cao cấp hơn (trƣu tƣơng hơn) nhƣ tâp hơp (set), các mảng liên kêt

B C

A

C B

A

Page 37: Dhhh ctdlgt bai giang cau truc du lieu

32

(associative array), các ánh xạ map, và các cây cân băng tối ƣu nhƣ AVL , cây đo đen . Chúng

ta se xem xet tai sao cây tim kiêm nhi phân lai hiêu qua nhƣ vây.

3.3.2. Khơi tao cây rông

Thao tac đâu tiên la khai bao câu truc cây va khơi tao môt cây rông đê băt đâu thƣc hiên

các thao tác khác.

Ở đây ta giả sư cây tim kiêm nhi phân chỉ chứa các khoa là các số nguyên dƣơng .

Khai bao cây tim kiêm nhi phân trong ngôn ngƣ C nhƣ sau :

// khai bao cau truc cay tim kiem nhi phan

typedef struct tree

{

int key;

struct tree *left,*right;

}BSTree;

Đê khơi tao môt cây rông ta khai bao gôc cua cây va gan cho gôc đo băng NULL :

// cay

BSTree **root;

*root = NULL;

3.3.3. Chen thêm một nút mơi vào cây

Đê chen môt nut mơi vao cây ta xuât phat tƣ gôc cua cây , ta goi đo la nut đang xet . Nêu

nhƣ nut đang xet co khoa băng vơi khoa cân chen vao cây thi xay ra hiên tƣơng trung khoa ,

thuât toan kêt thuc vơi thông báo trùng khoa. Nêu nhƣ nut đang xet la môt nut ngoai (external

nodes) thi ta tạo một nút mơi và gán các trƣờng thông tin tƣơng ứng cho nút đo , gán các con

cua nút đo băng NULL.

// them mot nut moi vao cay, gia tri khoa cua nut moi luu trong bien toan cuc newkey

void insert(BSTree **root)

{

if(*root==NULL)

{

*root=calloc(1,sizeof(BSTree));

(*root)->key = newkey;

(*root)->left=NULL;

(*root)->right=NULL;

}else{

if((*root)->key>newkey)

insert(&((*root)->left));

else

if((*root)->key<newkey)

insert(&((*root)->right));

else

printf("\nError: Duplicate key");

}

}

Thuât toan trên sƣ dung bô nhơ (log n) trong trƣơng hơp trung binh va (n) trong

trƣơng hơp tôi nhât . Độ phức tạp thuật toán băng vơi độ cao cua cây , tƣc la O (log n) trong

trƣơng hơp trung binh đôi vơi hâu hêt cac cây, nhƣng se la (n) trong trƣơng hơp xâu nhât.

Cũng nên chú ý là các nút mơi luôn đƣợc chen vào các nút ngoài cua cây tim kiêm nhi

phân, gôc cua cây không thay đôi trong qua trinh chen thêm nut vao cây .

Page 38: Dhhh ctdlgt bai giang cau truc du lieu

33

3.3.4. Xóa bo khoi cây một nút

Khi xoa bo môt nut X khoi cây (dƣa trên gia tri khoa), chúng ta chia ra một số trƣờng

hơp sau:

X la môt nut la: khi đo viêc xoa nut không lam anh hƣơng tơi cac nut khac , ta chi viêc

xoa bo nút đo khoi cây.

X chi co môt nut con (trái hoăc phải ): khi đo ta đƣa nu t con duy nhât cua X lên thay

cho nut X va xoa bo X.

Con nêu X là một nút trong và co hai con, ta se co hai lƣa chon, môt la tim nut hâu duê

nho nhất bên nhánh phải cua X (gọi là Y), thay khoa cua Y lên X va xoa bo Y . Cách thƣ hai la

tim nút hậu duệ lơn nhất bên nhánh trái cua X (gọi là Z), thay khoa cua Z lên X va xoa bo Z .

Các thao tác vơi Y hoăc Z đƣợc lăp lại tƣơng tự nhƣ đối vơi X.

Hinh minh họa:

Hinh 5.5. Xoa nút trên cây BST, tham khao tƣ wikipedia

Do cac nut thƣc sƣ bi xoa trong trƣơng hơp thƣ ba se co thê rơi vao trƣơng hơp 1 hoăc 2

(là các nút lá hoăc các nút chỉ co 1 con), đông thơi nut bi xoa se co khoa nho hơn hai con cua

X nên trong cai đăt ta nên tranh chi sƣ dung môt phƣơng phap , vi co thể dẫn tơi tinh huống

mât tinh cân băng cua cây.

Viêc cai đăt thuât toan xoa môt nut trên cây tim kiêm nhi phân không đơn gian nhƣ viêc

mô ta thuât toan xoa ơ trên . Trƣơc hêt ta se xuât phat tƣ gôc cua cây đê đi tim nut chƣa khoa

cân xoa trên cây . Trong qua trinh nay điêu quan trong la ta xac đinh ro nut cân xoa (biên p

trong đoan ma chƣơng trinh bên dƣơi ) là một nút lá, hay la môt nút chỉ co một con , hay la nut

co đầy đu cả hai con . Dù trong trƣờng hợp nào thi chúng ta cũng cần xác đinh nút cha cua nút

p (nút q), và p là con trái hay con phải cua q . Đê xac đinh cac trƣơng hơp trên ta sƣ dung mô t

biên cơ f, f băng 0 tƣơng ƣng vơi viêc nut cân xoa la gôc cua cây , f băng 1 tƣơng ƣng vơi p la

con phai cua q, và f băng 2 tƣơng ƣng vơi p la con trai cua q.

Cài đăt băng C cua thao tác xoa một nút khoi cây BST:

// xoa bo mot khoa khoi cay

void del(BSTree ** root, int key)

{

BSTree *p, *q, *r;

int f=0;

p = *root;

q = NULL;

while(p!=NULL&&p->key!=key)

{

q = p;

if(p->key<key)

{

Page 39: Dhhh ctdlgt bai giang cau truc du lieu

34

f = 1;

p = p->right;

}

else

{

f = 2;

p = p->left;

}

}

if(p!=NULL)

{

if(p->right==NULL)

{

if(f==1)

{

q->right=p->left;

free(p);

}

else if(f==2)

{

q->left=p->left;

free(p);

}else

{

*root = p->left;

free(p);

}

}else

{

q = p->right;

r = NULL;

while(q->left)

{

r = q;

q = q->left;

}

p->key = q->key;

if(r==NULL)

p->right = q->right;

else

r->left = q->right;

free(q);

}

}

}

Măc du viêc xoa cây không phai luôn đoi hoi phai duyêt tƣ gôc xuông thƣc hiê n ơ môt

nút lá nhƣng tinh huống này luôn co thể xảy ra (duyêt qua tƣng nut tơi môt nut la ), khi đo đô

phƣc tap cua thuât toan xoa cây tƣơng đƣơng vơi đô cao cua cây (tinh huống tôi nhất).

3.3.5. Tìm kiếm trên cây

Viêc tim kiêm trên cây nhi phân tim kiêm giông nhƣ khi ta thêm môt nut mơi vao cây .

Dƣa trên khoa tim kiêm key ta xuât phat tƣ gôc , gọi nút đang xét là X . Nêu khoa cua X băng

Page 40: Dhhh ctdlgt bai giang cau truc du lieu

35

vơi key, thi kêt thúc và trả về X . Nêu X la môt nut lá thi kêt quả trả về NULL (cũng chinh là

X). Nêu khoa cua X nho hơn key thi ta lăp lai thao tac tim kiêm vơi nut con phai cua X ,

ngƣơc lai thi tiên hanh tim kiêm vơi nut con trai cua X.

Độ phức tạp cua thuật toán nà y băng vơi đô phƣc tap cua thuât toan chen môt nut mơi

vào cây.

Cài đăt cua thuật toán đƣợc để lại nhƣ một bài tập dành cho các bạn độc giả.

3.3.6. Duyêt cây

Duyêt cây (tree travel) là thao tác duyệt qua (đên thăm) tât cả các nút trên cây.

Co nhiều cách để duyệt một cây , chăng han nhƣ duyêt theo chiêu sâu (DFS), duyêt theo

chiêu rông (BFS), nhƣng ơ đây ta phân chia cac cach duyêt môt cây BST dƣa trên thƣ tƣ đên

thăm nut gôc, nút con trái, và nút con phải cua gốc.

Cu thể co ba cách duyệt một cây BST: duyêt thƣ tƣ trƣơc, thƣ tƣ giƣa, thƣ tƣ sau.

Đê minh hoa kêt qua cua cac cach duyêt cây ta xet cây vi du sau :

Hinh 5.6. Cây tim kiêm nhi phân, tham khao tƣ wikipedia

Duyêt thƣ tƣ trƣơc (pre-order traversal):

Thăm gôc (visit root).

Duyêt cây con trai theo thƣ tƣ trƣơc

Duyêt cây con phai theo thƣ tƣ trƣơc .

Cu thể thuật toán đƣợc cài đăt nhƣ sau:

// duyet theo thu tu truoc

void pre_order(BSTree *node)

{

if(node!=NULL)

{

visit(node); // ham tham mot nut, don gian la in gia tri khoa

pre_order(node->left);

pre_order(node->right);

}

}

Kêt qua duyêt cây theo thƣ tƣ trƣơc : 8, 3, 1, 6, 4, 7, 10, 14, 13.

Trong cach duyêt theo thƣ tự trƣơc, gôc cua cây luôn đƣơc thăm đâu tiên.

Duyêt thƣ tƣ giƣa (in-order traversal):

Duyêt cây con trai theo thƣ tƣ giƣa

Thăm gôc

Page 41: Dhhh ctdlgt bai giang cau truc du lieu

36

Duyêt cây con phai theo thƣ tƣ giƣa .

Kêt qua duyêt cây theo thƣ tƣ trƣơc : 1, 3, 4, 6, 7, 8, 10, 13, 14.

Môt điêu dê nhân thây la cac khoa cua cây khi duyêt theo thƣ tƣ giƣa xuât hiên theo thƣ

tƣ tăng dân.

Duyêt thƣ tƣ sau (post-order traversal):

Duyêt cây con trai theo thƣ tƣ sau

Duyêt cây con phai theo thƣ tƣ sau

Thăm gôc

Kêt qua duyêt cây theo thƣ tƣ sau : 1, 4, 7, 6, 3, 13, 14, 10, 8.

Trong cach duyêt nay, gôc đƣơc thăm sau cung.

Nhận xét: - Khi duyệt trung tự (InOrder) cây BST ta đƣợc một dãy co thứ tự tăng.

Cài đăt băng C cua hai cách duyệt sau đƣợc dành cho các bạn độc giả nhƣ một bài tập.

3.3.7. Cài đặt cây BST

Cây TKNP, trƣơc hêt, là một cây nhi phân. Do đo, ta co thể áp dung các cách cài đăt

nhƣ đã trinh bày trong phần cây nhi phân. Sẽ không co sự khác biệt nào trong việc cài đăt cấu

trúc dữ liệu cho cây TKNP so vơi cây nhi phân, nhƣng tất nhiên, sẽ co sự khác biệt trong các

giải thuật thao tác trên cây TKNP nhƣ tim kiêm, thêm hoăc xoá một nút trên cây TKNP để

luôn đảm bảo tinh chất cuả cây TKNP.

Một cách cài đăt cây TKNP thƣờng găp là cài đăt băng con tro. Mỗi nút cua cây nhƣ là

một mẩu tin (record) co ba trƣờng: một trƣờng chứa khoá, hai trƣờng kia là hai con tro tro

đên hai nút con (nêu nút con vắng măt ta gán con tro băng NIL)

Khai báo nhƣ sau

typedef <kiểu dữ liệu cua khoá> KeyType;

typedef struct Node

{

KeyType Key;

Node* Left,Right;

}

typedef Node* Tree;

Khởi tạo cây TKNP rỗng Ta cho con tro quản lý nút gốc (Root) cua cây băng NULL.

void MakeNullTree(Tree *Root)

{

(*Root)=NULL;

}

Tìm kiếm một nút có khóa cho trƣơc trên cây TKNP Ðể tim kiêm 1 nút co khoá x trên cây TKNP, ta tiên hành từ nút gốc băng cách so sánh

khoá cua nút gốc vơi khoá x.

- Nêu nút gốc băng NULL thi không co khoá x trên cây.

- Nêu x băng khoá cua nút gốc thi giải thuật dừng và ta đã tim đƣợc nút chứa khoá x.

- Nêu x lơn hơn khoá cua nút gốc thi ta tiên hành (một cách đệ qui) việc tim khoá x

trên cây con bên phải.

- Nêu x nho hơn khoá cua nút gốc thi ta tiên hành (một cách đệ qui) việc tim khoá x

trên cây con bên trái.

Ví dụ: tìm nút có khoá 30 trong cây ở trong hình III.15

- So sánh 30 vơi khoá nút gốc là 20, vi 30 > 20 vậy ta tim tiêp trên cây con bên phải,

tức là cây co nút gốc co khoá là 35.

- So sánh 30 vơi khoá cua nút gốc là 35, vi 30 < 35 vậy ta tim tiêp trên cây con bên

Page 42: Dhhh ctdlgt bai giang cau truc du lieu

37

trái, tức là cây co nút gốc co khoá là 22.

- So sánh 30 vơi khoá cua nút gốc là 22, vi 30 > 22 vậy ta tim tiêp trên cây con bên

phải, tức là cây co nút gốc co khoá là 30.

- So sánh 30 vơi khoá nút gốc là 30, 30 = 30 vậy đên đây giải thuật dừng và ta tim

đƣợc nút chứa khoá cần tim.

- Hàm dƣơi đây trả về kêt quả là con tro tro tơi nút chứa khoá x hoăc NULL nêu

không tim thấy khoá x trên cây TKNP.

Tree Search(KeyType x,Tree Root)

{

if (Root == NULL) return NULL; //không tim thấy khoá x

else if (Root->Key == x) /* tim thấy khoá x */

return Root;

else if (Root->Key < x) //tim tiêp trên cây bên phải

return Search(x,Root->right);

else

//tim tiêp trên cây bên trái

return Search(x,Root->left);

}

Câu hoi ôn tập:

Cây tim kiêm nhi phân đƣợc tổ chức nhƣ thê nào để quá trinh tim kiêm đƣợc

hiệu quả nhất?

Nhận xét: giải thuật này sẽ rất hiệu quả về mặt thời gian nếu cây TKNP đƣợc tổ chức

tốt, nghĩa là cây tƣơng đối "cân bằng". Về chủ dề cây cân bằng các bạn có thể tham khảo

thêm trong các tài liệu tham khảo của môn này.

Thêm một nút có khóa cho trƣơc vào cây TKNP Theo dinh nghia cây tim kiêm nhi phân ta thấy trên cây tim kiêm nhi phân không co

hai nút co cùng một khoá. Do đo, nêu ta muốn thêm một nút co khoá x vào cây TKNP thì

trƣơc hêt ta phải tim kiêm để xác dinh co nút nào chứa khoá x chƣa. Nêu co thi giải thuật kêt

thúc (không làm gi cả!). Ngƣợc lại, sẽ thêm một nút mơi chứa khoá x này. Việc thêm một

khoá vào cây TKNP là việc tim kiêm và thêm một nút, tất nhiên, phải đảm bảo cấu

trúc cây TKNP không bi phá vỡ. Giải thuật cu thể nhƣ sau:

Ta tiên hành từ nút gốc băng cách so sánh khoa cuả nút gốc vơi khoá x.

- Nêu nút gốc băng NULL thi khoá x chƣa co trên cây, do đo ta thêm một nút mơi

chứa khoá x.

- Nêu x băng khoá cua nút gốc thi giải thuật dừng, trƣờng hợp này ta không thêm nút.

- Nêu x lơn hơn khoá cua nút gốc thi ta tiên hành (một cách đệ qui) giải thuật này trên

cây con bên phải.

- Nêu x nho hơn khoá cua nút gốc thi ta tiên hành (một cách đệ qui) giải thuật này trên

cây con bên trái.

Ví dụ: thêm khoá 19 vào cây ở trong hình III.15

So sánh 19 vơi khoá cua nút gốc là 20, vi 19 < 20 vậy ta xét tiêp đên cây bên trái, tức

là cây co nút gốc co khoá là 10.

- So sánh 19 vơi khoá cua nút gốc là 10, vi 19 > 10 vậy ta xét tiêp đên cây bên phải,

tức là cây co nút gốc co khoá là 17.

- So sánh 19 vơi khoá cua nút gốc là 17, vi 19 > 17 vậy ta xét tiêp đên cây bên phải.

Nút con bên phải băng NULL, chứng to răng khoá 19 chƣa co trên cây, ta thêm nút mơi chứa

khoá 19 và nút mơi này là con bên phải cua nút co khoá là 17, xem hinh III.16

Hình III.16: Thêm khoá 19 vào cây hình III.15 Thu tuc sau dây tiên hành việc thêm một khoá vào cây TKNP.

void InsertNode(KeyType x,Tree *Root ){

if (*Root == NULL){ /* thêm nút mơi chứa khoá x */

Page 43: Dhhh ctdlgt bai giang cau truc du lieu

38

(*Root)=(Node*)malloc(sizeof(Node));

(*Root)->Key = x;

(*Root)->left = NULL;

(*Root)->right = NULL;

}

else

if (x < (*Root)->Key)

InsertNode(x,Root->left);

else if (x>(*Root)->Key) InsertNode(x,Root->right);

}

Xóa một nút có khóa cho trƣơc ra khoi cây TKNP Giả sư ta muốn xoá một nút co khoá x, trƣơc hêt ta phải tim kiêm nút chứa khoá x trên

cây.

Việc xoá một nút nhƣ vậy, tất nhiên, ta phải bảo đảm cấu trúc cây TKNP không bi phá

vỡ. Ta co các trƣờng hợp nhƣ hinh III.17:

Hinh III.17 Vi du về giải thuật xoa nút trên cây

- Nêu không tim thấy nút chứa khoá x thi giải thuật kêt thúc.

- Nêu tim găp nút N co chứa khoá x, ta co ba trƣờng hợp sau (xem hinh III.17)

- Nêu N là lá ta thay no bởi NULL.

- N chỉ co một nút con ta thay no bởi nút con cua no.

- N có hai nút con ta thay no bởi nút lơn nhất trên cây con trái cua no (nút cực phải cua

cây con trái) hoăc là nút bé nhất trên cây con phải cua no (nút cực trái cua cây con phải).

Trong giải thuật sau, ta thay x bởi khoá cua nút cực trái cua cây con bên phải rôi ta xoá nút

cực trái này. Việc xoá nút cực trái cua cây con bên phải sẽ roi vào một trong hai trƣờng hợp

trên.

Giải thuật xoá một nút có khoá nho nhất

Hàm dƣơi dây trả về khoá cua nút cực trái, dông thời xoá nút này.

KeyType DeleteMin (Tree *Root )

{

KeyType k;

if ((*Root)->left == NULL){

k=(*Root)->key;

(*Root) = (*Root)->right;

return k;

}

else return DeleteMin(Root->left);

}

Thủ tục xóa một nút có khoá cho trƣơc trên cây TKNP void DeleteNode(key X, Tree *Root)

{

if ((*Root)!=NULL)

if (x < (*Root)->Key)

DeleteNode(x,Root->left)

else if (x > (*Root)->Key)

DeleteNode(x,Root->right) else

if ((*Root)->left==NULL)&&((*Root)->right==NULL)

(*Root)=NULL;

else

if ((*Root)->left == NULL)

(*Root) = (*Root)->right ;

else if ((*Root)->right==NULL)

(*Root) = (*Root)->left;

Page 44: Dhhh ctdlgt bai giang cau truc du lieu

39

else (*Root)->Key = DeleteMin(Root->right);

}

3.4.Cây cân băng – AVL

Trong khoa học máy tinh, một cây AVL là một cây tim kiêm nhi phân tự cân băng, và là cấu

trúc dữ liệu đầu tiên co khả năng này. Trong một cây AVL, tại mỗi nút chiều cao cua hai cây

con sai khác nhau không quá một. Hiệu quả là các phép chen (insertion), và xoa (deletion)

luôn chỉ tốn thời gian O(log n) trong cả trƣờng hợp trung binh và trƣờng hợp xấu nhất. Phép

bổ sung và loại bo co thể cần đên việc tái cân băng băng một hoăc nhiều phép quay.

3.4.1. Cây nhị phân cân băng hoàn toàn

a. Định nghĩa

Cây cân băng hoàn toàn là cây nhi phân tim kiêm mà tại mỗi nút cua no, số nút cua cây con

trái chênh lệch không quá một so vơi số nút cua cây con phải.

b. Đánh giá

Một cây rất kho đạt đƣợc trạng thái cân băng hoàn toàn và cũng rất dễ mất cân băng vi khi

thêm hay huy các nút trên cây co thể làm cây mất cân băng (xác suất rất lơn), chi phi cân băng

lại cây lơn vi phải thao tác trên toàn bộ cây.

Tuy nhiên nêu cây cân đối thi việc tim kiêm sẽ nhanh. Đối vơi cây cân băng hoàn toàn, trong

trƣờng hợp xấu nhất ta chỉ phải tim qua log2n phần tư (n là số nút trên cây).

Sau đây là vi du một cây cân băng hoàn toàn (CCBHT):

2n. Đây chinh là lý do cho phép bảo đảm khả năng tim

kiêm nhanh trên CTDL này.

Do CCBHT là một cấu trúc kém ổn đinh nên trong thực tê không thể sư dung. Nhƣng ƣu điểm

cua no lại rất quan trọng. Vi vậy, cần đƣa ra một CTDL khác co đăc tinh giống CCBHT

nhƣng ổn đinh hơn.

Nhƣ vậy, cần tim cách tổ chức một cây đạt trạng thái cân băng yêu hơn và việc cân băng lại

chỉ xảy ra ở phạm vi cuc bộ nhƣng vẫn phải bảo đảm chi phi cho thao tác tim kiêm đạt ở mức

O(log2n).

3.4.2. CÂY NHỊ PHÂN CÂN BẰNG (AVL)

Page 45: Dhhh ctdlgt bai giang cau truc du lieu

40

a. Định nghĩa:

Cây nhi phân tim kiêm cân băng là cây mà tại mỗi nút cua no độ cao cua cây con trái và cua

cây con phải chênh lệch không quá một.

Dƣơi đây là vi du cây cân băng (lƣu ý, cây này không phải là cây cân băng hoàn toàn):

Dễ dàng thấy CCBHT là cây cân băng. Điều ngƣợc lại không đúng.

b. Lịch sư cây cân băng (AVL Tree)

AVL là tên viêt tắt cua các tác giả ngƣời Nga đã đƣa ra đinh nghia cua cây cân băng Adelson-

Velskii và Landis (1962). Vi lý do này, ngƣời ta gọi cây nhi phân cân băng là cây AVL. Tù

nay về sau, chúng ta sẽ dùng thuật ngữ cây AVL thay cho cây cân băng.

Từ khi đƣợc giơi thiệu, cây AVL đã nhanh chong tim thấy ứng dung trong nhiều bài toán

khác nhau. Vi vậy, no mau chong trở nên thinh hành và thu hút nhiều nghiên cứu. Từ cây

AVL, ngƣời ta đã phát triển thêm nhiều loại CTDL hữu dung khác nhƣ cây đo-đen (Red-

Black Tree), B-Tree, …

c. Chiều cao của cây AVL

Một vấn đề quan trọng, nhƣ đã đề cập đên ở phần trƣơc, là ta pjải khẳng đinh cây AVL n nút

phải co chiều cao khoảng log2(n).

Để đánh giá chinh xác về chiều cao cua cây AVL, ta xét bài toán: cây AVL co chiều cao h sẽ

phải co tối thiểu bao nhiêu nút ?

Gọi N(h) là số nút tối thiểu cua cây AVL co chiều cao h.

Page 46: Dhhh ctdlgt bai giang cau truc du lieu

41

Ta có N(0) = 0, N(1) = 1 và N(2) = 2.

Cây AVL tối thiểu co chiều cao h sẽ co 1 cây con AVL tối thiểu chiều cao h-1 và 1 cây con

AVL tối thiểu chiều cao h-2. Nhƣ vậy:

N(h) = 1 + N(h-1) + N(h-2) (1)

Ta lại co: N(h-1) > N(h-2)

Nên từ (1) suy ra:

N(h) > 2N(h-2)

N(h) > 22N(h-4)

N(h) > 2iN

(h-2i)

h/2-1

2(N(h)) + 2

Nhƣ vậy, cây AVL co chiều cao O(log2(n)).

Vi du: cây AVL tối thiểu co chiều cao h=4

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

Chỉ số cân băng của một nút:

Định nghĩa: Chỉ số cân băng cua một nút là hiệu cua chiều cao cây con phải và cây con trái

cua no.

Đối vơi một cây cân băng, chỉ số cân băng (CSCB) cua mỗi nút chỉ co thể mang một trong ba

giá tri sau đây:

CSCB(p) = 0 <=> Độ cao cây trái (p) = Độ cao cây phải (p)

Page 47: Dhhh ctdlgt bai giang cau truc du lieu

42

CSCB(p) = 1 <=> Độ cao cây trái (p) < Độ cao cây phải (p)

CSCB(p) =-1 <=> Độ cao cây trái (p) > Độ cao cây phải (p)

Để tiện trong trinh bày, chúng ta sẽ ký hiệu nhƣ sau:

p->balFactor = CSCB(p);

Độ cao cây trái (p) ký hiệu là hL

Độ cao cây phải(p) ký hiệu là hR

Để khảo sát cây cân băng, ta cần lƣu thêm thông tin về chỉ số cân băng tại mỗi nút. Lúc đo,

cây cân băng co thể đƣợc khai báo nhƣ sau:

typedef struct tagAVLNode {

char balFactor; //Chỉ số cân bằng

Data key;

struct tagAVLNode* pLeft;

struct tagAVLNode* pRight;

}AVLNode;

typedef AVLNode *AVLTree;

Để tiện cho việc trinh bày, ta đinh nghia một số hăng số sau:

#define LH -1 //Cây con trái cao hơn

#define EH -0 //Hai cây con băng nhau

#define RH 1 //Cây con phải cao hơn

e. Đánh giá cây AVL

Cây cân băng là CTDL ổn đinh hơn hẳn CCBHT vi chỉ khi thêm huy làm cây thay đổi chiều

cao các trƣờng hợp mất cân băng mơi co khả năng xảy ra.

Cây AVL vơi chiều cao đƣợc khống chê sẽ cho phép thực thi các thao tác tim thêm huy vơi

chi phí O (log2(n)) và bảo đảm không suy biên thành O(n).

f. Các thao tác cơ bản trên cây AVL

Ta nhận thấy trƣờng hợp thêm hay huy một phần tư trên cây co thể làm cây tăng hay giảm

chiều cao, khi đo phải cân băng lại cây. Việc cân băng lại một cây sẽ phải thực hiện sao cho

chỉ ảnh hƣởng tối thiểu đên cây nhăm giảm thiểu chi phi cân băng. Nhƣ đã noi ở trên, cây cân

Page 48: Dhhh ctdlgt bai giang cau truc du lieu

43

băng cho phép việc cân băng lại chỉ xảy ra trong giơi hạn cuc bộ nên chúng ta co thể thực

hiện đƣợc muc tiêu vừa nêu.

Nhƣ vậy, ngoài các thao tác binh thƣờng nhƣ trên CNPTK, các thao tác đăc trƣng cua cây

AVL gôm:

Thêm một phần tư vào cây AVL.

Hủy một phần tư trên cây AVL.

Cân băng lại một cây vừa bị mất cân băng.

g. CÁC TRƢỜNG HỢP MẤT CÂN BẰNG

Ta sẽ không khảo sát tinh cân băng cua 1 cây nhi phân bất kỳ mà chỉ quan tâm đên các khả

năng mất cân băng xảy rakhi thêm hoăc huy một nút trên cây AVL.

Nhƣ vậy, khi mất cân băng, độ lệch chiều cao giữa 2 cây con sẽ là 2. Ta co 6 khả năng sau:

Trƣờng hợp 1: cây T lệch về bên trái (có 3 khả năng)

Trƣờng hợp 2: cây T lệch về bên phải

Ta co các khả năng sau:

Page 49: Dhhh ctdlgt bai giang cau truc du lieu

44

Ta co thể thấy răng các trƣờng hợp lệch về bên phải hoàn toàn đối xứng vơi các trƣờng hợp

lệch về bên trái. Vi vậy ta chỉ cần khảo sát trƣờng hợp lệch về bên trái. Trong 3 trƣờng hợp

lệch về bên trái, trƣờng hợp T1 lệch phải là phức tạp nhất. Các trƣờng hợp con lại giải quyêt

rất đơn giản.

Sau đây, ta sẽ khảo sát và giải quyêt từng trƣờng hợp nêu trên.

T/h 1.1: cây T1 lệch về bên trái. Ta thực hiện phép quay đơn Left-Left

T/h 1.2: cây T1 không lệch. Ta thực hiện phép quay đơn Left-Left

Page 50: Dhhh ctdlgt bai giang cau truc du lieu

45

T/h 1.3: cây T1 lệch về bên phải. Ta thực hiện phép quay kép Left-Right

Do T1 lệch về bên phải ta không thể áp dung phép quay đơn đã áp dung trong 2 trƣờng hợp

trên vi khi đo cây T sẽ chuyển từ trạng thái mất cân băng do lệch trái thành mất cân băng do

Hinh vẽ dƣơi đây minh họa phép quay kép áp dung cho trƣờng hợp này:

Lƣu ý răng, trƣơc khi cân băng cây T co chiều cao h+2 trong cả 3 trƣờng hợp 1.1, 1.2 và 1.3.

Sau khi cân băng, trong 2 trƣờng hợp 1.1 và 1.3 cây co chiều cao h+1; con ở trƣờng hợp 1.2

cây vẫn co chiều cao h+2. Và trƣờng hợp này cũng là trƣờng hợp duy nhất sau khi cân băng

Thao tác cân băng lại trong tất cả các trƣờng hợp đều coù độ phức tạp O(1).

Vơi những xem xét trên, xét tƣơng tự cho trƣờng hợp cây T lệch về bên phải, ta co thể xây

dựng 2 hàm quay đơn và 2 hàm quay kép sau:

//quay đơn Left-Left

void rotateLL(AVLTree &T)

{ AVLNode* T1 = T->pLeft;

T->pLeft = T1->pRight;

T1->pRight = T;

switch(T1->balFactor) {

case LH: T->balFactor = EH;

Page 51: Dhhh ctdlgt bai giang cau truc du lieu

46

T1->balFactor = EH; break;

case EH: T->balFactor = LH;

T1->balFactor = RH; break;

}

T = T1;

}

//quay đơn Right-Right

void rotateRR(AVLTree &T)

{ AVLNode* T1 = T->pRight;

T->pRight = T1->pLeft;

T1->pLeft = T;

switch(T1->balFactor) {

case RH: T->balFactor = EH;

T1->balFactor = EH; break;

case EH: T->balFactor = RH; break;

T1->balFactor = LH; break;

}

T = T1;

}

//quay kép Left-Right

void rotateLR(AVLTree &T)

{ AVLNode* T1 = T->pLeft;

AVLNode* T2 = T1->pRight;

T->pLeft = T2->pRight;

T2->pRight = T;

T1->pRight = T2->pLeft;

Page 52: Dhhh ctdlgt bai giang cau truc du lieu

47

T2->pLeft = T1;

switch(T2->balFactor) {

case LH: T->balFactor = RH;

T1->balFactor = EH; break;

case EH: T->balFactor = EH;

T1->balFactor = EH; break;

case RH: T->balFactor = EH;

T1->balFactor = LH; break;

}

T2->balFactor = EH;

T = T2;

}

//quay kép Right-Left

void rotateRL(AVLTree &T)

{ AVLNode* T1 = T->pRight;

AVLNode* T2 = T1->pLeft;

T->pRight = T2->pLeft;

T2->pLeft = T;

T1->pLeft = T2->pRight;

T2->pRight = T1;

switch(T2->balFactor) {

case RH: T->balFactor = LH;

T1->balFactor = EH; break;

case EH: T->balFactor = EH;

T1->balFactor = EH; break;

case LH: T->balFactor = EH;

Page 53: Dhhh ctdlgt bai giang cau truc du lieu

48

T1->balFactor = RH; break;

}

T2->balFactor = EH;

T = T2;

}

Để thuận tiện, ta xây dựng 2 hàm cân băng lại khi cây bi lệch trái hay lệch phải nhƣ sau:

//Cân băng khi cây bi lêch về bên trái

int balanceLeft(AVLTree &T)

{ AVLNode* T1 = T->pLeft;

switch(T1->balFactor) {

case LH: rotateLL(T); return 2;

case EH: rotateLL(T); return 1;

case RH: rotateLR(T); return 2;

}

return 0;

}

//Cân băng khi cây bi lêch về bên phải

int balanceRight(AVLTree &T)

{ AVLNode* T1 = T->pRight;

switch(T1->balFactor) {

case LH: rotateRL(T); return 2;

case EH: rotateRR(T); return 1;

case RH: rotateRR(T); return 2;

}

return 0;

}

Page 54: Dhhh ctdlgt bai giang cau truc du lieu

49

h. Thêm một phần tư trên cây AVL

Việc thêm một phần tư vào cây AVL diễn ra tƣơng tự nhƣ trên CNPTK. Tuy nhiên, sau khi

thêm xong, nêu chiều cao cua cây thay đổi, từ vi tri thêm vào, ta phải lần ngƣợc lên gốc để

kiểm tra xem co nút nào bi mất cân băng không. Nêu co, ta phải cân băng lại ở nút này.

Việc cân băng lại chỉ cần thực hiện 1 lần tại nơi mất cân băng. (Tại sao ? Hd: chú ý những

khả năng mất cân bằng có thể)

Hàm insert trả về giá tri –1, 0, 1 khi không đu bộ nhơ, găp nút cũ hay thành công. Nêu sau khi

thêm, chiều cao cây bi tăng, giá tri 2 sẽ đƣợc trả về:

int insertNode(AVLTree &T, DataType X)

{ int res;

if(T) {

if(T->key == X) return 0; //đã co

if(T->key > X) {

res = insertNode(T->pLeft, X);

if(res < 2) return res;

switch(T->balFactor) {

case RH: T->balFactor = EH;

return 1;

case EH: T->balFactor = LH;

return 2;

case LH: balanceLeft(T); return 1;

}

}else {

res = insertNode(T-> pRight, X);

if(res < 2) return res;

switch(T->balFactor) {

case LH: T->balFactor = EH;

return 1;

Page 55: Dhhh ctdlgt bai giang cau truc du lieu

50

case EH: T->balFactor = RH;

return 2;

case RH: balanceRight(T); return 1;

}

}

}

T = new TNode;

if(T == NULL) return -1; //thiêu bộ nhơ

T->key = X; T->balFactor = EH;

T->pLeft = T->pRight = NULL;

return 2; // thành công, chiều cao tăng

}

i. Hủy một phần tư trên cây AVL

Cũng giống nhƣ thao tác thêm một nút, việc huy một phần tư X ra khoi cây AVL thực hiện

giống nhƣ trên CNPTK. Chỉ sau khi huy, nêu tinh cân băng cua cây bi vi phạm ta sẽ thực hiện

việc cân băng lại.

Tuy nhiên việc cân băng lại trong thao tác huy sẽ phức tạp hơn nhiều do co thể xảy ra phản

ứng dây chuyền. (Tại sao ?)

Hàm delNode trả về giá tri 1, 0 khi huy thành công hoăc không co X trong cây. Nêu sau khi

huỷ, chiều cao cây bi giảm, giá tri 2 sẽ đƣợc trả về:

int delNode(AVLTree &T, DataType X)

{ int res;

if(T==NULL) return 0;

if(T->key > X) {

res = delNode (T->pLeft, X);

if(res < 2) return res;

switch(T->balFactor) {

case LH: T->balFactor = EH;

Page 56: Dhhh ctdlgt bai giang cau truc du lieu

51

return 2;

case EH: T->balFactor = RH;

return 1;

case RH: return balanceRight(T);

}

}

if(T->key < X) {

res = delNode (T->pRight, X);

if(res < 2) return res;

switch(T->balFactor) {

case RH: T->balFactor = EH;

return 2;

case EH: T->balFactor = LH;

return 1;

case LH: return balanceLeft(T);

}

}else { //T->key == X

AVLNode* p = T;

if(T->pLeft == NULL) {

T = T->pRight; res = 2;

}else if(T->pRight == NULL) {

T = T->pLeft; res = 2;

}else { //T có cả 2 con

res=searchStandFor(p,T->pRight);

if(res < 2) return res;

switch(T->balFactor) {

Page 57: Dhhh ctdlgt bai giang cau truc du lieu

52

case RH: T->balFactor = EH;

return 2;

case EH: T->balFactor = LH;

return 1;

case LH: return balanceLeft(T);

}

}

delete p;

return res;

}

}

//Tim phần tư thê mạng

int searchStandFor(AVLTree &p, AVLTree &q)

{ int res;

if(q->pLeft) {

res = searchStandFor(p, q->pLeft);

if(res < 2) return res;

switch(q->balFactor) {

case LH: q->balFactor = EH;

return 2;

case EH: q->balFactor = RH;

return 1;

case RH: return balanceRight(T);

}

}else {

p->key = q->key;

Page 58: Dhhh ctdlgt bai giang cau truc du lieu

53

p = q;

q = q->pRight;

return 2;

}

}

k. Nhận xét

Thao tác thêm một nút co độ phức tạp O(1).

Thao tác huy một nút co độ phức tạp O(h).

Vơi cây cân băng trung binh 2 lần thêm vào cây thi cần một lần cân băng lại; 5 lần huy thi

cần một lần cân băng lại.

Việc huỷ 1 nút co thể phải cân băng dây chuyền các nút từ gốc cho đên phần tư bi huỷ trong

khi thêm vào chỉ cần 1 lần cân băng cuc bộ.

Độ dài đƣờng tim kiêm trung binh trong cây cân băng gần băng cây cân băng hoàn toàn

log2n, nhƣng việc cân băng lại đơn giản hơn nhiều.

Một cây cân băng không bao giờ cao hơn 45% cây cân băng hoàn toàn tƣơng ứng dù

số nút trên cây là bao nhiêu.

Bài tập

1. Viêt chƣơng trinh tạo cây BST vơi thông tin tại mỗi nút là các số nguyên

2. Viêt chƣơng trinh tim kiêm một nút trong cây BST ở câu 1

3. Viêt chƣơng trinh xoa một nút trong cây BST ở câu 1

4. Cài đăt hoàn thiện các hàm cua cây AVL

Page 59: Dhhh ctdlgt bai giang cau truc du lieu

54

CHƢƠNG 4. BẢNG BĂM (HASH TABLE)

Phép băm là một thuật toán đƣợc đề xuất và hiện thực trên máy tinh từ những năm 50

cua thê kỷ 20. Thuật toán này dựa trên ý tƣởng là chuyển đổi khoá thành một số và sư dung số

này để đánh chỉ số cho bảng dữ liệu.

Nhƣ chúng ta đã biêt các phép toán dựa trên các cấu trúc nhƣ cây, danh sách … chu

yêu đƣợc thực hiện thông qua việc so sánh các phần tư co cấu trúc. Do vậy thời gian thực thi

lâu và phu thuộc vào kich thƣơc các phần tư này. Để khắc phuc ngƣời ta đƣa ra thuật toán sư

dung bảng băm (Hash Table). Các phép toán trên bảng băm co độ phức tạp là O(1) và không

phu thuộc vào kich thƣơc bảng. Dƣơi đây là một số vấn đề chinh mà chúng ta cần quan tâm

trong bảng băm :

Đinh nghia bảng băm.

Hàm băm và các loại hàm băm.

Xung đột và cách xư lý xung đột

4. 1. Định nghĩa bảng băm

4.1.1.Định nghĩa :

Bảng băm là một kiểu dữ liệu trừu tƣợng cho phép lƣu trữ dữ liệu một cách nhanh

chong và hiệu quả. Về thực chất bảng băm là một mảng co chỉ số là bất cứ loại dữ liệu nào.

Trong khi một mảng thông thƣờng yêu cầu chỉ số cua no phải là số nguyên thi chỉ số bảng

băm lại co thể là một số thực, một xâu, một mảng khác hay thậm chi là một dạng cấu trúc dữ

liệu. Các chỉ số này ngƣời ta gọi chung là khoá ( Key ) và nội dung chỉ đinh bởi các chỉ số

này gọi là các giá tri ( Value ).

Vậy bảng băm là một cấu trúc dữ liệu lƣu trữ một căp dữ liệu Key/Value và cho phép

tìm Key một cách nhanh chong.

Bảng băm sư dung một hàm cho phép biên đổi bất kỳ đối tƣợng nào thành chỉ số phù

hợp cua mảng. Hàm này đƣợc gọi là hàm băm (Hash Function)

Bảng băm co thể đƣợc mô tả nhƣ sau:

Gọi K là tập các khoá.

M là tập các đia chỉ.

HF(k) là hàm băm dùng để ánh xạ một khoá k từ tập khoá k thành một chỉ số trong tập

đia chỉ M.

Page 60: Dhhh ctdlgt bai giang cau truc du lieu

55

Hình 2.1 : Mô tả về hàm băm

Sau đây là vi du về một bảng băm (Hàm băm trong trƣờng hợp này co dạng : h(k) = k

mod 8 trong đo k là khoá).

Hình 2.2 : Một bảng băm đơn giản

Trong bảng băm nhiều khoá co giá tri khác

nhau co thể đƣợc băm thành cùng một chỉ số cua mảng. Hiện tƣợng này gọi là xung đột và

giải quyêt xung đột chinh là muc tiêu cua bất cứ bảng băm nào. Vấn đề này chúng ta sẽ đề cập

đên trong phần 3 cua chƣơng này.

4.1.2.Kích thƣơc của bảng băm :

Kich thƣơc một bảng băm cho biêt số muc vào tối đa mà bảng băm co thể lƣu trữ đƣợc.

Thông thƣờng các giá tri cua khoá đƣợc lƣu trữ vừa đu lấp đầy bảng nhƣng đôi khi các giá tri

này lại vƣợt quá giơi hạn cua mảng. Giải pháp đƣa ra là buộc các khoảng giá tri này năm

trong giơi hạn kich thƣơc cua bảng.

Kich thƣơc bảng phải đƣợc lƣu trữ một cách ngẫu nhiên vi các phƣơng pháp giải quyêt

xung đột trong bảng băm co một số điều kiện về kich thƣơc bảng nhất đinh để đảm bảo thực

thi chinh xác. Tuy nhiên hầu hêt các trƣờng hợp kich thƣơc bảng băm thƣờng đƣợc lựa chọn

là luỹ thừa cua 2 ( 2n )hay một số nguyên tố.

Bảng băm co kich thƣơc là luỹ thừa cua 2 chỉ là một kỳ vọng lơn. Bởi vi kich thƣơc này

cho phép việc tinh toán đia chỉ đƣợc thực hiện dễ dàng hơn và kêt quả co đƣợc nhanh hơn.

Cách để buộc các giá tri năm trong khoảng luỹ thừa cua 2 một cách nhanh chong là sư dung

hàm măt nạ.

Kich thƣơc bảng băm thƣờng đƣợc sư dung là một số nguyên tố. Li do là vi các phép

băm nhin chung là kho hiểu và các phép băm yêu cầu thêm các bƣơc chia cua số nguyên tố

đƣợc trộn lẫn vơi nhau. Măt khác một số phƣơng pháp xư lý xung đột cũng yêu cầu kiểu kich

thƣơc này.

4.1.3. Phân loại :

Co rất nhiều loại bảng băm khác nhau. Thông thƣờng bảng băm đƣợc phân loại theo

cấu trúc hoăc theo cách xư lý xung đột.

1.3.1.Phân loại theo cấu trúc :

Page 61: Dhhh ctdlgt bai giang cau truc du lieu

56

Bảng băm phân loại theo cấu trúc gôm co :

Bảng băm chữ nhật.

Bảng băm tam giác (tam giác trên và tam giác dƣơi ).

Bảng băm đƣờng chéo.

Gọi i, j là các khoá tƣơng ứng vơi phần tư hàng i, cột j. Khi đo một phần tư trong bảng

băm đƣợc xác đinh bởi căp i, j.

a.Bảng băm chữ nhật :

Một phần tư cua bảng đƣợc xác đinh bởi khoá i ở hàng i và khoá j ở hàng j. Tổng quát

vi tri cua phần tư này co thể xác đinh qua công thức :

f(i,j) = n*i + j (n là số cột cua bảng chữ nhật)

Bảng băm hinh chữ nhật đƣợc mô tả bởi một danh sách kề :

0 1 2 … n-1 n n+1 … m*n

b.Bảng băm tam giác :

Bảng băm tam giác trên n cột:

Bảng băm tam giác dƣơi m hàng:

Mỗi phần tư trên bảng tam giác tƣơng ứng vơi hàng i, cột j (i j) và đia chỉ cua no đƣợc

xác đinh qua hàm băm :

f (i,j) = i*(i + 1)/2 + j

c.Bảng băm đường chéo :

Một số loại bảng băm đƣờng chéo co dạng sau :

1.3.2.Phân loại theo cách xử lý xung đột :

Page 62: Dhhh ctdlgt bai giang cau truc du lieu

57

Bảng băm phân loại theo cách này gôm :

Bảng băm sư dung phƣơng pháp nối kêt trực tiêp

Bảng băm vơi phƣơng pháp nối kêt hợp nhất

Bảng băm vơi phƣơng pháp do tuyên

Bảng băm vơi phƣơng pháp do căn bậc 2

Bảng băm vơi phƣơng pháp băm kép

4.1.4.Các phép toán trên bảng băm :

Khởi tạo (Initialize ): Khởi tạo bảng băm, cấp phát vùng nhơ, quy đinh số phần tư cua

bảng ( kich thƣơc cua bảng ).

Kiểm tra rỗng ( Empty ): Kiểm tra liệu bảng băm co rỗng hay không.

Lấy kich thƣơc bảng băm (Size): Lấy số phần tư hiện thời co trong bảng băm.

Tim kiêm ( Search ): Tim một phần tư theo một khoá k cho trƣơc.

Thêm mơi một phần tư ( Insert ): Chen thêm một phần tư vào một vi tri trống cua bảng

băm.

Xoá ( Delete / Removal ): Loại bo một phần tư khoi bảng băm.

Sao chép (Copy ): Tạo một bảng băm mơi trên cơ sở một bảng băm đã co.

Duyệt ( Traverse ): Duyệt các phần tư cua bảng theo một thứ tự nhất đinh.

4.2.Hàm băm và các loại hàm băm :

4.2.1.Hàm băm (Hash Function):

Hàm băm là hàm sư dung để ánh xạ tập các khoá đại diện cho các muc dữ liệu trong

bảng thành đia chỉ nơi chứa muc dữ liệu đo.

Hình 2.3 : Mô hình hàm băm

Khoá trong bảng băm co thể là dạng số hoăc chuối (

xâu ký tự ). Nêu khoá là dạng số thi trƣơc khi áp dung phép băm ta phải lựa chọn các chữ số,

giơi hạn giá tri, áp dung các thuật toán. Các khoá ở dạng số thƣờng đƣợc chọn co kiểu số

nguyên.

Nêu khoá ở dạng xâu ký tự thi trƣơc khi áp dung phép băm no cần đƣợc biên đổi thành

dạng phù hợp ( Vi du lấy giá tri mã ASCII cua các ký tự chẳng hạn ), chọn lựa những phần

độc lập và co ý nghia nhất trong khoá và lựa chọn một hàm băm phù hợp nhất vơi cấu trúc

cua khoá.

Hàm băm đƣợc chia làm hai dạng chinh : Dạng bảng tra và dạng công thức.

Hàm băm dạng bảng tra :

Page 63: Dhhh ctdlgt bai giang cau truc du lieu

58

Giả sư co bảng tra co khoá là bộ chữ cái tiêng Anh.Bảng co 26 đia chỉ co giá tri từ 0..25.

Khoá a ứng vơi đia chỉ 0, khoá b ứng vơi đia chỉ 1… khoá z ứng vơi đia chỉ 25.

Khoá Đia chỉ Khoá Đia chỉ Khoá Đia chỉ Khoá Đia chỉ

a 0 h 7 o 14 v 21

b 1 i 8 p 15 w 22

c 2 j 9 q 16 x 23

d 3 k 10 r 17 y 24

e 4 l 11 s 18 z 25

f 5 m 12 t 19

g 6 n 13 u 20

Bảng 2.1 : Hàm băm dạng bảng tra

Hàm băm dạng công thức : Hàm băm dạng công thức thƣờng co dạng tổng quát là

HF(k) trong đo k là khoá. Hàm băm dạng này rất đa dạng và không bi ràng buộc bởi bất cứ

tiêu chuẩn nào.

4.2.2.Một số loại hàm băm :

Một hàm băm tốt phải thoả mãn một số điều kiện sau :

Tinh toán nhanh chong và đơn giản.

Các khoá phân bố đều trong bảng.

Ít xảy ra xung đột giữa các khoá.

Gọi P(k) là xác suất khoá k xuất hiện trong bảng. Khi đo vơi mỗi i = 0, 1, …, m

- 1 thì ta có :

Giá tri băm phải độc lập vơi bất cứ phần nào cua dữ liệu nghia là no phải phù hợp

và co tinh ngẫu nhiên.

Sau đây là một số hàm băm đơn giản và phổ biên.

2.2.1.Hàm băm sử dụng phương pháp chia :

Hàm băm này co các đăc điểm sau :

Một khoá đƣợc ánh xạ vào một trong m ô cua bảng thông qua hàm:

HF(k) = k mod m

Trong đo : k là khoá, m là kich thƣơc bảng.

Chỉ sư dung phép chia đơn do đo tốc độ tinh toán nhanh.

Vấn đề đăt ra là phải chọn một giá tri m phù hợp.

ikHk m

kP)(

1)(

Page 64: Dhhh ctdlgt bai giang cau truc du lieu

59

o m chọn không tốt khi no co một trong các giá tri sau :

+ m = 2P , khi đo h(k) sẽ chọn cùng giá tri là p bit cuối cua k.

+ m = 10P, khi đo hàm băm không phu thuộc vào tất cả các số thập phân cua khoá.

+ m = 2P – 1. Nêu khoá là một xâu ký tự đƣợc dich thành các giá tri là luỹ thừa cua 2, thi

hai xâu co thể đƣợc băm thành cùng một giá tri đia chỉ trên bảng.

o Giá tri cua m là tốt khi no là một số nguyên tố và không quá gần vơi giá tri là luỹ

thừa cua 2.

Vi du về cài đăt một hàm băm sư dung phép chia :

Public Function Hash(ByVal Key As Long) As Long

Hash = Key Mod HashTableSize

End Function

2.2.2.Hàm băm sử dụng phương pháp nhân :

Phƣơng pháp nhân co hai bƣơc :

Khoá k đƣợc nhân vơi hăng số A năm trong khoảng 0 < A < 1. Sau đo ngƣời ta sẽ sư

dung phần phân số cua k*A.

Phần phân số noi trên đƣợc nhân vơi m sau đo lấy phần nguyên. Do đo hàm băm co

dạng :

HF(k) = m * (k*A mod 1 )

Trong đo : k là khoá, m là kich thƣơc bảng, A là hăng số.

Một hàm băm sư dung phép nhân muốn co hiệu quả cao phải lựa chọn giá tri m và A

cho phù hợp.

m thƣờng đƣợc chọn là m = 2p.

A đƣợc chọn phu thuộc vào đăc trƣng cua dữ liệu. Một giá tri A tốt đƣợc đề xuất co

giá tri là :

A = )2/)51/((1 = 2/)15( 0.6180339887…

Vi du về cài đăt một hàm băm sư dung phép chia :

Private Const S As Long = 64

Private Const N As Long = 1023

Public Function Hash(ByVal Key As Long) As Long

Hash = ((K * Key) And N) \ S

End Function

2.2.3.Hàm băm sử dụng phép nghịch đảo :

Đây là phƣơng pháp trong đo hàm băm co dạng :

HF(k) = A / ( B*k + C ) mod m

Page 65: Dhhh ctdlgt bai giang cau truc du lieu

60

Trong đo : k là khoá, m là kich thƣơc bảng, A, B, C là các hăng số.

2.2.4.Hàm băm sử dụng phương pháp cộng xâu :

Để băm một xâu co chiều dài thay đổi, mỗi ký tự đƣợc thêm vào xâu sẽ đƣợc chia lấy

dƣ cho 256 cho đên tận ký tự cuối cùng. Giá tri băm, năm trong khoảng 0..255, đƣợc tinh nhƣ

sau :

Public Function Hash(ByVal S As String) As Long

Dim h As Byte

Dim i As Long

h = 0

For i = 1 to Len(S)

h = h + Asc(Mid(S, i, 1))

Next i

Hash = h

End Function

2.2.5.Hàm băm sử dụng phương pháp XOR xâu :

Trong các xâu thƣờng xuất hiện một chuỗi ký tự tƣơng tự nhau hay đảo ngữ. Do đo

việc thực hiện phép XOR các Byte trong xâu sẽ giúp khắc phuc hiện tƣợng này và giúp đạt

đƣợc các giá tri băm năm trong khoảng 0..255. Kêt quả cua mỗi phép XOR tạo ra một thành

phần ngẫu nhiên.

Private Rand8(0 To 255) As Byte

Public Function Hash(ByVal S As String) As Long

Dim h As Byte

Dim i As Long

h = 0

For i = 1 To Len(S)

h = Rand8(h Xor Asc(Mid(S, i, 1)))

Next i

Hash = h

End Function

2.2.6.Phép băm phổ quát (Universal Hashing):

Nhƣ chúng ta thấy co nhiều loại hàm băm khác nhau. Xong chúng ta cần phải chọn

đƣợc một hàm băm thich hợp để hạn chê hiện tƣợng xung đột giữa các khoá. Giải pháp đƣa ra

là sư dung hàm băm phổ quát.

Băm phổ quát nghia là chúng ta chọn ngẫu nhiên một hàm băm h trong một tập các

hàm băm H khi thuật toán bắt đầu. Hàm băm đƣợc chọn phải đảm bảo :

Page 66: Dhhh ctdlgt bai giang cau truc du lieu

61

Co tinh chất ngẫu nhiên.

Đảm bảo các khoá it xảy ra xung đột.

Gọi H là tập hữu hạn các hàm băm ánh xạ một tập các khoá U thành các giá tri năm

trong khoảng {0, 1, …, m - 1}. H gọi là phổ quát nêu :

Mỗi căp khoá riêng biệt x, y U số hàm băm h H cho kêt quả h(x) = h(y) là |H| /

m.

Noi cách khác vơi mỗi hàm băm ngẫu nhiên từ H khả năng xung đột giữa x và y ( xy

) chính xác là 1/m( m là kich thƣơc bảng băm cho trƣơc ).

Tập H sẽ đƣợc xây dựng nhƣ sau :

Chọn kich thƣơc bảng m là một số nguyên tố.

Phân tích khoá x thành r + 1 byte để x co dạng x = {x1, x2, ..., xr}.

Giá tri lơn nhất cua chuỗi sau khi phân tich < m.

Gọi = {1, 2,…, r} biểu thi cho một chuỗi r + 1 phần tư đƣợc chọn trong khoảng

{0, 1,…, m - 1}.

Hàm băm h H tƣơng ứng đƣợc đinh nghia nhƣ sau :

h(x) = xa i

r

ii

0

mod m

Theo đinh nghia ở trên H có mr+1

phần tư.

4.3.Xung đột và cách xư lý xung đột

4.3.1. Định nghĩa :

Xung đột trong phép băm đƣợc hiểu là trạng thái khi hai khoá khác nhau đƣợc băm

thành cùng một giá tri đia chỉ. Tổng quát ta co:

k1 k2 thì ta nói k1 và k2 là hai khoá xung đột khi: HF(k1) = HF(k2)

4.3.2.Hệ số tải (Load Factor - ) :

Giả sư co bảng băm co kich thƣơc m vơi n muc dữ liệu. Khi đo tỷ số = n/m đƣợc

gọi là hệ số tải. Hệ số tải cho biêt trạng thái lấp đầy cua bảng. Vi du một bảng băm co hệ số

tải là 0.25 thi co nghia là bảng băm này đã sư dung 25% kich thƣơc bảng để lƣu dữ liệu.

Hệ số tải quyêt đinh xác suất xảy ra tƣơng tranh cua các khoá. Do đo cần phải chọn

một hệ số tải thich hợp để giảm thiểu xung đột. Giá tri cua hệ số tải thƣờng đƣợc sư dung là

nho hơn hoăc băng 30%.

4.3.3.Một số phƣơng pháp xư lý xung đột :

Co hai cách tiêp cận chu yêu để giải quyêt xung đột : sư dung bảng băm đia chỉ mở và

cấu trúc lại bảng băm.

Page 67: Dhhh ctdlgt bai giang cau truc du lieu

62

Để giải quyêt xung đột thông qua bảng băm đia chỉ mở ngƣời ta co các phƣơng pháp :

do tuyên tinh, do căn bậc hai, băm kép và băm lại.

Đối vơi cách tiêp cận thay đổi cấu trúc bảng ngƣời ta co các phƣơng pháp : Moc nối

trực tiêp, sư dung các Bucket.

Ngoài ra đối vơi trƣờng hợp dữ liệu co kich thƣơc lơn ngƣời ta co thể sư dung các

phƣơng pháp băm khác nhƣ : băm lại, băm mở rộng.

Dƣơi đây là chi tiêt về các phƣơng pháp này.

3.3.1.Băm theo địa chỉ mở (Open-adressing hashing) :

Băm theo đia chỉ mở giải quyêt xung đột băng cách lƣu tất cả các muc dữ liệu trong

chinh bảng băm. Phƣơng pháp này khá thich hợp khi chúng ta co thể ƣơc lƣợng đƣợc số muc

vào. Khi đo chúng ta co thể co đu các vi tri để lƣu tất cả các muc trong bảng (kể cả các vi tri

sư dung để ngăn cách) và vẫn giảm đƣợc không gian lƣu trữ nhiều hơn so vơi phƣơng pháp

moc nối.

Ngƣời ta đinh nghia một hàm băm chung cho phƣơng pháp băm theo đia chỉ mở. Nhƣ

vậy hàm băm lúc này gôm co 2 tham số : khoá k và số lần do tim p , trong đo 0 p m-1.

Tham số p sư dung để giơi hạn số lần do và cho phép chúng ta biêt khi nào thuật toán dừng.

Sau đây chúng ta xét một số phƣơng pháp băm theo đia chỉ mở cu thể.

3.3.1.1.Phương pháp dò tuyến tính :

Do tuyên tinh là mô hinh đia chỉ mở đơn giản nhất. Phƣơng pháp này gôm các thao

tác: tim kiêm, chen thêm một muc dữ liệu.

Hàm băm sư dung cho phƣơng pháp này co dạng :

h(k,p) = ( h(k) + p )mod m

Thao tác tim kiêm :

Khi xung đột xảy ra phƣơng pháp này đơn giản là do một vi tri trống trong bảng. Để tim

một muc dữ liệu trƣơc hêt ta phải thực hiện băm khoá cua muc dữ liệu này để tim ra chỉ số

cua no trong bảng. Nêu muc dữ liệu không co tại vi tri cua chỉ số mà chúng ta thu đƣợc thi

chúng ta bắt đầu thực hiện do theo tuyên tại vi tri này. Co 3 khả năng co thể xảy ra :

1. Vi tri tiêp theo co chứa muc dữ liệu và tim kiêm kêt thúc thành công.

2. Vi tri tiêp theo trống, dữ liệu không tim thấy, quá trinh tim kiêm kêt thúc không thành

công.

3. Vi tri tiêp theo bi chiêm giữ nhƣng các khoá lại không phù hợp vi thê vi tri tiêp theo

đo sẽ đƣợc do.

Số các vi tri cần do trong phƣơng pháp này phu thuộc vào 2 yêu tố :

+ Hàm băm đƣợc chọn nhƣ thê nào.

+ Bảng đã sư dung bao nhiêu không gian để lƣu dữ liệu.

Page 68: Dhhh ctdlgt bai giang cau truc du lieu

63

Nêu chúng ta chọn đƣợc một hàm băm thich hợp và bảng đã sư dung khoảng 30% - 50%

thi sẽ đảm bảo đƣợc số vi tri cần do là nho nhất co thể.

Chúng ta co một vi du về cách cài đăt thao tác tim kiêm đo là :

int jsw_find ( void *key, int len )

{

unsigned h = hash ( key, len ) % N;

void *save = table[h];

while ( table[h] != NULL ) {

if ( compare ( key, table[h] ) == 0 )

return 1;

h = ( h + 1 ) % N;

if ( compare ( table[h], save ) == 0 )

return 0;

}

return 0;

}

Thao tác chèn :

Để chen thêm một muc mơi chúng ta cần thực hiện :

Tinh các giá tri băm cho các khoá thông qua hàm băm đã chọn.

Nêu vi tri co giá tri băm đã co dữ liệu thi thao tác do đƣợc thực hiện từ vi tri này.

Thao tác do đƣợc thực hiện cho đên khi tim đƣợc một vi tri trống. Thao tác này sẽ do tiêp ở vi

tri đầu nêu no đạt đên vi tri cuối cua tuyên.

Khi tim đƣợc một ô trống thi muc dữ liệu sẽ đƣợc chen vào.

Thao tác này co thể cài đăt nhƣ sau :

void jsw_insert ( void *key, int len )

{

unsigned h = hash ( key, len ) % N;

while ( table[h] != NULL )

h = ( h + 1 ) % N;

table[h] = key;

}

Thao tác xoá :

Thao tác nay không đơn giản nhƣ hai thao tác trên. Việc xoá trực tiêp một muc khoi

bảng là khôn thể vi các phép do tiêp theo đo co thể nhận ra các khoá đã bi bo đi và nêu một

bucket rỗng đƣợc tạo ra trong khi một buket khác vẫn đầy thi quá trinh tim kiêm co thể không

Page 69: Dhhh ctdlgt bai giang cau truc du lieu

64

chinh xác. Nhƣ vậy thao tác xoá co thể phá vỡ cấu trúc dữ liệu cua bảng. Giải pháp đƣa ra là

khi xoá một khoá trên một đoạn cua bucket thi ta lại chen khoá vào đoạn tƣơng tự cua no.

Nhƣng cách này dƣờng nhƣ khá phức tạp.

Sau đây là một vi du về thao tác xoá :

void jsw_remove ( void *key, int len )

{

unsigned h = hash ( key, len ) % N;

while ( table[h] != NULL )

h = ( h + 1 ) % N;

table[h] = DELETED;

}

Đánh giá :

Trong phƣơng pháp này các khoá co khuynh hƣơng bi đƣa vào các đoạn gọi là Cluster

( bo cum ). Điều này co nghia là nhiều phần trong bảng co thể đầy lên nhanh chong trong khi

các phần khác vẫn con trống. Do phƣơng pháp này cần sư dung một lƣợng lơn các Bucket

rỗng năm xen kẽ vơi các Bucket đã sư dung nên việc bo cum sẽ làm cho nhiều Bucket bi

duyệt qua trƣơc khi tim đƣợc một Bucket rỗng. Vi vậy thao tác tim kiêm sẽ bi chậm đi và kéo

theo các thao tác chen và xoá cũng chậm. Một bảng băm co hệ số tải càng lơn thi khả năng bo

cum xảy ra càng lơn. Do đo một hàm băm tốt và kich thƣơc bảng là một số nguyên tố sẽ cải

thiện đƣợc vấn đề này.

3.3.1.2.Phương pháp dò căn bậc 2 :

Để khắc phuc vấn đề bo cum chinh ngƣời ta đƣa ra phƣơng pháp do căn bậc hai.

Phƣơng pháp này sư dung hàm băm co dạng :

h(k,p) = ( h(k) + c1p + c2p2 ) mod m

Các giá tri c1, c2, m xác đinh liệu toàn bộ bảng co đƣợc sư dung hay không.

Thao tác tim kiêm :

Theo hàm băm nhƣ trên để tim kiêm một muc trong bảng ngƣời ta sẽ bắt đầu từ vi tri

đầu tiên trong bảng đƣợc xác đinh bởi hàm băm, gọi là vi tri i và tiêp tuc do tơi các vi tri i +

12, i + 2

2 , …, i + ( m - 1 )

2 ( tất cả đều mod m ). Cứ nhƣ vậy quá trinh tim kiêm đƣợc thực

hiện cho đên khi tim thấy muc dữ liệu trong bảng ( kêt thúc thành công ) hoăc găp một vi tri

trống ( kêt thúc không thành công ).

Thuật toán sư dung cho phƣơng pháp này co phần phức tạp hơn phƣơng pháp do tuyên

tinh. Dƣơi đây là vi du cu thể :

int jsw_search ( void *key, int len )

{

Page 70: Dhhh ctdlgt bai giang cau truc du lieu

65

unsigned h = hash ( key, len ) % N;

unsigned step;

for ( step = 1; table[h] != NULL; ++step ) {

if ( compare ( key, table[h] ) == 0 )

return 1;

h = ( h + step * step ) % N;

}

return 0;

}

Thao tác chèn :

void jsw_insert ( void *key, int len )

{

unsigned h = hash ( key, len ) % N;

unsigned step;

for ( step = 1; table[h] != NULL; ++step )

h = ( h + step * step ) % N;

table[h] = key;

}

Thao tác xoá :

void jsw_remove ( void *key, int len )

{

unsigned h = hash ( key, len ) % N;

unsigned step;

for ( step = 1; table[h] != NULL; ++step )

h = ( h + step * step ) % N;

table[h] = DELETED;

}

Đánh giá :

Phƣơng pháp do theo căn bậc hai giảm đáng kể hiện tƣợng bo cum chinh. Tuy nhiên vi

chuỗi do tim luôn bắt đầu ở cùng bucket ( một ô cua bảng) nên chúng ta lại găp phải hiện

tƣợng bo cum thứ cấp ( Secondary Clustering ). Đây không phải là một hiện tƣợng đáng quan

tâm nhƣ bo cum chinh. Nhƣng do phƣơng pháp do căn bậc hai chỉ hoạt động khi hệ số tải <

0.5 và kich thƣơc cua bảng là một số nguyên tố nên hiện tƣợng này lại làm chậm đáng kể tốc

độ tim kiêm.

Page 71: Dhhh ctdlgt bai giang cau truc du lieu

66

Noi chung phƣơng pháp này nhanh và tránh đƣợc hiện tƣợng bo cum chinh nhƣng lại it

đƣợc sư dung trong thực tê vi sự giơi hạn về thời gian. Phƣơng pháp này chỉ đảm bảo hoạt

động hiệu quả khi kich thƣơc bảng là số nguyên tố và dung lƣợng bảng đã sư dung chƣa quá

một nưa.

3.3.1.3.Phương pháp băm kép :

Phƣơng pháp này là một giải pháp đáng lƣu ý thay cho phƣơng pháp do theo căn bậc

hai. No co thể khắc phuc đƣợc hiện tƣợng bo cum chinh mà không chiu sự giơi hạn nào.

Phƣơng pháp này sư dung hai hàm băm độc lập nhau. Hàm băm thứ nhất đƣợc sư

dung nhƣ binh thƣờng ( theo một trong hai phƣơng pháp đã kể trên ). Hàm băm thứ hai đƣợc

sư dung để tạo ra kich thƣơc bƣơc do. Do bản thân khoá co thể xác đinh đƣợc bƣơc do nên

tránh đƣợc hiện tƣợng bo cum chinh. Phƣơng pháp này co thuật toán khá đơn giản nhƣng co 2

điều cần chú ý để đảm bảo phƣơng pháp này hoạt động chinh xác :

Hàm băm thứ hai không đƣợc trả về giá tri 0 để tránh hiện tƣợng lăp vô hạn.

Giống nhƣ do theo tuyên, kich thƣơc bảng phải là một số nguyên tố hoăc là một giá tri

luỹ thừa cua 2 vơi kêt quả hàm băm thứ hai trả về là một số lẻ.

Nhƣ vậy cách cài đăt cho phƣơng pháp này cũng tƣơng tự nhƣ các phƣơng pháp băm

theo đia chỉ mở khác. Một vi du minh hoạ cu thể là :

Thao tác tim kiêm :

int jsw_search ( void *key, int len )

{

unsigned h = hash ( key, len ) % N;

unsigned h2 = hash2 ( key ) % ( N - 1 ) + 1;

while ( table[h] != NULL ) {

if ( compare ( key, table[h] ) == 0 )

return 1;

h = ( h + h2 ) % N;

}

return 0;

}

Thao tác chèn :

void jsw_insert ( void *key, int len )

{

unsigned h = hash ( key, len ) % N;

unsigned h2 = hash2 ( key ) % ( N - 1 ) + 1;

while ( table[h] != NULL )

Page 72: Dhhh ctdlgt bai giang cau truc du lieu

67

h = ( h + h2 ) % N;

table[h] = key;

}

Thao tác xoá :

void jsw_remove ( void *key, int len )

{

unsigned h = hash ( key, len ) % N;

unsigned h2 = hash2 ( key ) % ( N - 1 ) + 1;

for ( table[h] != NULL )

h = ( h + h2) % N;

table[h] = DELETED;

}

3.3.2.Cấu trúc lại bảng băm :

Phƣơng pháp này giải quyêt xung đột băng cách thay đổi cấu trúc cua bảng băm. Theo

đo một vi tri co thể chứa đƣợc nhiều hơn một muc dữ liệu. Sau đây là một số phƣơng pháp

cấu trúc lại bảng băm.

3.3.2.1.Sử dụng Bucket :

Trong phƣơng pháp này bản thân mỗi phần tư trong bảng băm là một mảng đƣợc gọi

là ngăn chứa( Bucket ). Các khoá đƣợc băm về cùng một vi tri sẽ đƣợc đăt trong các cột tƣơng

ứng vơi các vi tri này. Vi du :

0 1 2 3 4 5 6 7 8 9 10

K

1 k3 k5 k5 k7 k8

K

2 k4 k9

Bảng 2.2 : Mô hình một Bucket

Giả sư co hai khoá k1, k2 là hai khoá xung đột. Kêt quả sau khi băm hai khoá này là 2.

Vậy ta xêp k1 và k2 vào cột 1 tƣơng ứng vơi vi tri thứ 2. Tƣơng tự nhƣ vậy nêu kêt quả băm

các khoá khác cho vi tri 2 thi các khoá sẽ lần lƣợt đƣợc xêp vào cột này.

Nhƣ vậy co thể thấy phƣơng pháp này cần nhiều không gian lƣu trữ hơn thực tê dữ

liệu yêu cầu.

Trong phƣơng pháp này, để tim một khoá nhất đinh ngƣời ta phải kiểm tra tất cả các

cột liên quan tơi vi tri cua khoá này ( it nhất là cho đên khi chúng ta tim đƣợc một vi tri trống

Page 73: Dhhh ctdlgt bai giang cau truc du lieu

68

). Nêu các khoá co thứ tự, chúng ta nên lƣu các khoá theo trật tự tăng dần. Nhƣ vậy chúng ta

co thể loại bơt các cột cần kiểm tra. Độ phức tạp trung binh cua thao tác tim kiêm phu thuộc

vào số vi tri trong Bucket đã đƣợc điền. Do đo khi nào bảng chƣa đƣợc điền đầy khi đo khả

năng xung đột vẫn co thể xảy ra.

3.3.2.2.Phương pháp móc nối trực tiếp :

Theo phƣơng pháp này bảng băm là một mảng các danh sách liên kêt. Phƣơng pháp

này đăt các khoá bi xung đột vào cùng một danh sách liên kêt tại vi tri là kêt quả sau khi băm.

Một bảng băm theo phƣơng pháp này co thể mô tả nhƣ sau :

Hình 2.3 : Bảng băm theo

phương pháp móc nối

Vấn đề đăt ra ở đây là danh sách liên kêt đƣợc cấu trúc nhƣ thê nào. Khi việc tim kiêm

cua bảng băm phu thuộc vào việc tim kiêm trên danh sách liên kêt thi một danh sách đƣợc sắp

xêp là một cấu trúc đáng lƣu ý. Cấu trúc này co hiệu quả it hơn vơi thao tác chen và thao tác

tim kiêm thành công hoăc không thành công ( tinh trung binh ). Khi đo một nưa số danh sách

liên kêt sẽ đƣợc tim kiêm. Nêu danh sách không đƣợc sắp xêp thi một lần tim kiêm không

thành công co thể duyệt qua toàn bộ các danh sách liên kêt.

Tuy nhiên điều noi trên chỉ đúng về măt lý thuyêt. Trên thực tê con co nhiều thao tác

hơn cần thiêt. Vi vơi một hàm băm tốt các chuỗi ( danh sách liên kêt ) phải đảm bảo đu ngắn

để thấy đƣợc sự khác nhau giữa danh sách đƣợc sắp xêp và danh sách chƣa đƣợc sắp xêp.

Chúng ta giả sư răng hầu hêt thời gian thực hiện phƣơng pháp này sư dung để thực

hiện so sánh giữa khoá tim kiêm vơi các khoá khác trong danh sách. Chúng ta cũng giả sư

thời gian cần thiêt để băm tơi danh sách thich hợp và xác đinh khi nào đên vi tri cuối cua danh

sách tƣơng đƣơng vơi một so sánh.

Nhƣ vậy tất cả các thao tác cần 1 + Số so sánh (lần).

Giả sư bảng băm co kich thƣơc m và có n muc đƣợc chen vào bảng. Khi đo mỗi danh

sách trung binh sẽ chứa n/m muc. Ở đây ta co thể thấy số muc trung binh trên một danh sách

chinh là hệ số tải = n/m.

Sau đây là một số thao tác chinh cua phƣơng pháp này.

Thao tác tim kiêm :

Khi tim một khoá trong bảng, khoá sẽ đƣợc băm và trả về kêt quả là chỉ số nơi co danh

sách liên kêt sẽ đƣợc tim kiêm. Nhƣ vậy hàm băm sẽ quyêt đinh phải tim ở danh sách nào.

Page 74: Dhhh ctdlgt bai giang cau truc du lieu

69

Khi tim kiêm thành công thuật toán sẽ thực hiện băm và tim tơi danh sách thich hợp.

Tính trung binh thi một nưa số muc sẽ đƣợc kiểm tra trƣơc khi một muc chinh xác đƣợc tim

thấy. Do đo thời gian tim kiêm là : 1 + /2.

Trong trƣờng hợp tim kiêm không thành công, nêu danh sách không co trật tự thi tất cả

các muc sẽ đƣợc kiểm tra và thời gian tim kiêm là : 1 + .

Sau đây là cài đăt cua thao tác này :

struct jsw_node {

void *key;

struct jsw_node *next;

};

struct jsw_node *table[N];

int jsw_find ( void *key, int len )

{

unsigned h = hash ( key, len ) % N;

if ( table[h] != NULL ) {

struct jsw_node *it = table[h];

while ( it != NULL ) {

if ( compare ( key, it->key ) == 0 )

return 1;

it = it->next;

}

}

return 0;

}

Thao tác chèn :

Chen vào một danh sách liên kêt đơn giản hơn tim kiêm. Khi cần chen thêm một muc dữ

liệu vào bảng thi khoá sẽ đƣợc băm và cho kêt quả là vi tri cua danh sách cần chen. Một nút

sẽ đƣợc thêm vào danh sách ( tạo phần header cua nút nêu cần thiêt ) sau đo khoá và giá tri sẽ

đƣợc đẩy vào phần trƣơc cua nút này.

Việc chen một nút vào cuối danh sách liên kêt khá đơn giản. Nêu một nút đƣợc chen vào

phia đầu cua danh sách liên kêt thi danh sách liên kêt sẽ đƣợc sư dung nhƣ một ngăn xêp vơi

tinh chất truy cập tuần tự. Trên thực tê chen một nút vào trƣơc danh sách là cách đơn giản và

đạt hiệu quả tốt nhất.Vi du về cài thao tác này là :

int jsw_insert ( void *key, int len )

Page 75: Dhhh ctdlgt bai giang cau truc du lieu

70

{

unsigned h = hash ( key, len ) % N;

struct jsw_node *it = malloc ( sizeof *it );

if ( it == NULL )

return 0;

it->key = key;

it->next = table[h];

table[h] = it;

return 1;

}

Thao tác xoá :

Khi xoá một muc dữ liệu thi cần thực hiện thao tác tim khoá cũ.

int jsw_remove ( void *key, int len )

{

unsigned h = hash ( key, len ) % N;

struct jsw_node *save;

if ( table[h] == NULL )

return 0;

if ( compare ( key, table[h]->key ) == 0 ) {

save = table[h]->next;

table[h]->next = table[h]->next;

free ( save );

}

else {

struct jsw_node *it = table[h];

while ( it->next != NULL ) {

if ( compare ( key, it->next->key ) == 0 )

break;

it = it->next;

}

if ( it->next == NULL )

return 0;

save = it->next;

it->next = it->next->next;

Page 76: Dhhh ctdlgt bai giang cau truc du lieu

71

free ( save );

}

return 1;

}

Đánh giá :

Phƣơng pháp moc nối trực tiêp là phƣơng pháp xư lý xung đột phổ biên nhất vi no khá

linh hoạt. Nội dung cua phƣơng pháp này đơn giản và no co một số ƣu điểm sau :

Giơi hạn kich thƣơc bảng không quá cứng nhắc.

Việc thực thi tránh tối đa hiện tƣợng xung đột.

Bảng dễ dàng xư lý các khoá trùng lăp.

Thao tác xoá đơn giản và co thể thực hiện thƣờng xuyên.

Tuy nhiên việc cài đăt phƣơng pháp này phức tạp hơn các phƣơng pháp khác. No cũng

co một số nhƣợc điểm :

Phải xây dựng lại bảng khi việc thay đổi kich thƣơc găp kho khăn.

Sư dung nhiều không gian nhơ hơn cho các liên kêt.

Tốc độ xư lý co thể chậm hơn do sự tham chiêu qua lại cua các liên kêt.

3.3.3.Các phương pháp băm khác :

Băm lại :

Chúng ta thấy răng tất cả các phƣơng pháp trên chỉ hiệu quả khi hệ số tải < 2/3, tức là

bảng không quá đầy. Khi bảng băm bi lấp đầy các phƣơng pháp này không con hiệu quả nữa.

Giải pháp đƣa ra là tạo ra một bảng mơi co kich thƣơc lơn gấp đôi bảng ban đầu. Sau đo tất cả

các khoá sẽ đƣợc băm lại.

Độ phức tạp cua phƣơng pháp này là O(n) do đo phƣơng pháp này chỉ sư dung khi

kich thƣơc dữ liệu lơn. Cá biệt trong phƣơng pháp do theo căn bậc hai chúng ta phải băm lại

khi ½ bảng đƣợc sư dung.

Băm mở rộng :

Đây là một phƣơng pháp hỗ trợ hữu hiệu cho việc sư dung co hiệu quả không gian lƣu

trữ.

4.3.4. Đánh giá :

Khi xây dựng bảng băm nêu chúng ta biêt trƣơc số muc dữ liệu sẽ đƣợc chen vào bảng

thi mô hinh băm theo đia chỉ mở vơi hệ số tải < ½ sẽ cho kêt quả tốt nhất.

Măt khác nêu chúng ta không biêt trƣơc co bao nhiêu muc dữ liệu đƣợc chen vào bảng

thi phƣơng pháp moc nối trực tiêp co hiệu quả hơn.

Page 77: Dhhh ctdlgt bai giang cau truc du lieu

72

Khi hệ số tải tăng sư dung phƣơng pháp băm theo đia chỉ mở sẽ găp những bất lợi chu

yêu trong thực thi. Nhƣng đối vơi phƣơng pháp moc nối hiệu quả cua no chỉ bi giảm tuyên

tinh khi hệ số tải tăng. Do đo nêu không chắc chắn về hệ số tải thi phƣơng pháp đƣợc lựa

chọn là phƣơng pháp moc nối.

4.4.4.Kết luận :

Bảng băm là một cấu trúc dữ liệu thich hợp cho việc lƣu trữ và tim kiêm dữ liệu

không co trật tự. Tuy phải xư lý xung đột nhƣng hầu hêt các thao tác cua bảng băm đều dễ

thực hiện và các thao tác này co hiệu quả gần nhƣ không đổi trong hầu hêt các trƣờng hợp.

Hàm băm ánh xạ các khoá dữ liệu vào trong các ô co sẵn cua bảng. Một bảng băm co

hiệu quả khi chúng ta chọn đƣợc một hàm băm tốt. Vấn đề cốt yêu là :

Sư dung bảng băm co kich thƣơc là một số nguyên tố.

Sư dung hàm băm co thể đảm bảo các khoá đƣợc phân bố đều trong bảng.Tuy nhiên

ngay cả trong trƣờng hợp chúng ta chọn đƣợc hàm băm tốt nhất thi hiện tƣợng xung đột vẫn

co thể xảy ra.

Băm theo đia chỉ mở là một trong các phƣơng pháp giải quyêt xung đột. Trong đo :

Phƣơng pháp do tuyên tinh : sư dung nêu các ô đƣợc duyệt toàn bộ và tim vi tri thành

công. Nhƣng phƣơng pháp này co thể gây ra hiện tƣợng bo cum.

Phƣơng pháp do căn bậc 2 : Phƣơng pháp này tốt hơn do co thể giảm xác suất xảy ra

hiện tƣợng bo cum.

Hệ số tải nên < ½ để tránh làm giảm hiệu quả thực hiện cua phƣơng pháp.

Moc nối cũng là một biện pháp xư lý xung đột hiệu quả. Xong phƣơng pháp này đoi hoi

không gian lƣu trữ lơn.

4.5. Bài tập áp dụng

1. Tạo các bảng băm chứa các số nguyên theo từng loại giải quyêt xung đột

2. Tạo các bảng băm chứa họ tên sinh viên theo từng loại giải quyêt xung đột

3. Tạo từ điển đơn giản

Một số đề thi tham khảo

Đề 1

Câu 1: Cho biểu thức (a+b-c)*(d-e*f)-(x+y)/z

Hãy vẽ cây nhi phân tƣơng ứng và suy ra dạng tiền tố và dạng hậu tố cua biểu thức đo.

Câu 2:

a) Khai bao câu truc dƣ liêu danh sach liên kêt đơn chƣa cac thông tin vê nhân sƣ gôm co

các trƣờng thông tin:

Họ và tên

Thâm niên công tac (tinh theo năm)

Hê sô lƣơng

Lƣơng cơ ban.

b) Viêt ham thêm môt ban ghi chƣa thông tin nhân sƣ cua môt ngƣơi vao cuôi danh sach

đa khai bao ơ phân a.

Page 78: Dhhh ctdlgt bai giang cau truc du lieu

73

c) Viêt ham tinh xem co bao nhiêu nhân viên phai đong thuê thu nhâp biêt r ăng lƣơng

cua một ngƣời trong một tháng đƣợc tinh băng công thức : hê sô lƣơng * lƣơng cơ ban

+ thâm niên * 200 (ngàn đông) và ngƣời đong thuê thu nhập là ngƣời co thu nhập 1

năm lơn hơn 1000000000.

Đề 2

Câu 1:

a) Chen các khoa sau lân lƣơt vao môt cây tim kiêm nhi phân rông : 20, 5, 1 17, 30, 24, 7,

18, 23, 9, 32, 25. Xoa khoi cây các nút chứa các khoa sau : 24, 10, 7.

b) Khai bao câu truc cây nhi phân chƣa cac thông tin nhƣ ơ phân a cua câu 2 và viêt hàm

duyêt cây theo thƣ tƣ trƣơc.

Chuyên biêu thƣc trung tô sau thanh cây biêu thƣc : x*y + z*(t-u). Trong đo z , y, z, t, u la cac

biên (toán hạng).

Câu 2:

Khai bao danh sach liên kêt đơn chƣa cac trƣơng thông tin vê môt khach hang nhƣ

sau:

Mã khách hàng,

Họ và tên khách hàng,

Sô điên thoai cua khach hang (13 chƣ sô).

a) Viêt ham co input la danh sach đa đƣơc khai bao ơ phân a va môt xâu sdt la môt phân

sô điên thoai trong trƣơng điên thoai cua môt khac h hang nao đo . Viêt ham tra vê nut

đâu tiên trong danh sach liên kêt co phân đâu trƣơng sô điên thoai trung vơi xâu sdt.

b) Viêt ham săp xêp danh sach khach hang trên theo chiêu tăng dân cua tên khach hang .

Đề 3

Câu 1:

a) Hãy chen các khoa sau vào một cây tim kiêm nhi phân rỗng mà các nút chứa các số

nguyên: 17, 4, 19, 27, 7, 18, 8, 30, 15. Xoa khoi cây lần lƣợt các nút chứa các khoa :

19, 8, 17.

b) Chuyên biêu thƣc trung tô sau thanh biêu thƣc tiên tô: 12 – 7*(3+9 – 2*5).

c) Hãy khai báo cấu trúc cây tim kiêm nhi phân chứa các trƣờng thông tin về sinh viên

nhƣ sau:

Mã sinh viên,

Họ và tên,

Điêm trung binh.

trong đo ma sinh viên la trƣơng khoa.

Viêt ham chen thêm môt sinh viên vào cây vừa khai báo.

Câu 2:

a) Khai bao danh sach liên kêt đơn chƣa cac sô thƣc.

b) Viêt ham đao ngƣơc danh sach liên kêt đơn chƣa cac sô thƣc đƣơc khai bao ơ phân a .

c) Viêt ham săp xêp danh sach sô thƣc trên theo chiêu tăng dân.

Đề 4:

Câu 1:

a) Hãy chen các khoa sau vào một cây tim kiêm nhi phân rỗng mà các nút chứa các số

nguyên: 20, 17, 29, 15, 80, 6, 19, 51, 32. Xoa khoi cây lần lƣợt các nút chứa các khoa :

29, 20, 17.

b) Chuyên biêu thƣc trung tô sau thanh biêu thƣc hâu tô:

5 * ( 2 + 3 ) – 2 * ( 15 - 10 ) + 21.

c) Hãy khai báo cấu trúc cây tim kiêm nhi phân chứa các trƣờng thông tin về nhân sự

nhƣ sau:

Họ và tên

Mã nhân viên

Page 79: Dhhh ctdlgt bai giang cau truc du lieu

74

Lƣơng

trong đo ma nhân viên la trƣơng khoa.

Viêt ham tinh trung binh cộng lƣơng cua các nhân viên trong một cây vừa khai báo .

Câu 2:

a) Khai bao danh sach liên kêt đơn chƣa cac trƣơng thông tin vê hang hoa nhƣ sau :

Tên măt hang

Giá măt hàng

Sô lƣơng con trong kho

b) Hãy cài đăt hàm sắ p xêp danh sach trên theo chiêu tăng dân cua gia măt hang , nêu

cùng giá thi theo tên.

c) Viêt ham nhân input la môt danh sach đƣơc khai bao nhƣ trên va hai sô nguyên dƣơng

x < y, in ra tên cac măt hang co sô lƣơng con trong kho lơn hơn x va nho hơn y.

Đề 5:

Câu 1:

a) Khai bao danh sach liên kêt đơn chƣa cac trƣơng thông tin vê phân mêm nhƣ sau:

Tên phân mêm

Dung lƣơng (tinh băng Mb)

Đia chi download

b) Hãy cài đăt hàm thêm một phần tư vào cuối danh sách trên.

c) Viêt ham tinh dung lƣơng trung binh cua cac phân mêm trong danh sach liên kêt trên .

Câu 2:

a) Hãy chen các khoa sau vào một cây tim kiêm nhi phân rỗng mà các nút chứa các số

nguyên: 19, 7, 1, 9, 25, 21, 70, 30, 8. Xoa khoi cây nút gốc. Đƣa ra kêt qua duyêt cây

theo thƣ tƣ trƣơc va thƣ tƣ sau.

b) Chuyên biêu thƣc hâu tô sau thanh biêu thƣc tiên tô va tinh gia tri cua no:

11 2 3 * + 4 5 * 8 + 7 / +

c) Hãy khai báo cấu trúc cây tim kiêm nhi phân chƣa cac trƣơng thông tin vê nhân sƣ

nhƣ sau:

Họ và tên

Mã nhân viên

Lƣơng

trong đo ma nhân viên la trƣơng khoa.

Viêt ham tim nut chƣa thông tin vê nhân viên co lƣơng thâp nhât.

Page 80: Dhhh ctdlgt bai giang cau truc du lieu

75

TÀI LIỆU THAM KHẢO.

1. Đinh Mạnh Tƣờng, Cấu trúc dữ liệu và thuật toán, NXB Đại học Quốc gia Hà

nội, 2002

2. Đỗ Xuân Lôi, Cấu trúc dữ liệu và giải thuật, NXB Khoa học và Kỹ thuật, 1997

3. Nguyễn Quốc Lƣợng, Hoàng Đức Hải ,Cấu trúc dữ liệu + giải thuật = chƣơng

trình, NXB Giáo duc, 1996

4. Hoare, C.A.R, Note on date Structuring in structured Programming Dahl,

Dijkstra, and Hoare, pp 83-174.

5. Robert Sedgewick , Cẩm năng thuật toán, NXB KH Kỹ thuật, 2000