Cấu trúc dữ liệu của một phần tử trong danh sách liên kết đơn... PHẦN BÀI HỌC CHƯƠNG 1:TỔNG QUAN VỀ CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT GIỚI THIỆUChương I nhằm giới thiệu cho sinh viên tổng t
Trang 1BÀI GIẢNG CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT
LỜI NÓI ĐẦU
Giáo trình Cấu trúc dữ liệu và giải thuật được biên soạn dựa theo chươngtrình khung của Tổng cục dạy nghề chuyên ngành Kỹ thuật lắp ráp sửa chửamáy tính Giáo trình trình bày những vấn đề cốt lõi nhất của môn cấu trúc dữliệu và giải thuật Các bài học được trình bày ngắn gọn, có nhiểu ví dụ minhhọa Cuối mỗi bài đều có bài tập để sinh viên luyện tập
Giáo trình này áp dụng cho sinh viên Trường Cao Đẳng Nghề KiênGiang, nghề Kỹ thuật sửa chữa lắp ráp máy tính Giáo trình là phần tiếp nối vớimôn ngôn ngữ lập trình
Giáo trình bổ sung những kiến thức về cấu trúc dữ liệu và giải thuật.Giúp cho sinh viên xây dựng được cấu trúc dữ liệu động bằng danh sách liênkết, ngăn xếp, hàng đợi,… Các phương pháp sắp xếp dữ liệu cơ bản, các kỹthuật tìm kiếm
Giáo trình gồm có 5 chương:
Chương 1: Tổng quan về cấu trúc dữ liệu và giải thuật
Chương 2: Đệ qui và giải thuật đệ qui
Tất cả những ý kiến đóng góp điều được trân trọng
Rạch giá, ngày 04 tháng 12 năm 2011
Tham gia biên soạn
1 Chủ biên Ông Nhan Thanh Liêm
2 Bà Trương Thị Trúc Loan
3 Ông Nguyễn Phước Lộc
4 Ông Nguyễn Hữu Nhân
Trang 25 Bà Lưu Phương Thúy
MỤC LỤC
TUYÊN BỐ BẢN QUYỀN……….1
LỜI NÓI ĐẦU……….2
PHẦN GIỚI THIỆU MÔN HỌC:………7
CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT……… 7
I VỊ TRÍ, TÍNH CHẤT CỦA MÔN HỌC 7
II MỤC TIÊU CỦA MÔN HỌC 7
III NỘI DUNG CỦA MÔN HỌC 7
IV PHƯƠNG PHÁP ĐÁNH GIÁ VÀ HƯỚNG DẪN TỰ HỌC 8
1 PHƯƠNG PHÁP ĐÁNH GIÁ 8
CHƯƠNG 1:TỔNG QUAN VỀ CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT……….9
I MỐI QUAN HỆ GIỮA CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT 9
1 Tổ chức biểu diễn các đối tượng thực tế 9
2 Xây dựng các thao tác xử lý dữ liệu 9
II CÁC TIÊU CHUẨN ĐÁNH GIÁ CTDL 12
1 Phản ánh đúng thực tế 12
2 Phù hợp với các thao tác trên đó 13
3 Tiết kiệm tài nguyên hệ thống 13
III CÁC KIỂU DỮ LIỆU CƠ BẢN 13
1 Khái niệm về kiểu dữ liệu 13
2 Các kiểu dữ liệu cơ bản 13
IV CÁC KIỂU DỮ LIỆU CÓ CẤU TRÚC 15
V KIỂU CON TRỎ 16
1 Biến không động (biến tĩnh, biến nửa tĩnh): 16
2 Kiểu con trỏ 17
3 Biến động 18
Trang 3VI ĐÁNH GIÁ ĐỘ PHỨC TẠP GIẢI THUẬT 19
PHƯƠNG PHÁP ĐÁNH GIÁ VÀ HƯỚNG DẪN TỰ HỌC:………… 20
CHƯƠNG 2: ĐỆ QUI VÀ GIẢI THUẬT ĐỆ QUI……… 23
I KHÁI NIỆM ĐỆ QUI 23
1 Định nghĩa số tự nhiên 23
2 Định nghĩa xâu ký tự bằng đệ qui 23
3 Định nghĩa hàm giai thừa 23
4 Hàm đệ quy 24
II GIẢI THUẬT ĐỆ QUI VÀ CHƯƠNG TRÌNH ĐỆ QUI 25
III ƯU VÀ NHƯỢC ĐIỂM CỦA GIẢI THUẬT ĐỆ QUI 27
IV CÁC BÀI TOÁN ĐỆ QUI CƠ BẢN 27
1 Bài toán tháp Hà nội 27
2 Bài toán tính tổng các phần tử trong mảng dùng đệ quy: 30
PHƯƠNG PHÁP ĐÁNH GIÁ VÀ HƯỚNG DẪN TỰ HỌC………31
CHƯƠNG 3: DANH SÁCH……… 37
A Định nghĩa danh sách liên kết 37
B Các hình thức tổ chức của danh sách liên kết 37
I DANH SÁCH LIÊN KẾT ĐƠN 39
1 Cấu trúc dữ liệu của một phần tử trong danh sách liên kết đơn .39 2 Các thao tác cơ bản trên danh sách đơn 40
2.1 Thêm một phần tử vào danh sách 40
2.2 Tìm một phần tử trong danh sách đơn 44
2.3 Hủy một phần tử khỏi danh sách 44
2.4 Duyệt danh sách 47
II DANH SÁCH LIÊN KẾT KÉP 48
1 Chèn một phần tử vào danh sách: 49
1.5 Hủy một phần tử khỏi danh sách 53
III STACK – NGĂN XẾP 56
1 Biểu diễn Stack dùng mảng 58
2 Biểu diễn Stack dùng danh sách 58
3 Ứng dụng của Stack 59
4 Các ví dụ: 59
V QUEUE – HÀNG ĐỢI 63
Trang 41 Biểu diễn dùng mảng 65
2 Dùng danh sách liên kết 66
3 Ứng dụng của hàng đợi 67
4 Các ví dụ: 68
PHƯƠNG PHÁP ĐÁNH GIÁ VÀ HƯỚNG DẪN TỰ HỌC 72
CHƯƠNG 4 CÁC PHƯƠNG PHÁP SẮP XẾP CƠ BẢN……….81
I ĐỊNH NGHĨA BÀI TOÁN SẮP XẾP 81
II PHƯƠNG PHÁP CHỌN TRỰC TIẾP – SELECTION SORT 82
1 Giải thuật 82
2 Cài đặt 84
3 Đánh giá giải thuật 84
4 Ứng dụng phương pháp chọn trực tiếp để sắp xếp danh sách 84
III PHƯƠNG PHÁP CHÈN TRỰC TIẾP – INSERTION SORT 86
1 Giải thuật 86
2 Cài đặt 89
3 Đánh giá giải thuật 89
IV PHƯƠNG PHÁP SẮP XẾP CHÈN NHỊ PHÂN – BINARY INSERTION SORT 89
V PHƯƠNG PHÁP ĐỔI CHỔ TRỰC TIẾP – INTERCHANGE SORT90 1 Giải thuật 90
2 Cài đặt 93
3 Đánh giá giải thuật 93
VI PHƯƠNG PHÁP NỔI BỌT – BUBBLE SORT 93
1 Giải thuật 93
2 Cài đặt 96
3 Đánh giá giải thuật 96
VII PHƯƠNG PHÁP SẮP XẾP NHANH – QUICK SORT 96
1 Giải thuật phân hoạch dãy al, al+1, , ar thành 2 dãy con 97
2 Giải thuật phân hoạch dãy sắp xếp dãy al, al+1, , ar 98
3 Cài đặt 99
4 Ðánh giá giải thuật 99
5 Ứng dụng Merge sort để sắp xếp danh sách 100
VIII PHƯƠNG PHÁP SẮP XẾP TRỘN – MERGE SORT 102
Trang 51 Giải thuật 102
2 Cài đặt 103
3 Đánh giá giải thuật 104
4 Ứng dụng Merge sort để sắp xếp danh sách 105
PHƯƠNG PHÁP ĐÁNH GIÁ VÀ HƯỚNG DẪN TỰ HỌC………… 108
CHƯƠNG 5 TÌM KIẾM……… 116
I TÌM KIẾM TUYẾN TÍNH 116
1 Giải thuật 116
2 Cài đặt 117
3 Đánh giá giải thuật 118
II TÌM KIẾM NHỊ PHÂN 118
1 Giải thuật 118
2 Cài đặt 119
3 Đánh giá giải thuật 120
THUẬT NGỮ KỸ THUẬT VÀ TÀI LIỆU THAM KHẢO………122
THUẬT NGỮ KỸ THUẬT 122
TÀI LIỆU THAM KHẢO 122
Trang 6PHẦN GIỚI THIỆU MÔN HỌC:
CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT
MÃ SỐ MÔN HỌC: MH 27
- Vị trí của môn học : Môn học được bố trí sau khi học sinh học xong môn/mô-đun: Lập trình C, Cơ sở Dữ liệu
- Tính chất của môn học : Là môn học chuyên ngành tự chọn
- Hiểu được dữ liệu là gì, giải thuật là gì, mối quan hệ mật thiết giữa cấutrúc dữ liệu và giải thuật
- Phân tích được đâu là dữ liệu, đâu là giải thuật, sự kết hợp chúng để tạothành một chương trình máy tính
- Biết cách tổ chức dữ liệu hợp lý, khoa học cho một chương trình đơngiản
- Biết áp dụng thuật toán hợp lý nhất đối với cấu trúc dữ liệu tương thích đểgiải quyết bài toán tối ưu nhất
- Biết và áp dụng được các phương pháp sắp xếp, tìm kiếm từ đơn giản
Mã
bài
Tổng số
Lý thuyế t
Thực hành
Kiểm tra
I Tổng quan về Cấu trúc dữ liệu
Trang 7IV PHƯƠNG PHÁP ĐÁNH GIÁ VÀ HƯỚNG DẪN TỰ HỌC
Biết và áp dụng được các phương pháp sắp xếp, tìm kiếm từ đơn giản
Về kỹ năng:
Đánh giá kỹ năng thực hành của học sinh:
Dùng một ngôn ngữ lập trình bất kỳ nào đó thể hiện trên máy tính các bàitoán cần kiểm nghiệm về: đệ qui, danh sách, sắp xếp, tìm kiếm
Về thái độ: Cẩn thận, tự giác chuyên cần trong học tập.
2 HƯỚNG DẪN THỰ HỌC
Sinh viên có thể tự học thông qua giáo trình này Tham khảo một số sách phần mục lục
Trang 8PHẦN BÀI HỌC CHƯƠNG 1:TỔNG QUAN VỀ CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT GIỚI THIỆU
Chương I nhằm giới thiệu cho sinh viên tổng thể cề cấu trúc dữ liệu vàgiải thuật trong lập trình cấu trúc, hiểu được các mối lien hệ giữa cấu trúc dữliệu và giải thuật, các kiều dữ liệu…Các tiêu chuẩn để đánh giá một giải thuật
Chương I gồm có 6 nội dung sau:
- Mối liên hệ giữa cấu trúc dữ liệu và giải thuật
- Các tiêu chuẩn đánh giá cấu trúc dữ liệu
- Các kiểu dữ liệu cơ bản
- Các kiểu dữ liệu có cấu trúc
- Kiểu con trỏ
- Đánh giá độ phức tập của giải thuật
I MỐI QUAN HỆ GIỮA CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT
Thực hiện một đề án tin học là chuyển bài toán thực tế thành bài toán cóthể giải quyết trên máy tính Một bài toán thực tế bất kỳ đều bao gồm các đốitượng dữ liệu và các yêu cầu xử lý trên những đối tượng đó Vì thế, để xây dựngmột mô hình tin học phản ánh được bài toán thực tế cần chú trọng đến hai vấn
đề :
1 Tổ chức biểu diễn các đối tượng thực tế
Các thành phần dữ liệu thực tế đa dạng, phong phú và thường chứa đựngnhững quan hệ nào đó với nhau, do đó trong mô hình tin học của bài toán, cầnphải tổ chức , xây dựng các cấu trúc thích hợp nhất sao cho vừa có thể phản ánhchính xác các dữ liệu thực tế này, vừa có thể dễ dàng dùng máy tính để xử lý
Công việc này được gọi là xây dựng cấu trúc dữ liệu cho bài toán
2 Xây dựng các thao tác xử lý dữ liệu
Khi giải quyết một bài toán trên máy tính, chúng ta thường có khuynhhướng chỉ chú trọng đến việc xây dựng giải thuật mà quên đi tầm quan trọng củaviệc tổ chức dữ liệu trong bài toán
Giải thuật phản ánh các phép xử lý , còn đối tượng xử lý của giải thuật lại
là dữ liệu, chính dữ liệu chứa đựng các thông tin cần thiết để thực hiện giảithuật Để xác định được giải thuật phù hợp cần phải biết nó tác động đến loại dữliệu nào (ví dụ để làm nhuyễn các hạt đậu , người ta dùng cách xay chứ khôngbăm bằng dao, vì đậu sẽ văng ra ngoài) và khi chọn lựa cấu trúc dữ liệu cũng
Trang 9cần phải hiểu rõ những thao tác nào sẽ tác động đến nó (ví dụ để biểu diễn cácđiểm số của sinh viên người ta dùng số thực thay vì chuỗi ký tự vì còn phải thựchiện thao tác tính trung bình từ những điểm số đó)
Như vậy trong một đề án tin học, giải thuật và cấu trúc dữ liệu có mối quan
hệ chặt chẽ với nhau, được thể hiện qua công thức :
Cấu trúc dữ liệu + Giải thuật = Chương trình
Với một cấu trúc dữ liệu đã chọn, sẽ có những giải thuật tương ứng, phùhợp Khi cấu trúc dữ liệu thay đổi thường giải thuật cũng phải thay đổi theo đểtránh việc xử lý gượng ép, thiếu tự nhiên trên một cấu trúc không phù hợp
Ví dụ 1.2.1:
Một chương trình quản lý điểm thi của sinh viên cần lưu trữ các điểm sốcủa 3 sinh viên Do mỗi sinh viên có 4 điểm số ứng với 4 môn học khác nhaunên dữ liệu có dạng bảng như sau:
- Chỉ xét thao tác xử lý là xuất điểm số các môn của từng sinh viên
- Giả sử có các phương án tổ chức lưu trữ sau:
Phương án 1 : Sử dụng mảng một chiều
Có tất cả 3(SV)*4(Môn) = 12 điểm số cần lưu trữ, do đó khai báo mảng
result như sau :
Trang 10Hình 1.1 Minh họa dùng mảng lưu trữ
Và truy xuất điểm số môn j của sinh viên i - là phần tử tại (dòng i, cột j)trong bảng - phải sử dụng một công thức xác định chỉ số tương ứng trong mảng
a:
bảngđiểm(dòng i, cột j) =>a[((i-1)*số cột) + j]
Ngược lại, với một phần tử bất kỳ trong mảng, muốn biết đó là điểm số củasinh viên nào, môn gì, phải dùng công thức xác định sau:a[ i ] => bảngđiểm(dòng((i / số cột) +1), cột (i % số cột) )
Với phương án này, thao tác xử lý được cài đặt như sau :
void XuatDiem() //Xuất điểm số của tất cả sinh viên{
const int so_mon = 4;
khi đó trong ma trậna các phần tử sẽ được lưu trữ như sau :
Trang 11Cột 0 Cột 1 Cột 2 Cột 3 Dòng 0 a[0][0] =7 a[0][1] =9 a[0][2] =5 a[0][3] =2
Dòng 1 a[1][0] =5 a[1][1] =0 a[1][2] =9 a[1][3] =4
Dòng 2 a[2][0] =6 a[2][1] =3 a[2][2] =7 a[2][3] =4
Và truy xuất điểm số môn j của sinh viên i - là phần tử tại (dòng i, cột j)trong bảng - cũng chính là phần tử nằm ở vị trí (dòng i, cột j) trong ma trậnbảngđiểm(dòng i,cột j) =>a[ i] [j]
Với phương án này, thao tác xử lý được cài đặt như sau :
void XuatDiem() //Xuất điểm số của tất cả sinh viên {
int so_mon = 4, so_sv =3;
for ( int i=0; i<so_sv; i+)
for ( int j=0; i<so_mon; j+)
printf("Điểm môn %d của sv %d là: %d",
II CÁC TIÊU CHUẨN ĐÁNH GIÁ CTDL
Do tầm quan trọng đã được trình bày trong phần 1.1, nhất thiết phải chútrọng đến việc lựa chọn một phương án tổ chức dữ liệu thích hợp cho đề án Mộtcấu trúc dữ liệu tốt phải thỏa mãn các tiêu chuẩn sau :
1 Phản ánh đúng thực tế
Đây là tiêu chuẩn quan trọng nhất, quyết định tính đúng đắn của toàn bộbài toán Cần xem xét kỹ lưỡng cũng như dự trù các trạng thái biến đổi của dữliệu trong chu trình sống để có thể chọn cấu trúc dữ liệu lưu trữ thể hiện chínhxác đối tượng thực tế
2 Phù hợp với các thao tác trên đó
Tiêu chuẩn này giúp tăng tính hiệu quả của đề án: việc phát triển các thuậttoán đơn giản, tự nhiên hơn; chương trình đạt hiệu quả cao hơn về tốc độ xử lý
Trang 123 Tiết kiệm tài nguyên hệ thống
Cấu trúc dữ liệu chỉ nên sử dụng tài nguyên hệ thống vừa đủ để đảm nhiệmđược chức năng của nó.Thông thường có 2 loại tài nguyên cần lưu tâm nhất :CPU và bộ nhớ Tiêu chuẩn này nên cân nhắc tùy vào tình huống cụ thể khi thựchiện đề án
Nếu tổ chức sử dụng đề án cần có những xử lý nhanh thì khi chọn cấu trúc
dữ liệu yếu tố tiết kiệm thời gian xử lý phải đặt nặng hơn tiêu chuẩn sử dụng tối
ưu bộ nhớ, và ngược lại
Ví dụ 1.3.1: Một số tình huống chọn cấu trúc lưu trữ lãng phí:
Sử dụng biến int (2 bytes) để lưu trữ một giá trị cho biết tháng hiện hành
Biết rằng tháng chỉ có thể nhận các giá trị từ 1-12, nên chỉ cần sử dụng kiểu
char (1 byte) là đủ.
Để lưu trữ danh sách học viên trong một lớp, sử dụng mảng 50 phần tử(giới hạn số học viên trong lớp tối đa là 50) Nếu số lượng học viên thật sự íthơn 50, thì gây lãng phí Trường hợp này cần có một cấu trúc dữ liệu linh độnghơn mảng- ví dụ xâu liên kết - sẽ được bàn đến trong các chương sau
1 Khái niệm về kiểu dữ liệu.
KiểudữliệuTcóthểxemnhưlàsựkếthợpcủa2thànhphần:
- MiềngiátrịmàkiểudữliệuTcóthểlưutrữ:V,
-Tậphợpcácphéptoánđểthaotácdữliệu:O T=<V,O>
Mỗikiểudữliệuthườngđượcđạidiệnbởimộttên(địnhdanh).MỗiphầntửdữliệucókiểuTsẽcógiátrịtrongmiềnVvàcóthểđượcthựchiệncácphéptoánthuộctậphợpcácphéptoántrongO
Đểlưutrữcácphầntửdữliệunàythườngphảitốnmộtsốbyte(s)trongbộnhớ,sốbyte(s)nàygọilàkíchthướccủakiểudữliệu
2 Các kiểu dữ liệu cơ bản
Kiểusốnguyên:Cóthểcódấuhoặckhôngcódấuvàthườngcócáckíchthướcsau:+Kiểusốnguyên1byte
Trang 13O={+,-,*,/,<,>,<=,>=,=,…}
Kiểukýtự:Cóthểcócáckíchthướcsau:
+Kiểukýtựbyte+Kiểukýtự2bytesKiểukýtựthườngđượcthựchiệnvớicácphéptoán:
Char 01 byte -128 đến 127 Có thể dùng như số nguyên 1
byte có dấu hoặc kiểu ký tựunsign char 01 byte 0 đến 255 Số nguyên 1 byte không dấu
32767unsign int 02 byte 0 đến 65335 Có thể gọi tắt là unsign
Long 04 byte -232 đến 231 -1
unsign long 04 byte 0 đến 232-1
3.4E38
Giới hạn chỉ trị tuyệt đối.Cácgiá trị <3.4E-38 được coi = 0.Tuy nhiên kiểu float chỉ có 7chữ số có nghĩa
1.7E308long double 10 byte 3.4E-4932¼
1.1E4932
Trang 14Một số điều đáng lưu ý đối với các kiểu dữ liệu cơ bản trong C là kiểu ký
tự (char) có thể dùng theo hai cách (số nguyên 1 byte hoặc ký tự) Ngoài ra Ckhông định nghĩa kiểu logic (boolean) mà nó đơn giản đồng nhất một giá trịnguyên khác 0 với giá trị TRUE và giá trị 0 với giá trị FALSE khi có nhu cầuxét các giá trị logic
Như vậy, trong C xét cho cùng chỉ có 2 loại dữ liệu cơ bản là số nguyên và
số thực Tức là chỉ có dữ liệu số Hơn nữa các số nguyên trong C có thể đượcthể hiện trong 3 hệ cơ số là hệ thập phân, hệ thập lục phân và hệ bát phân Nhờnhững quan điểm trên, C rất được những người lập trình chuyên nghiệp thíchdùng
Các kiểu cơ sở rất đơn giản và không thể hiện rõ sự tổ chức dữ liệu trongmột cấu trúc, thường chỉ được sử dụng làm nền để xây dựng các kiểu dữ liệuphức tạp khác
Trong nhiều trường hợp, chỉ với các kiểu dữ liệu cơ sở không đủ để phảnánh tự nhiên và đầy đủ bản chất của sự vật thực tế, dẫn đến nhu cầu phải xâydựng các kiểu dữ liệu mới dựa trên việc tổ chức, liên kết các thành phần dữ liệu
có kiểu dữ liệu đã được định nghĩa
Những kiểu dữ liệu được xây dựng như thế gọi là kiểu dữ liệu có cấu trúc
Đa số các ngôn ngữ lập trình đều cài đặt sẵn một số kiểu có cấu trúc cơ bản nhưmảng, chuỗi, tập tin, bản ghi và cung cấp cơ chế cho lập trình viên tự địnhnghĩa kiểu dữ liệu mới
Ví dụ : Để mô tả một đối tượng sinh viên, cần quan tâm đến các thông tin
sau:
- Mã số sinh viên: chuỗi ký tự
- Họ tên sinh viên: chuỗi ký tự
- Ngày sinh: kiểu ngày tháng
- Nơi sinh: chuỗi ký tự
- Điểm thi: số nguyên
Các kiểu dữ liệu cơ sở cho phép mô tả một số thông tin như :
Trang 15typedef struct ngaysinh NGAYSINH;
Cuối cùng, ta có thể xây dựng kiểu dữ liệu thể hiện thông tin về một sinhviên :
typedef struct sinhvien SINHVIEN;
Giả sử đã có cấu trúc phù hợp để lưu trữ một sinh viên, nhưng thực tế lạicần quản lý nhiều sinh viên, lúc đó nảy sinh nhu cầu xây dựng kiểu dữ liệumới
Mục tiêu của việc nghiên cứu cấu trúc dữ liệu chính là tìm những phươngcách thích hợp để tổ chức, liên kết dữ liệu, hình thành các kiểu dữ liệu có cấutrúc từ những kiểu dữ liệu đã được định nghĩa
1 Biến không động (biến tĩnh, biến nửa tĩnh):
Khi xây dựng chương trình, lập trình viên có thể xác định được ngay nhữngđối tượng dữ liệu luôn cần được sử dụng, không có nhu cầu thay đổi về số lượngkích thước do đó có thể xác định cách thức lưu trữ chúng ngay từ đầu Cácđối tượng dữ liệu này sẽ được khai báo như các biến không động Biến khôngđộng là những biến thỏa:
+Ðược khai báo tường minh,
+Tồn tại khi vào phạm vi khai báo và chỉ mất khi ra khỏi phạm vi này, +Ðược cấp phát vùng nhớ trong vùng dữ liệu (Data segment) hoặc là Stack(đối với biến nửa tĩnh - các biến cục bộ)
+Kích thước không thay đổi trong suốt quá trình sống
Trang 16Do được khai báo tường minh, các biến không động có một định danh đãđược kết nối với địa chỉ vùng nhớ lưu trữ biến và được truy xuất trực tiếp thôngqua định danh đó
+ Vp = {{các điạ chỉ có thể lưu trữ những đối tượng có kiểu T}, NULL}
(với NULL là một giá trị đặc biệt tượng trưng cho một giá trị không biết hoặckhông quan tâm)
+Op = {các thao tác định địa chỉ của một đối tượng thuộc kiểu T khi biết
con trỏ chỉ đến đối tượng đó} (thường gồm các thao tác tạo một con trỏ chỉ đếnmột đối tượng thuộc kiểu T; hủy một đối tượng dữ liệu thuộc kiểu T khi biếtcon trỏ chỉ đến đối tượng đó}
Nói một cách dễ hiểu, kiểu con trỏ là kiểu cơ sở dùng lưu địa chỉ của mộtđối tượng dữ liệu khác
Biến thuộc kiểu con trỏ Tp là biến mà giá trị của nó là địa chỉ cuả mộtvùng nhớ ứng với một biến kiểu T, hoặc là giá trị NULL
Cú pháp định nghĩa một kiểu con trỏ trong ngôn ngữ C :
typedef <kiểu con trỏ> *<kiểu cơ sở>;
Trang 17Các thao tác cơ bản trên kiểu con trỏ:(minh họa bằng C)
+ Khi 1 biến con trỏ p lưu địa chỉ của đối tượng x, ta nói ?p trỏ đến x?
+ Gán địa chỉ của một vùng nhớ con trỏ p:
p = <địa chỉ>;
p = <địa chỉ> + <giá trị nguyên>;
+ Truy xuất nội dung của đối tượng do p trỏ đến (*p)
3 Biến động
Trong nhiều trường hợp, tại thời điểm biên dịch không thể xác định trướckích thước chính xác của một số đối tượng dữ liệu do sự tồn tại và tăng trưởngcủa chúng phụ thuộc vào ngữ cảnh của việc thực hiện chương trình Các đốitượng dữ liệu có đặc điểm kể trên nên được khai báo như biến động
Biến động là những biến thỏa:
+ Biến không được khai báo tường minh
+ Có thể được cấp phát hoặc giải phóng bộ nhớ khi người sử dụng yêucầu
+ Các biến này không theo qui tắc phạm vi (tĩnh)
+ Vùng nhớ của biến được cấp phát trong Heap
Kích thước có thể thay đổi trong quá trình sống
Do không được khai báo tường minh nên các biến động không có một địnhdanh được kết buộc với địa chỉ vùng nhớ cấp phát cho nó, do đó gặp khó khănkhi truy xuất đến một biến động Ðể giải quyết vấn đề, biến con trỏ (là biếnkhông động) được sử dụng để trỏ đến biến động Khi tạo ra một biến động, phảidùng một con trỏ để lưu địa chỉ của biến này và sau đó, truy xuất đến biến độngthông qua biến con trỏ đã biết định danh
Hai thao tác cơ bản trên biến động là tạo và hủy một biến động do biến contrỏ p trỏ đến:
Tạo ra một biến động và cho con trỏ ?p? chỉ đến nó:
Hầu hết các ngôn ngữ lập trình cấp cao đều cung cấp những thủ tục cấpphát vùng nhớ cho một biến động và cho một con trỏ giữ địa chỉ vùng nhớ đó
Một số hàm cấp phát bộ nhớ của C :
void* malloc(size); // trả về con trỏ chỉ đến một vùng
// nhớ size byte vừa được cấp phát
void* calloc(n,size);// trả về con trỏ chỉ đến một vùng
// nhớ vừa được cấp phát gồm n
Trang 18p2 = (int*)calloc(10, sizeof(int));
(p2+3)* = 0; // đặt giá trị 0 cho phần tử thứ 4 // của mảng p2
free(p1); free(p2);
Khi nói đến hiệu qủa của một thuật toán, người ta thường quan tâm đến chiphí cần dùng để thực hiện nó Chi phí này thể hiện qua việc sử dụng tài nguyênnhư bộ nhớ, thời gian sử dụng CPU, … Ta có thể đánh giá thuật toán bằngphương pháp thực nghiệm thông qua việc cài đặt thuật toán rồi chọn các bộ dữliệu thử nghiệm Thống kê các thông số nhận được khi chạy các dữ liệu này ta sẽ
có một đánh giá về thuật toán
Tuy nhiên, phương pháp thực nghiệm có một số nhược điểm sau khiến cho
PHƯƠNG PHÁP ĐÁNH GIÁ VÀ HƯỚNG DẪN TỰ HỌC:
1 PHƯƠNG PHÁP ĐÁNH GIÁ
Về kiến thức:
Trang 19+ Được đánh giá kiến thức qua việc đặt câu hỏi kiểm tra trao đổi trựctiếp đạt được các yêu cầu sau:
+ Hiểu được mối quan hệ mật thiết giữa cấu trúc dữ liệu và giải thuật.+ Phân tích được đâu là dữ liệu, đâu là giải thuật, sự kết hợp chúng đểtạo thành một chương trình máy tính
Về thái độ: Cẩn thận, tự giác chuyên cần trong học tập.
đủ để đáp ứng mọi yêu cầu về tổ chức dữ liệu không ?
3 Một ngôn ngữ lập trình có nên cho phép người sử dụng tự định nghĩathêm các kiểu dữ liệu có cấu trúc ? Giải thích và cho ví dụ
4 Cấu trúc dữ liệu và cấu trúc lưu trữ khác nhau những điểm nào ? Mộtcấu trúc dữ liệu có thể có nhiều cấu trúc lưu trữ được không ? Ngược lại, mộtcấu trúc lưu trữ có thể tương ứng với nhiều cấu trúc dữ liệu được không ? Cho ví
dụ minh hoạ
5.Giả sử có một bảng giờ tàu cho biết thông tin về các chuyến tàu khácnhau của mạng đường sắt Hãy biểu diễn các dữ liệu này bằng một cấu trúc dữliệu thích hợp (file, array, struct ) sao cho dễ dàng truy xuất giờ khởi hành, giờđến của một chuyến tàu bất kỳ tại một nhà ga bất kỳ
2.2 Bài tập thực hành :
Giả sử quy tắc tổ chức quản lý nhân viên của một công ty như sau :
Thông tin về một nhân viên bao gồm lý lịch và bảng chấm công :
+ Lý lịch nhân viên :
- Mã nhân viên : chuỗi 8 ký tự
- Tên nhân viên : chuỗi 20 ký tự
- Tình trạng gia đình : 1 ký tự ( M = Married, S = Single)
- Số con : số nguyên 20
- Trình độ văn hoá : chuỗi 2 ký tự
Trang 20- Số ngày nghỉ không phép trong tháng : số 28
- Số ngày làm thêm trong tháng : số 28
- Kết qủa công việc : chuỗi 2 ký tự
- số con > 2: Phụ trội = +5% Lương căn bản
- trình độ văn hoá = CH: Phụ trội = +10%Lương căn bản
- làm thêm: Phụ trội=+4%Lương căn bản/ngày
- nghỉ không phép: Phụ trội= -5%Lương căn bản/ngày
Chức năng yêu cầu :
- Cập nhật lý lịch, bảng chấm công cho nhân viên (thêm, xoá, sửa)
- Xem bảng lương hàng tháng
- Tìm thông tin của một nhân viên
Tổ chức cấu trúc dữ liệu thích hợp để biểu diễn các thông tin trên, và cài đặt chương trình theo các chức năng đã mô tả.
Lưu ý :Nên phân biệt các thông tin mang tính chất tĩnh ( lý lịch) và động ( chấm
công hàng tháng) Số lượng nhân viên tối đa là 50 người
Trang 21CHƯƠNG 2: ĐỆ QUI VÀ GIẢI THUẬT ĐỆ QUI GIỚI THIỆU
Chương 2 giúp cho sinh viên cái nhìn về lập trình đệ qui và giải thuật đệqui trong lập trình Các khái niệm về đệ qui và chương trình đệ qui Các bài toán
đệ qui minh họa cụ thể
MỤC TIÊU
- Khái niệm được đệ qui
- Trình bày được giải thuật và chương trình sử dụng giải thuật đệ qui
- So sánh giải thuật đệ qui với các giải thuật khác để rút ra tính ưu việt hoặcnhược điểm của giải thuật
- Thực hành (lập trình và biên dịch) được với các bài toán đệ qui đơn giản
NỘI DUNG
Chương 2 gồm có 4 nội dung chính sau:
- Khái niệm đệ qui
- Giải thuật đệ quivà chương trình đệ qui
- Ưu và nhược điểm của giải thuật đệ qui
- Các bài toán đệ quy cơ bản
Đệ qui là một khái niệm cơ bản trong toán học và khoa học máy tính Mộtđối tượng được gọi là đệ qui nếu nó hoặc một phần của nó được định nghĩathông qua khái niệm về chính nó
Một số ví dụ điển hình về việc định nghĩa bằng đệ qui là:
1 Định nghĩa số tự nhiên
0 là số tự nhiên
Nếu k là số tự nhiên thì k+1 cũng là số tự nhiên
Như vậy, bắt đầu từ phát biểu “0 là số tự nhiên”, ta suy ra 0+1=1 là số tựnhiên Tiếp theo 1+1=2 là số tự nhiên, v.v
2 Định nghĩa xâu ký tự bằng đệ qui
Xâu rỗng là 1 xâu ký tự
Một chữ cái bất kỳ ghép với 1 xâu sẽ tạo thành 1 xâu mới
Từ phát biểu “Xâu rỗng là 1 xâu ký tự”, ta ghép bất kỳ 1 chữ cái nào vớixâu rỗng đều tạo thành xâu ký tự
Như vậy, chữ cái bất kỳ có thể coi là xâu ký tự Tiếp tục ghép 1 chữ cáibất kỳ với 1 chữ cái bất kỳ cũng tạo thành 1 xâu ký tự, v.v
3 Định nghĩa hàm giai thừa
Khi n=0, định nghĩa 0!=1
Trang 22Khi n>0, định nghĩa n!=(n-1)! x n
Như vậy, khi n=1, ta có 1!=0!x1 = 1x1=1 Khi n=2, ta có 2!=1!x2=1x2=2,v.v
4 Hàm đệ quy
Hàm đệ quy là một hàm trong đó có dùng lời gọi hàm đến chính bản thân
nó Ví dụ ta có hàm đệ quy như sau:
int Sum(int n) { if (n==0) return 0; else return (n+Sum(n-1)); // gọi đệ quy đến chính bản thân hàm sum }
Khi một hàm đệ quy gọi đến chính nó thì mỗi lần gọi máy sẽ tạo ra tập cácbiến cục bộ mới hoàn toàn độc lập với biến cục bộ đã tạo ra trong lần gọi trước.Bao nhiêu lần gọi hàm đệ quy thì tương ứng với bấy nhiêu lần thoát ra khỏihàm, mỗi lần ra khỏi hàm thì tập biến cục bộ bị xóa
Có một sự tương ứng giữa các lời gọi hàm và lần thoát khỏi hàm theo thứ
tự ngược lại: lần ra khỏi hàm đầu tiên tương ứng với lần gọi hàm cuối cùng
Ví dụ minh họa hàm đệ quy: Tính giai thừa của n (tích của các số từ 1 đến
n) Ta có định nghĩa của giai thừa n như sau: n! = 1.2.3 (n-1).n hoặc địnhnghĩa: (1)!.1
Phân tích chương trình đệ quy:
Giả sử chương trình có lời gọi hàm như sau
long I=Giaithua(5);
Trang 23Hình 2.1 Minh họa giải thuật đệ qui
Lưu ý: Hàm đệ quy dùng nhiều vùng nhớ trên ngăn xếp do đó có thể dẫn
đến tràn ngăn xếp Do đó nếu một bài toán có thể dùng phương pháp lặp (không
đệ quy) để giải quyết thì nên sử dụng cách này
Trong lĩnh vực lập trình, một chương trình máy tính gọi là đệ qui nếutrong chương trình có lời gọi chính nó
Một chương trình không thể gọi mãi chính nó, vì như vậy sẽ tạo ra mộtvòng lặp vô hạn
Trên thực tế, một chương trình đệ qui trước khi gọi chính nó bao giờ cũng
có một thao tác kiểm tra điều kiện dừng Nếu điều kiện dừng thỏa mãn, chươngtrình sẽ không gọi chính nó nữa, và quá trình đệ qui chấm dứt
Trang 24Trong các ví dụ ở trên, ta đều thấy có các điểm dừng Chẳng hạn, trong ví
dụ thứ nhất, nếu k = 0 thì có thể suy ngay k là số tự nhiên, không cần tham chiếuxem k-1 có là số tự nhiên hay không
Khi chương trình gọi chính nó, mục đích là để giải quyết 1 vấn đề tương tự,nhưng nhỏ hơn
Khi chương trình gọi tới chính nó, các tham số, hoặc khoảng tham số,thường trở nên nhỏ hơn, để phản ánh 1 thực tế là vấn đề đã trở nên nhỏ hơn, dễhơn Khi tham số giảm tới mức cực tiểu, một điều kiện so sánh được kiểm tra vàchương trình kết thúc, chấm dứt việc gọi tới chính nó
Ví dụ 2.2.1: Tính tổng các số nguyên từ 1 đến N
NN −1 N − 2
∑ i = N + ∑ i = N + (N − 1) + ∑ i
i=1 ii−1
Ta phân tích như sau:
+ Trường hợp đặc biệt N=1 thì kết quả là 1
Trường hợp khác ta thực hiện đệ quy: N + Tong(N-1)
Ví dụ 2.2.2: tìm USCLN của hai số nguyên dương a, b
Trường hợp đặc biệt khi a = b khi đó USCLN(a, b) = a
Trường hợp chung a và b khác nhau ta có thể thực hiện đệ quy như sau:
- USCLN(a, b) = USCLN(a-b, b) nếu a>b
- USCLN(a, b) = USCLN(a, b-a) nếu a<b
Hàm tìm USCLN đệ quy được viết như sau:
int USCLN(int a, int b)
}
Ví dụ 2.2.3: Tính an
Trường hợp đặc biệt n = 0, kết quả là 1
Trường hợp khác, kết quả là a * a(n-1).
Trang 25III ƯU VÀ NHƯỢC ĐIỂM CỦA GIẢI THUẬT ĐỆ QUI
Ưu điểm của chương trình đệ qui cũng như định nghĩa bằng đệ qui là có thểthực hiện một số lượng lớn các thao tác tính toán thông qua 1 đoạn chương trìnhngắn gọn (thậm chí không có vòng lặp, hoặc không tường minh để có thể thựchiện bằng các vòng lặp) hay có thể định nghĩa một tập hợp vô hạn các đối tượngthông qua một số hữu hạn lời phát biểu
Thông thường, một chương trình được viết dưới dạng đệ qui khi vấn đề cần
xử lý có thể được giải quyết bằng đệ qui Tức là vấn đề cần giải quyết có thể đưađược về vấn đề tương tự, nhưng đơn giản hơn
Vấn đề này lại được đưa về vấn đề tương tự nhưng đơn giản hơn nữa v.v,cho đến khi đơn giản tới mức có thể trực tiếp giải quyết được ngay mà khôngcần đưa về vấn đề đơn giản hơn nữa
IV CÁC BÀI TOÁN ĐỆ QUI CƠ BẢN
1 Bài toán tháp Hà nội
Cho 3 cột tháp được đặt tên là C1, C2, và C3 Có N đĩa có đường kính giảmdần và được sắp như hình vẽ Hãy dịch chuyển N đĩa đó sang cột C2, theonguyên tắc sau: mỗi lần chỉ dịch được một đĩa, không được để một đĩa có đườngkính lớn nằm trên đĩa có đường kính nhỏ Ta phân tích cách thực hiện như sau:
lớn sang C2, chuyển đĩa nhỏ từ C3 sang C2
Hình 2.2Minh họa tháp Hà nội với n=2
Với N = 3: ta thực hiện với giả thiết đã biết cách làm với N-1 đĩa (2 đĩa
trong ví dụ N=3): chuyển đĩa 1 và 2 sang cọc 3, chuyển đĩa 3 sang cọc 2, chuyểnhai đĩa 1, 2 từ cọc 3 sang cọc 2
Trang 26Hình 2.3Minh họa tháp Hà nội với n=3
Trong trường hợp N = 3 như hình trên thực hiện ba bước để đưa 3 đĩa vềcọc 2: gồm B1, B2 và B3 Với B2 thì đơn giản do chuyển 1 đĩa, còn bước B1 vàB3 phải di chuyển nhiều hơn 1 đĩa nên chúng sẽ bao gồm nhiều bước nhỏ trong
đó B1 gồm {B1.1, B1.2, B1.3} và B2 gồm {B2.1, B2.2, B2.3}.Cuối cùng cáchthực hiện theo các bước: B1.1 ⇒ B1.2 ⇒ B1.3 ⇒ B2 ⇒ B3.1 ⇒ B3.1⇒ B3.3
Hình 2.4Minh họa trên tháp Hà nội với n=4
Chúng ta định nghĩa hàm DichChuyen chuyển N đĩa từ cọc nguồn, sang cọcđích thông qua một cọc trung gian (cọc thứ 3 còn lại)
Trang 27Hàm này định nghĩa như sau:
DichChuyen(N, Nguon, Dich, Trung gian);
Với N = 2 ta diễn tả lại như sau:
Trang 282 Bài toán tính tổng các phần tử trong mảng dùng đệ quy:
Cho dãy a[1:n], gọi hàm Sum là hàm đệ quy tính tổng, khi đó tổng của dãya[1:n] là Sum(a[1:n])
Sum(a[1:n]) = Sum(a[1:n-1]) + a[n] và Sum(a[m:m]) = a[m], trường hợpm=1 thì Sum(a[1:1]) = a[1]
Hình 2.5 Minh họa bài toán tính tổng các phần tử trong mảng dùng đệ quy
Trang 29PHƯƠNG PHÁP ĐÁNH GIÁ VÀ HƯỚNG DẪN TỰ HỌC
Tìm hiểu thêm về phân loại đệ quy
o Đệ quy trực tiếp: Trong một hàm có lời gọi hàm đến chính bản thân nó
o Đệ quy tuyến tính:Thân hàm gọi một lần đến chính nó
Trang 30 Tìm hiểu cơ chế thực hiện hàm đệ quy
Tại mỗi thời điểm của hàm đệ quy được đặc trưng bởi: nội dung các biến
và các lệnh cần thực hiện tiếp theo Do đó tại mỗi thời điểm trong tiến trình xử
lý của hàm đệ quy cần phải lưu trữ cả các trạng thái xử lý dang dở
Ví dụ: trong hàm đệ quy tính giai thừa n,
GT(n):
if (n == 0) return 1;
else
return (n* GT(n-1));
Trong trường hợp n=3
Trang 31Hình 2.6 Minh họa bài toán tính giai thừa dùng đệ quy
Khi thực hiện lời gọi GT(3) thì sẽ phát sinh lời gọi hàm đến GT(2) và đồngthời phải lưu giữ thông tin trạng thái xử lý còn dang dở GT(3) = 3 * GT(2) Đếnlượt hàm GT(2) sẽ phát sinh lời gọi hàm đến GT(1) và lưu giữ thông tin trạngthái còn dang dở GT(2) = 2 * GT(1)…Quá trình cứ thực hiện tương tự cho tớikhi gặp trường hợp suy biến GT(0) = 1
Kết thúc quá trình gọi đệ quy là quá trình xử lý ngược được thực hiện: Giátrị của GT(0) được dùng để tính GT(1) theo quá trình lưu trữ Dùng giá trị GT(1)
để tính GT(2) theo quá trình tương tự Dùng giá trị GT(2) để tính GT(3) để ra kếtquả cuối cùng Song song với quá trình xử lý ngược là xóa bỏ thông tin lưu trữtrong những lần gọi hàm tương ứng Các trường hợp khử đệ quy đơn giản
Ví dụ hàm đệ quy tính giá trị dãy Fibonacci
Fibo(n)
if (n ==0) || (n == 1)
return 1;
else return (Fibo(n-1) + Fibo(n-2));
Trang 32Hình 2.7Minh họa đệ quy tính dãy Fibonaci
Do đặc điểm của quá trình xử lý một hàm đệ quy: việc thực thi lời gọi đệquy sinh ra lời gọi đệ quy mới cho đến khi gặp trường hợp suy biến, do đó cầnphải có cơ chế lưu trữ thông tin thoả yêu cầu:
o Ở mỗi lần gọi phải lưu trữ thông tin trạng thái con còn đang xử lý dang
dở, số trạng thái này bằng với số lần gọi chưa hoàn tất
o Sau khi thực hiện xong một lần gọi thứ k, cần khôi phục lại toàn bộthông tin trạng thái của lần gọi trước đó là lần gọi k-1
o Lệnh gọi cuối cùng (trường hợp suy biến) sẽ được hoàn tất trước tiên.Các lệnh gọi sau sẽ hoàn thành trước, do đó dãy thông tin trạng thái được hồiphục theo thứ tự ngược với thứ tự lưu trữ
Cấu trúc dữ liệu ngăn xếp lưu trữ theo kiểu Last In First Out thoả các yêucầu trên nên được sử dụng để lưu trữ thông tin trạng thái của quá trình xử lý đệquy Thông thường đệ quy là phương pháp giúp chúng ta tìm giải thuật chonhững bài toán khó Kết quả của giải thuật đệ quy thường rất gọn gàng, dễ hiểu
và dễ chuyển thành các chương trình trên các ngôn ngữ lập trình Tuy nhiên,việc xử lý giải thuật đệ quy cũng gây khó khăn cho máy về không gian lưu trữ
và thời gian xử lý Vì vậy việc thay thế một chương trình đệ quy bằng mộtchương trình không đệ quy cũng được quan tâm rất nhiều Thông thường khigặp một bài toán khó giải quyết theo hướng không đệ quy thì người ta thực hiệnquá trình như sau:
o Dùng quan niệm đệ quy để tìm giải thuật cho bài toán
o Mã hoá giải thuật đệ quy
o Khử đệ quy để có một chương trình không đệ quy
Quá trình trên gọi là khử đệ quy, đôi khi việc khử đệ quy cũng không dễdàng gì, nên nhiều khi cũng phải chấp nhận chương trình đệ quy!
o Các trường hợp khử đệ quy đơn giản
Ví dụ 1: hàm tính giai thừa không đệ quy
long int GiaiThua( int n)
Trang 33while ( k < n ) {
{
A(X) P(f(X)) }
}
Trong đó:
X: là biến (một hay nhiều biến) P(X): là hàm đệ quy phụ thuộc X A(X) vàD(X): là các nhóm lệnh không đệ quy f(X): là hàm biến đổi x trong lần gọi thứ
Pi nếu B(fi(X)) không đúng thì thực hiện lệnh X và gọi
Pi+1, ngược lại B(fi(X)) đúng thì thực hiện D(X) và kết thúc quá trình gọi(Pi ko gọi thêm hàm đệ quy khác)
Ví dụ: Tìm USCLN của hai số dựa vào thuật toán Euclide Giải thuật đệ quy
USCLN(m ,n) bằng Euclide như sau :
void USCLN( int m, int n, int & kq)
Trong trường hợp này:
X là m, n và kq P(X) : USCLN(m, n, kq) B(X) : n ==0 D(X) : kq = m ;A(X) : không có f(x): USCLN(n, m %n, kq) Hàm USCLN không đệ quy đượcthể hiện như sau:
void USCLN(int m, int n, int & kq)
Trang 35CHƯƠNG 3: DANH SÁCH GIỚI THIỆU
Chương 3 nhằm giới thiệu cho sinh viên cấu trúc danh sách và các loạidanh sách trong lập trình cấu trúc Cấu trúc của một danh sách liên kết đơn,danh sách liên kết kép, stack, queue Cách biều diễn và ứng dụng trong thực tếcủa các loại danh sách
MỤC TIÊU
- Định nghĩa được danh sách liên kết
- Khai thác được các loại danh sách liên kết: liên kết đơn; kép; ngăn xếp; hàngđợi (về cách tổ chức và các thao tác xử lý cơ bản trên danh sách liên kết)
- Giải các bài toán sử dụng danh sách liên kết, ngăn xếp, hàng đợi
NỘI DUNG
Chương 3 gồm có 4 nội dung chính sau:
- Danh sách liên kết đơn
- Danh sách liên kết kép
- Satck (ngăn xếp)
- Queue (hàng đợi)
A Định nghĩa danh sách liên kết
Cho T là một kiểu được định nghiã trước, kiểu danh sách Tx gồm các phần
tử thuộc kiểu T được định nghĩa là:
Ví du: Hồ sơ các học sinh của một trường được tổ chức thành danh sách
gồm nhiều hồ sơ của từng học sinh; số lượng học sinh trong trường có thể thayđổi do vậy cần có các thao tác thêm, hủy một hồ sơ; để phục vụ công tác giáo vụcần thực hiện các thao tác tìm hồ sơ của một học sinh, in danh sách hồ sơ
B Các hình thức tổ chức của danh sách liên kết
Có nhiều hình thức tổ chức mối liên hệ tuần tự giữa các phần tử trong cùngmột danh sách:
Mối liên hệ giữa các phần tử được thể hiện ngầm: mỗi phần tử trong danh
sách được đặc trưng bằng chỉ số Cặp phần tử xi, xi+1 được xác định là kế cậntrong danh sách nhờ vào quan hệ giữa cặp chỉ số i và (i+1) Với hình thức tổ
Trang 36chức này, các phần tử của danh sách thường bắt buộc phải lưu trữ liên tiếp trong
bộ nhớ để có thể xây dựng công thức xác định địa chỉ phần tử thứ i:
1 2 3 4 5
9 4 5 3 8 address(i) = address(1) + (i-1)*sizeof(T)
Có thể xem mảng và tập tin là những danh sách đặc biệt được tổ chức theohình thức liên kết "ngầm" giữa các phần tử Tuy nhiên mảng có một đặc trưnggiới hạn là số phần tử mảng cố định, do vậy không có thao tác thêm, hủy trênmảng; trường hợp tập tin thì các phần tử được lưu trữ trên bộ nhớ phụ có nhữngđặc tính lưu trữ riêng sẽ được trình bày chi tiết ở giáo trình Cấu trúc dữ liệu 2 Cách biểu diễn này cho phép truy xuất ngẫu nhiên, đơn giản và nhanh chóngđến một phần tử bất kỳ trong danh sách, nhưng lại hạn chế về mặt sử dụng bộnhớ Ðối với mảng, số phần tử được xác định trong thời gian biên dịch và cầncấp phát vùng nhớ liên tục Trong trường hợp tổng kích thước bộ nhớ trống còn
đủ để chứa toàn bộ mảng nhưng các ô nhớ trống lại không nằm kế cận nhau thìcũng không cấp phát vùng nhớ cho mảng được Ngoài ra do kích thước mảng
cố định mà số phần tử của danh sách lại khó dự trù chính xác nên có thể gây ratình trạng thiếu hụt hay lãng phí bộ nhớ Hơn nữa các thao tác thêm, hủy mộtphần tử vào danh sách được thực hiện không tự nhiên trong hình thức tổ chứcnày
Mối liên hệ giữa các phần tử được thể hiện tường minh: mỗi phần tử ngoài
các thông tin về bản thân còn chứa một liên kết (địa chỉ) đến phần tử kế trongdanh sách nên còn được gọi là danh sách móc nối Do liên kết tường minh, vớihình thức này các phần tử trong danh sách không cần phải lưu trữ kế cận trong
bộ nhớ nên khắc phục được các khuyết điểm của hình thức tổ chức mảng, nhưngviệc truy xuất đến một phần tử đòi hỏi phải thực hiện truy xuất qua một số phần
tử khác Có nhiều kiểu tổ chức liên kết giữa các phần tử trong danh sách như :
Danh sách liên kết đơn: mỗi phần tử liên kết với phần tử đứng sau nó
trong danh sách:
Danh sách liên kết kép: mỗi phần tử liên kết với các phần tử đứng trước
và sau nó trong danh sách:
Danh sách liên kết vòng : phần tử cuối danh sách liên kết với phần tử đầu
danh sách:
Trang 37Hình thức liên kết này cho phép các thao tác thêm, hủy trên danh sách đượcthực hiện dễ dàng, phản ánh được bản chất linh động của danh sách
I DANH SÁCH LIÊN KẾT ĐƠN
1 Cấu trúc dữ liệu của một phần tử trong danh sách liên kết đơn
Mỗi phần tử của danh sách đơn là một cấu trúc chứa 2 thông tin :
+ Thành phần dữ liệu: lưu trữ các thông tin về bản thân phần tử
+ Thành phần mối liên kết: lưu trữ địa chỉ của phần tử kế tiếp trong danh
sách, hoặc lưu trữ giá trị NULL nếu là phần tử cuối danh s ách
typerdef strucr node NODE;
Ví dụ 3.1.1.1 : Ðịnh nghĩa danh sách đơn lưu trữ hồ sơ sinh viên:
};typerdef struc nodesinhvien NODESINHVIEN;
Một phần tử trong danh sách đơn là một biến động sẽ được yêu cầu cấpphát khi cần Và danh sách đơn chính là sự liên kết các biến động này với nhau
do vậy đạt được sự linh động khi thay đổi số lượng các phần tử
Nếu biết được địa chỉ của phần tử đầu tiên trong danh sách đơn thì có thểdựa vào thông tin pNext của nó để truy xuất đến phần tử thứ 2 trong xâu, và lạidựa vào thông tin Next của phần tử thứ 2 để truy xuất đến phần tử thứ3 Nghĩa là để quản lý một xâu đơn chỉ cần biết địa chỉ phần tử đầu xâu
Trang 38Thường một con trỏ Head sẽ được dùng để lưu trữ địa chỉ phần tử đầu xâu,
ta gọi Head là đầu xâu Ta có khai báo:
NODE*pHead;
Tuy về nguyên tắc chỉ cần quản lý xâu thông qua đầu xâu pHead, nhưngthực tế có nhiều trường hợp cần làm việc với phần tử cuối xâu, khi đó mỗi lầnmuốn xác định phần tử cuối xâu lại phải duyệt từ đầu xâu Ðể tiện lợi, có thể sửdụng thêm một con trỏ pTail giữ địa chỉ phần tử cuối xâu
Khai báo pTail như sau :
NODE *pTail;
Lúc này có xâu đơn:
Hình 3.1 Minh họa danh sách liên kết đơn
2 Các thao tác cơ bản trên danh sách đơn
2.1 Thêm một phần tử vào danh sách
Hình 3.2 Minh họa thêm một phần tử vào danh sách liên kết đơn
Có 3 loại thao tác chèn p vào xâu:
2.1.1 Chèn vào đầu danh sách
Thuật toán :
Trang 39Bắt đầu: Nếu Danh sách rỗng Thì
Trang 402.1.2 Chèn vào cuối danh sách
Hình 3.3 Minh họa chèn một phần tử vào cuối danh sách liên kết đơn