Tải bản đầy đủ (.doc) (37 trang)

Đề tài : TÌM HIỂU THUẬT TOÁN SẮP XẾP HEAP SORT VÀ ỨNG DỤNG GIẢI BÀI TOÁN NGƯỜI BÁN HÀNG

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (665.29 KB, 37 trang )

TRƯỜNG ĐẠI HỌC CÔNG NGHỆ THÔNG TIN& TRUYỀN THÔNG
KHOA CÔNG NGHỆ THÔNG TIN

------------o0o------------

BÁO CÁO
THỰC TẬP CƠ SỞ
Đề tài :

TÌM HIỂU THUẬT TOÁN SẮP XẾP HEAP SORT VÀ ỨNG DỤNG
GIẢI BÀI TOÁN NGƯỜI BÁN HÀNG

Giáo viên hướng dẫn : Đặng Thị Oanh
Sinh viên thực hiện

: Nguyễn Hữu Thi

Lớp

: CNTT_K12D

Thái Nguyên, tháng 04 năm 2016

1


MỤC LỤC
MỤC LỤC............................................................................................................2
LỜI NÓI ĐẦU.....................................................................................................4
CHƯƠNG 1..........................................................................................................5
KIẾN THỨC CƠ SỞ...........................................................................................5


1.1. BÀI TOÁN SẮP XẾP...................................................................................................5
1.1.1.Khái niệm bài toán sắp xếp.......................................................5
1.1.2.Nội dung, ví dụ phân tích về bài toán sắp xếp..........................5
1.2. TỔNG QUAN VỀ NGÔN NGỮ LẬP TRÌNH C.........................................................9
1.2.1. Sự phát triển của ngôn ngữ lập trình C.....................................9
1.2.2. Cấu trúc chương trình C..........................................................11
1.2.3. Các kiểu dữ liệu và khai báo biến...........................................13

CHƯƠNG 2........................................................................................................16
THUẬT TOÁN HEAP SORT...........................................................................16
2.1. Ý TƯỞNG..................................................................................................................16
2.2. NỘI DUNG.................................................................................................................17
2.2.1. Các tính chất của Heap...........................................................17
2.2.2. Giải thuật Heap Sort...............................................................18
2.2.3. Ðánh giá giải thuật.................................................................18
2.3. THỦ TỤC HEAP SORT.............................................................................................18
2.4. VÍ DỤ..........................................................................................................................18
2.5 LƯU ĐỒ THUẬT TOÁN............................................................................................21
2.6. ĐỘ PHỨC TẠP CỦA THUẬT TOÁN.......................................................................23

CHƯƠNG 3........................................................................................................24
ỨNG DỤNG VÀ CHƯƠNG TRÌNH...............................................................24
3.1. BÀI TOÁN NGƯỜI BÁN HÀNG..............................................................................24
3.1.1. Bài toán..................................................................................24
3.1.2. Phân tích.................................................................................24
3.1.3. Độ phức tạp của thuật toán....................................................26
3.1.4. Cài đặt thuật toán...................................................................26
3.2.1. Công cụ lập trình....................................................................31
2



3.2.2. Kết quả thực hiện...................................................................32

KẾT LUẬN........................................................................................................35
TÀI LIỆU THAM KHẢO................................................................................36
Chữ ký GV hướng dẫn......................................................................................37
LỜI CẢM ƠN
Để có thể hoàn tất được bài thực tập này, em xin gửi lời cảm ơn sâu sắc
nhất đến quý Thầy Cô Khoa Công nghệ thông tin, Trường Đại học Công Nghệ
Thông Tin và Truyền Thông Thái Nguyên, với tri thức và tâm huyết của mình đã
truyền đạt vốn kiến thức quý báu cho chúng em trong suốt thời gian học tập tại
trường.
Em xin chân thành cảm ơn TS Đặng Thị Oanh đã tận tâm giúp đỡ và
hướng dẫn em trong suốt quá trình thực hiện đề tài này. Nếu không có sự hướng
dẫn, chỉ bảo của cô thì em khó có thể hoàn thiện được.
Em cũng xin chân thành cảm ơn đến bạn bè đã giúp đỡ, trao đổi hoàn
thiện bài thực tập cơ sở này.

Thái Nguyên, ngày 29 tháng 04 năm 2016
Sinh viên

Nguyễn Hữu Thi

3


LỜI NÓI ĐẦU
Hiện nay công nghệ thông tin ngày càng phát triển và được ứng dụng rộng
rãi trong tất cả các lĩnh vực của cuộc sống. Trong hầu hết các hệ lưu trữ, quản
lý dữ liệu, thao tác tìm kiếm thường được thực hiện nhiều nhất để khai thác

thông tin một cách nhanh chóng.
Muốn việc tìm kiếm nhanh chóng thì dữ liệu cần phải được sắp xếp sẵn,
ngăn nắp theo một trật tự, hệ thống nhất định sẽ cho phép chúng ta tìm kiếm
nhanh. Việc tìm kiếm, sắp xếp có ý nghĩa rất lớn trong việc quản lý và lưu trữ.
Nội dung mà em muốn trình bày dưới đây là một trong số những thuật toán
sắp xếp thông dụng nhất và đó cũng là nội dung mà cô Đặng Thị Oanh đã giúp
đỡ và hướng dẫn em nghiên cứu về đề tài “Tìm hiểu thuật toán sắp xếp kiểu
vun đống (Heap Sort) và ứng dụng giải bài toán người giao hàng”.
Mục tiêu và nhiệm vụ
- Mô tả về thuật toán sắp xếp kiểu vun đống (Heap Sort)
- Hướng đến các kỹ thuật lập trình với ngôn ngữ lập trình C
- Áp dụng kết quả nghiên cứu để tạo một DEMO mô phỏng thuật toán sắp
xếp qua bài toán người giao hàng.
Em rất mong nhận được sự thông cảm và đóng góp của các Thầy, Cô để lần
sau làm bài được tốt hơn.

Em xin trân thành cảm ơn !

Thái Nguyên, ngày 29 tháng 04 năm 2016

4


CHƯƠNG 1
KIẾN THỨC CƠ SỞ
1.1. BÀI TOÁN SẮP XẾP
1.1.1. Khái niệm bài toán sắp xếp
Sắp xếp là quá trình bố trí lại các phần tử của một tập đối tượng nào đó theo
một thứ tự nhất định. Chẳng hạn như thứ tự tăng dần (hay giảm dần) đối với
một dãy số, thứ tự từ điển đối với các từ v.v… Yêu cầu về sắp xếp thường

xuyên xuất hiện trong các ứng dụng Tin học với các mục đích khác nhau: sắp
xếp dữ liệu trong máy tính để tìm kiếm cho thuận lợi, sắp xếp các kết quả xử lý
để in ra trên bảng biểu v.v…
1.1.2. Nội dung, ví dụ phân tích về bài toán sắp xếp
Dữ liệu có thể xuất hiện dưới nhiều dạng khác nhau, nhưng ở đây ta quy
ước: Một tập các đối tượng cần sắp xếp là tập các bản ghi (records), mỗi bản
ghi bao gồm một số trường (fields) khác nhau. Nhưng không phải toàn bộ các
trường dữ liệu trong bản ghi đều được xem xét đến trong quá trình sắp xếp mà
chỉ là một trường nào đó (hay một vài trường nào đó) được chú ý tới thôi.
Trường như vậy ta gọi là khoá (key). Sắp xếp sẽ được tiến hành dựa vào giá trị
của khoá này.
Ví dụ: Hồ sơ tuyển sinh của một trường Đại học là một danh sách thí sinh,
mỗi thí sinh có tên, số báo danh, điểm thi. Khi muốn liệt kê danh sách những
thí sinh trúng tuyển tức là phải sắp xếp các thí sinh theo thứ tự từ điểm cao nhất
tới điểm thấp nhất. Ở đây khoá sắp xếp chính là điểm thi
STT
1
2
3
4

SBD
A100
B200
X150
G180

Họ và Tên
Nguyễn Văn A
Trần Thị B

Phạm Văn C
Đỗ Thị D

Điểm Thi
20
25
18
21

Khi sắp xếp, các bản ghi trong bảng sẽ được đặt lại vào các vị trí sao cho giá
trị khoá tương ứng với chúng có đúng thứ tự đã ấn định. Vì kích thước của toàn
5


bản ghi có thể rất lớn, nên nếu việc sắp xếp thực hiện trực tiếp trên các bản ghi
sẽ đòi hỏi sự chuyển đổi vị trí của các bản ghi, kéo theo việc thường xuyên phải
di chuyển, copy những vùng nhớ lớn, gây ra những tổn phí thời gian khá nhiều.
Thường người ta khắc phục tình trạng này bằng cách xây dựng một bảng
khoá: Mỗi bản ghi trong bảng ban đầu sẽ tương ứng với một bản ghi trong bảng
khoá. Bảng khoá cũng gồm các bản ghi nhưng mỗi bản ghi chỉ gồm có hai
trường
Trường thứ nhất chứa khoá.
Trường thứ hai chứa liên kết tới một bản ghi trong bảng ban đầu, tức là chứa
thông tin đủ để biết bản ghi tương ứng với nó trong bảng ban đầu là bản ghi nào.
Sau đó, việc sắp xếp được thực hiện trực tiếp trên bảng khoá, trong quá
trình sắp xếp, bảng chính không hề bị ảnh hưởng gì, việc truy cập vào một bản
ghi nào đó của bảng chính vẫn có thể thực hiện được bằng cách dựa vào trường
liên kết của bản ghi tương ứng thuộc bảng khoá. Như ở ví dụ trên, ta có thể xây
dựng bảng khoá gồm 2 trường, trường khoá chứa điểm và trường liên kết chứa
số thứ tự của người có điểm tương ứng trong bảng ban đầu:


Điểm thi

STT

20

1

25
18

2
3

21

4

Sau khi sắp xếp theo trật tự điểm cao nhất tới điểm thấp nhất, bảng khoá sẽ trở
thành:
Điểm thi

STT

25

2
6



21
20

4
1

18

3

Dựa vào bảng khoá, ta có thể biết được rằng người có điểm cao nhất là người
mang số thứ tự 2, tiếp theo là người mang số thứ tự 4, tiếp nữa là người mang số
thứ tự 1, và cuối cùng là người mang số thứ tự 3, còn muốn liệt kê danh sách
đầy đủ thì ta chỉ việc đối chiếu với bảng ban đầu và liệt kê theo thứ tự 2, 4, 1, 3.
Có thể còn cải tiến tốt hơn dựa vào nhận xét sau: Trong bảng khoá, nội dung
của trường khoá hoàn toàn có thể suy ra được từ trường liên kết bằng cách: Dựa
vào trường liên kết, tìm tới bản ghi tương ứng trong bảng chính rồi truy xuất
trường khoá trong bảng chính. Như ví dụ trên thì người mang số thứ tự 1 chắc
chắn sẽ phải có điểm thi là 20, còn người mang số thứ tự 3 thì chắc chắn phải có
điểm thi là 18. Vậy thì bảng khoá có thể loại bỏ đi trường khoá mà chỉ giữ lại
trường liên kết. Trong trường hợp các phần tử trong bảng ban đầu được đánh số
từ 1 tới n và trường liên kết chính là số thứ tự của bản ghi trong bảng ban đầu
như ở ví dụ trên, người ta gọi kỹ thuật này là kỹ thuật sắp xếp bằng chỉ số: Bảng
ban đầu không hề bị ảnh hưởng gì cả, việc sắp xếp chỉ đơn thuần là đánh lại chỉ
số cho các bản ghi theo thứ tự sắp xếp.
Cụ thể hơn:
Nếu r[1..n] là các bản ghi cần sắp xếp theo một thứ tự nhất định thì việc sắp
xếp bằng chỉ số tức là xây dựng một dãy Index[1..n] mà ở đây:
Index[j] = Chỉ số của bản ghi sẽ đứng thứ j khi sắp thứ tự

(Bản ghi r[index[j]] sẽ phải đứng sau j - 1 bản ghi khác khi sắp xếp)
Do khoá có vai trò đặc biệt như vậy nên sau này, khi trình bày các giải thuật, ta
sẽ coi khoá như đại diện cho các bản ghi và để cho đơn giản, ta chỉ nói tới giá
trị của khoá mà thôi. Các thao tác trong kỹ thuật sắp xếp lẽ ra là tác động lên
toàn bản ghi giờ đây chỉ làm trên khoá. Còn việc cài đặt các phương pháp sắp
7


xếp trên danh sách các bản ghi và kỹ thuật sắp xếp bằng chỉ số, ta coi như bài
tập.
Bài toán sắp xếp giờ đây có thể phát biểu như sau:
Xét quan hệ thứ tự toàn phần "nhỏ hơn hoặc bằng" ký hiệu "≤" trên một tập hợp
S, là quan hệ hai ngôi thoả mãn bốn tính chất:
Với ∀a, b, c ∈ S
Tính phổ biến: Hoặc là a ≤ b, hoặc b ≤ a;
Tính phản xạ: a ≤ a
Tính phản đối xứng: Nếu a ≤ b và b ≤ a thì bắt buộc a = b.
Tính bắc cầu: Nếu có a ≤ b và b ≤ c thì a ≤ c.
Trong trường hợp a ≤ b và a ≠ b, ta dùng ký hiệu "<" cho gọn
Cho một dãy k[1..n] gồm n khoá. Giữa hai khoá bất kỳ có quan hệ thứ tự toàn
phần "≤". Xếp lại dãy các khoá đó để được dãy khoá thoả mãn k[1]≤ k[2] ≤
…≤ k[n].
Giả sử cấu trúc dữ liệu cho dãy khoá được mô tả như sau:
const
n =…; {Số khoá trong dãy khoá, có thể khai báo dưới dạng biến số nguyên
để tuỳ biến hơn}
type
TKey = …; {Kiểu dữ liệu một khoá}
TArray = array[1..n] of TKey;
var

k: TArray; {Dãy khoá}
Thì những thuật toán sắp xếp dưới đây được viết dưới dạng thủ tục sắp xếp dãy
khoá k, kiểu chỉ số đánh cho từng khoá trong dãy có thể coi là số nguyên
Integer.

8


1.2. TỔNG QUAN VỀ NGÔN NGỮ LẬP TRÌNH C
1.2.1. Sự phát triển của ngôn ngữ lập trình C
C là một ngôn ngữ lập trình tương đối nhỏ gọn vận hành gần với phần cứng
và nó giống với ngôn ngữ Assembler hơn hầu hết các ngôn ngữ bậc cao. Hơn
thế, C đôi khi được đánh giá như là "có khả năng di động", cho thấy sự khác
nhau quan trọng giữa nó với ngôn ngữ bậc thấp như là Assembler, đó là việc
mã C có thể được dịch và thi hành trong hầu hết các máy tính, hơn hẳn các
ngôn ngữ hiện tại trong khi đó thì Assembler chỉ có thể chạy trong một số máy
tính đặc biệt. Vì lý do này C được xem là ngôn ngữ bậc trung.
C đã được tạo ra với một mục tiêu là làm cho nó thuận tiện để viết các
chương trình lớn với số lỗi ít hơn trong mẫu hình lập trình thủ tục mà lại không
đặt gánh nặng lên vai người viết ra trình dịch C, là những người bề bộn với các
đặc tả phức tạp của ngôn ngữ. Cuối cùng C có thêm những chức năng sau:


Một ngôn ngữ cốt lõi đơn giản, với các chức năng quan trọng chẳng hạn
như là những hàm hay việc xử lý tập tin sẽ được cung cấp bởi các bộ thư
viện các thủ tục.



Tập trung trên mẫu hình lập trình thủ tục, với các phương tiện lập trình

theo kiểu cấu trúc.



Một hệ thống kiểu đơn giản nhằm loại bỏ nhiều phép toán không có ý
nghĩa thực dụng.



Dùng ngôn ngữ tiền xử lý, tức là các câu lệnh tiền xử lý C, cho các nhiệm
vụ như là định nghĩa các macro và hàm chứa nhiều tập tin mã
nguồn (bằng cách dùng câu lệnh tiền xử lý dạng #include chẳng hạn).



Mức thấp của ngôn ngữ cho phép dùng tới bộ nhớ máy tính qua việc sử
dụng kiểu dữ liệu pointer.



Số lượng từ khóa rất nhỏ gọn.



Các tham số được đưa vào các hàm bằng giá trị, không bằng địa chỉ.

9





Hàm các con trỏ cho phép hình thành một nền tảng ban đầu cho tính
đóng và tính đa hình.



Hỗ trợ các bản ghi hay các kiểu dữ liệu kết hợp do người dùng từ khóa
định nghĩa struct cho phép các dữ liệu liên hệ nhau có thể được tập hợp lại
và được điều chỉnh như là toàn bộ.

Một số chức năng khác mà C không có (hay còn thiếu) nhưng có thể tìm thấy
ở các ngôn ngữ khác bao gồm:


An toàn kiểu,



Tự động Thu dọn rác,



Các lớp hay các đối tượng cùng với các ứng xử của chúng (xem
thêm OOP)



Các hàm lồng nhau,




Lập trình tiêu bản hay Lập trình phổ dụng,



Quá tải và Quá tải toán tử,



Các hỗ trợ cho đa luồng, đa nhiệm và mạng.

Mặc dù C còn thiếu nhiều chức năng hữu ích nhưng lý do quan trọng để C
được chấp nhận vì nó cho phép các trình dịch mới được tạo ra một cách nhanh
chóng trên các nền tảng mới và vì nó cho phép người lập trình dễ kiểm soát
được những gì mà chương trình (do họ viết) thực thi. Đây là điểm thường làm
cho mã C chạy hiệu quả hơn các ngôn ngữ khác. Thường thì chỉ có ngôn
ngữ ASM chỉnh bằng tay chạy nhanh hơn (ngôn ngữ C), bởi vì ASM kiểm soát
được toàn bộ máy. Mặc dù vậy, với sự phát triển các trình dịch C, và với sự
phức tạp của các CPU hiện đại, C đã dần thu nhỏ khoảng cách khác biệt về vận
tốc này.
Một lý do nữa cho việc C được sử dụng rộng rãi và hiệu quả là do các trình
dịch, các thư viện và các phần mềm thông dịch của các ngôn ngữ bậc cao khác
lại thường được tạo nên từ C.

10


1.2.2. Cấu trúc chương trình C
Một chương trình bao gồm một hoặc nhiều hàm, mỗi hàm được người lập
trình tổ chức để giải quyết một hoặc một số công việc nào đó của bài toán cần

giải quyết. Một chương trình C để có thể thực thi được luôn cần phải có hàm
main().
• Cấu trúc cơ bản của chương trình như sau:
• Các #include ( dùng để khai báo sử dụng các hàm chuẩn)
• Các #define ( dùng để định nghĩa các hằng )
• Khai báo các đối tượng dữ liệu ngoài ( biến, mảng, cấu trúc vv..).
• Khai báo nguyên mẫu các hàm.
• Hàm main().
• Định nghĩa các hàm ( hàm main có thể đặt sau hoặc xen vào giữa các hàm
khác).
- Mỗi câu lệnh có thể viết trên một hay nhiều dòng nhưng phải kết thúc bằng
dấu (;).
- Trong chương trình, khi ta sử dụng các hàm chuẩn, ở đầu chương trình ta
phải khai báo sử dụng,
Ví dụ "hello, world".
Ví dụ đơn giản sau đây được thấy trong lần in đầu tiên của cuốn "The C
Programming Language", và đã trở thành bài tiêu chuẩn trong chương nhập
môn của hầu hết các loại sách giáo khoa về lập trình. Chương trình hiển thị câu
"hello, world!" trên đầu ra chuẩn, mà thường là một màn hình. Mặc dù vậy, nó
có thể xuất ra một tập tin hay xuất ra trên một thiết bị phần cứng kể cả trên
một vùng chứa, tùy thuộc vào việc đầu ra chuẩn được chỉ thị vào đâu khi
chương trình này được thực thi.

1 #include <stdio.h>

11


2 int main(void)
3{

4

printf("hello, world!");

5

return 0;

6}
Chương trình trên sẽ dịch đúng trong hầu hết các trình dịch hỗ trợ
chuẩn ANSI C hay chuẩn C99.
Sau đây là các phân tích theo từng dòng mã của ví dụ trên
1 #include <stdio.h>
Dòng đầu tiên này là một chỉ thị tiền xử lý #include. Điều này sẽ làm cho bộ
tiền xử lý (bộ tiền xử lý này là một công cụ để kiểm tra mã nguồn trước khi nó
được dịch) tiến hành thay dòng lệnh đó bởi toàn bộ các dòng mã hay thực thể
trong tập tin mà nó đề cập đến (tức là tập tin stdio.h). Dấu ngoặc nhọn bao
quanh stdio.h cho biết rằng tập tin này có thể tìm thấy trong các nơi đã định
trước cho bộ tiền xử lý biết thông qua các đường tìm kiếm đến các tập
tin header. Tập hợp các tập tin được khai báo sử dụng qua các chỉ thị tiền xử lý
còn được gọi là các tập tin bao gồm.
2 int main(void)
Dòng trên biểu thị một hàm chuẩn tên main. Hàm này có mục đích đặc biệt
trong C. Khi chương trình thi hành thì hàm main() được gọi trước tiên. Phần
mã int chỉ ra rằnggiá trị trả về của hàm main (tức là giá trị mà main() sẽ được
trả về sau khi thực thi) sẽ có kiểu là một số nguyên. Còn phần mã (void) cho biết
rằng hàm main sẽ không cần đến tham số để gọi nó. Xem thêm Void
Dấu '{' cho biết sự bắt đầu của định nghĩa của hàm main.
4


printf("hello, world\n");
12


Dòng trên gọi đến một hàm chuẩn khác tên là printf. Hàm này đã được khai
báo trước đó trong tập tin stdio.h. Dòng này sẽ cho phép tìm và thực thi mã (đã
được hỗ trợ sẵn) với ý nghĩa là hiển thị lên đầu ra chuẩn dòng chữ
hello, world<kí tự xuống dòng EOL-chuyển dấu nhắc sang dòng mới>.
Mã kí tự \n là một dãy thoát được chuyển dịch thành dấu kí tự EOL (viết tắt từ
chữ End-Of-Line) có nghĩa là chuyển vị trí dấu nhắc xuống đầu một dòng kế.
Gía trị trả về của hàm printf (theo khai báo nguyên mẫu chuẩn của hàm này
trong C) có kiểu int, nhưng vì giá trị trả về này không được (người lập trình)
dùng tới nên giá trị đó bị bỏ qua (một cách lặng lẽ).
5

return 0;

Dòng này sẽ kết thúc việc thực thi mã của hàm main và buộc nó trả về giá trị
0 (là một số nguyên như khai báo ban đầu int main ).
6}
Dấu '}' cho biết việc kết thúc mã cho hàm main.
1.2.3. Các kiểu dữ liệu và khai báo biến
C có một hệ thống kiểu tương tự như của Pascal, mặc dù chúng khác nhau
trong một số khía cạnh. Có nhiều kiểu cho các số nguyên với nhiều cỡ cho có
đấu và không có dấu, có kiểu số floating point, kiểu các kí tự char, các kiểu thứ
tự enum, kiểu bản ghi record và kiểu đơn vị union.
C tạo ra sự mở rộng mạnh mẽ việc sử dụng của kiểu các con trỏ pointer,
một dạng đơn giản các tham chiếu mà chúng chứa địa chỉ các vùng nhớ. Các
con trỏ có thể được tham chiếu ngược (dereference) để lấy về giá trị của dữ liệu
được chứa trong địa chỉ đó (địa chỉ mà con trỏ chỉ vào). Địa chỉ này có thể

được điều chỉnh bằng các phép gán thông thường và các phép toán số học trên
con trỏ. Trong thời gian thực thi, một con trỏ đại diện cho một địa chỉ của bộ
nhớ. Trong thời gian chuyển dịch, nó là một kiểu phức tạp đại diện cho cả địa
chỉ và kiểu của dữ liệu. Điều này cho phép các biểu thức bao gồm các con trỏ
13


được kiểm tra về kiểu. Các con trỏ thì được dùng cho nhiều mục tiêu trong C.
Các dòng kí tự string thường được đại diện bởi một con trỏ chỉ tới một dãy của
các kí tự. Sự cấp phát bộ nhớ động, được miêu tả sau đây, thì được tiến hành
thông qua các con trỏ.
Một con trỏ rỗng có nghĩa là nó không chỉ đến một chỗ nào hết. Điều này
có ích trong những trường hợp như là con trỏ next trong một nút cuối của
một danh sách liên kếtlinked list. Việc tham chiếu ngược một con trỏ trống gây
ra các biểu hiện không dự đoán trước được. Các con trỏ kiểu void thì lại có thể
chỉ đến một đối tượng mà không cần biết kiểu của đối tượng đó. Điều này đặc
biệt hữu dụng trong lập trình tiêu bản bởi vì cỡ và kiểu của các đối tượng mà
chúng chỉ tới thì không thể biết được và do đó không thể thực hiện tham chiếu
ngược, nhưng chúng lại có thể được hoán chuyển thành các con trỏ của các
kiểu khác. Các kiểu mảng array trong C thì có cỡ cố định, độ lớn tĩnh của nó
phải được biết trước trong thời gian chuyển dịch. Điều này gây nhiều trở ngại
trong thực tế bởi vì người ta có thể chỉ định các vùng nhớ ở thời gian thực thi
dựa trên các thư viện chuẩn và hành xử chúng như là các mảng. Không như các
ngôn ngữ khác, C biểu thị các mảng giống như trường hợp các con trỏ: chúng
đóng vai trò một địa chỉ của bộ nhớ và một kiểu dữ liệu. Do đó, các giá trị chỉ
số có thể vượt quá cỡ của một mảng.
C cũng cung cấp các kiểu mảng đa chiều. Các giá trị chỉ số của các mảng
đa chiều thì được gán theo thứ tự hàng chính. Một cách có ý nghĩa thì các
mảng này hoạt động như là mảng của các mảng nhưng thực chất chúng được
phân bố như là mảng một chiều với việc tính và tạo các vị trí tương đối.

C thường được dùng trong việc lập trình các hệ thống bậc thấp, ở đó có thể
cần thiết để xem số nguyên như là một địa chỉ của bộ nhớ, là một giá trị double
precision, hay là một kiểu con trỏ. Trong các trường hợp này, C cung cấp
việc hoán chuyển, mà phép toán này sẽ bắt buộc chuyển đổi giá trị từ một kiểu
sang một kiểu khác. Dùng phép hoán chuyểnsẽ làm mất đi phần nào tính an
toàn mà thường được cung cấp bởi hệ thống kiểu.

14


Các đoạn mã lệnh trong các mô-đun được viết bằng ngôn ngữ hệ thống có
cấu trúc (cú pháp) và các từ khoá riêng. Cú pháp ngôn ngữ lập trình hệ thống
có thể được viết bằng tiếng Nga hoặc tiếng Anh, ngoại trừ một vài từ khoá mà
không có tương đương trong tiếng Nga, ví dụ, Null.
Các toán tử của ngôn ngữ lập trình hệ thống được tách biệt với nhau bằng
dấu chấm phảy (;), nhưng sau từ khoá EndProcedrure hoặc EndFunction thì
không đặt dấu chấm phẩy, vì đây không phải là toán tử mà là khoảng giới hạn
đoạn lệnh.
Dưới đây là giới thiệu về cú pháp của ngôn ngữ lập trình hệ thống.
Điều kiện
Cú pháp “If .. Then…”
Dùng để kiểm tra biểu thức logic và hoàn toàn tương tự như các ngôn ngữ
lập trình khác.

15


CHƯƠNG 2
THUẬT TOÁN HEAP SORT
2.1. Ý TƯỞNG

HeapSort được đề xuất bởi J.W.J.Williams năm 1981, thuật toán không
những đóng góp một phương pháp sắp xếp hiệu quả mà còn xây dựng một
cấu trúc dữ liệu quan trọng để biểu diễn hàng đợi có độ ưu tiên: Cấu trúc dữ
liệu Heap.
Đầu tiên, dãy khoá k[1.. n] được vun từ dưới lên để nó biểu diễn một đống,
khi đó khoá k[1] tương ứng với nút gốc của đống là khoá lớn nhất, ta đảo giá
trị khoá đó cho k[n] và không tính tới k[n] nữa (hình dưới). Còn lại dãy khoá
k[1..n-1] tuy không còn là biểu diễn của một đống nữa nhưng nó lại biểu diễn
cây nhị phân hoàn chỉnh mà hai nhánh cây ở nút thứ 2 và nút thứ 3 (hai nút
con của nút 1) đã là đống rồi. Vậy chỉ cần vun một lần, ta lại được một
đống,đảo giá trị k[1] cho k[n-1] và tiếp tục cho tới khi đống chỉ còn lại 1 nút
(hình dưới).
Ví dụ: Đảo giá trị k[1] cho k[n] và xét phần còn lại:
1
0

2

8

3

9

4

7

5


8

6

2

1

7

3

9

4

5

6

1

1
0

16


-Vun phần còn lại thành đống rồi lại đảo trị k[1] cho k[n-1]
9


5

8

4

7

3

8

6

2

1

5

7

3

6

4

2


1

9

2.2. NỘI DUNG
2.2.1. Các tính chất của Heap
Giả sử xét trường hợp sắp xếp tăng dần, khi đó heap được định nghĩa là một
dãy các phần tử a p ,a 2 ,….a q thỏa mãn các quan hệ sau với mọi i ∈ [ p, q ]
a i ≥ a 2i
a i ≥ a 2i +1 {( a i ,a 2i ),( a i ,a 2i +1 ) là các cặp phần tử liên đới}

Tính chất 1: Nếu a p ,a 2 ,….a q là một heap thì khi cắt bỏ một số phần tử ở
hai đầu của heap, dãy còn lại vẫn là một heap.
Tính chất 2: Nếu a p ,a 2 ,….a q là một heap thì phần tử a 1 (đầu heap) luôn là
phần tử lớn nhất trong heap.
Tính chất 3: Mọi dãy a p ,a 2 ,….a q dãy con aj, aj+1,…, a r tạo thành một heap
với j=(q div 2 +1).

17


2.2.2. Giải thuật Heap Sort
Giải thuật Heapsort trải qua 2 giai đoạn
- Giai đoạn 1: Hiệu chỉnh dãy số ban đầu thành heap
- Giai đoạn 2: Sắp xếp dãy số dựa trên heap
Bước 1: Ðưa phần tử lớn nhất về vị trí đúng ở cuối dãy
r = n; Hoánvị (a p , a q )
Bước 2: Loại bỏ phần tử lớn nhất ra khỏi heap: r = r-1;
Hiệu chỉnh phần còn lại của dãy từ a p , a2 ... a q thành một

heap
Bước 3: Nếu r > 1 (heap còn phần tử ): Lặp lại Bước 2
Ngược lại: Dừng
Dựa trên tính chất 3, ta có thể thực hiện giai đoạn 1 bằng cách bắt đầu từ
heap mặc nhiên an/2+1, an/2+2 ... an, sau đó thêm dần các phần tử an/2,
an/2-1, …, a1 ta sẽ nhận được heap theo mong muốn.
2.2.3. Ðánh giá giải thuật
Trong giai đoạn sắp xếp ta cần thực hiện n-1 bước mỗi bước cần nhiều
nhất là log2(n-1), log2(n-2), … 1 phép đổi chỗi.
Như vậy độ phức tạp thuật toán Heap sort O(nlog2n)
2.3. THỦ TỤC HEAP SORT
- Thủ tục Adjust (root, endnode) vun cây gốc root thành đống trong điều kiện
hai cây gốc 2.root và 2.root + 1 đã là đống rồi. Các nút từ endnode + 1 tới n
đã nằm ở vị trí đúng và không được tính tới nữa.
- Thủ tục HeapSort mô tả lại quá trình vun đống và chọn khoá theo ý tưởng
trên
2.4. VÍ DỤ
Cho dãy số a
18


12

2

8

5

1


6

4

15

Giai đoạn 1: hiệu chỉnh dãy ban đầu thành heap

1
2

2

8

5

1

6

I=
4

1
2

2


8

1
5

2

8

I=
2

1
2

1
5

1

I=
1

1

1

6

4


5

Phần tử liên
đới

15

1

6

4

6

4

5

Phần tử liên
đới

8

2

1

5


Lan truyền việc hiểu
chỉnh

I=
1

1
2

15

Phần tử liên
đới

I=
3

1
2

4

8

5

1

6


4

5

1

6

4

2

Phần tử liên
đới

1

8

2
19


Giai đoạn 2: Sắp xếp dãy số dựa trên heap

1
5

1

2

8

5

1

6

4

2
r
=8

Hiệu chỉnh lại heap
1
2

5

8

2

1

6


4

15

1
2

5

8

2

1

6

4

15

r
=7

Hiệu chỉnh lại heap
8

5

6


2

1

4

12

15

8

5

6

2

1

4

12

15

r=
6


Hiệu chỉnh lại heap
6

5

4

2

1

8

12

15

6

5

4

2

1

8

12


15

12

15

r=
5

Thực hiện tương tự cho r = 4, 3, 2 ta được
1

2

4

5

6

8

20


2.5 LƯU ĐỒ THUẬT TOÁN
Xem như dãy số cần sắp xếp đã được nhập vào sẵn.
*Hiệu chỉnh Heap*
Begin

i = 1; j = 2*i; x = a[i]; cont = true;
Sai
j <= r &
Cont

Đúng
Sai

jĐúng
Sai
a[j] <
a[j+1]

Đúng

j = j+1
Sai
a[j] <
x

Đúng

Cont = false

a[i] = a[j] ;
i=j;
j = 2*j ;

a[i] = x

End

21


*Sắp xếp vun đống*

Begin

Hiệu chỉnh dãy a 1
,a 2 …a n thành
Heap; r = n
Sai
r >
0

Đúng

Đổi chỗ a[l] và a[r]
r = r – 1;
Hiệu chỉnh dãy a 1 , a 1+1 ,…,a r
thành Heap;
Xuất
Dãy số

22


2.6. ĐỘ PHỨC TẠP CỦA THUẬT TOÁN
Về độ phức tạp của thuật toán, ta đã biết rằng cây nhị phân hoàn chỉnh có n

nút thì chiều cao của nó là [lg(n)] + 1. Cứ cho là trong trường hợp xấu nhất thủ
tục Adjust phải thực hiện tìm đường đi từ nút gốc tới nút lá ở xa nhất thì đường đi
tìm được cũng chỉ dài bằng chiều cao của cây nên thời gian thực hiện một lần gọi
Adjust là O(lgn). Từ đó có thể suy ra, trong trường hợp xấu nhất, độ phức tạp của
HeapSort cũng chỉ là O(nlgn). Việc đánh giá thời gian thực hiện trung bình phức
tạp hơn, ta chỉ ghi nhận một kết quả đã chứng minh được là độ phức tạp trung
bình của HeapSort cũng là O(nlgn).

23


CHƯƠNG 3
ỨNG DỤNG VÀ CHƯƠNG TRÌNH
3.1. BÀI TOÁN NGƯỜI BÁN HÀNG

3.1.1. Bài toán
Một người bán hàng xuất phát từ thành phố 1, muốn đi qua tất cả các thành
phố còn lại mỗi thành phố đúng 1 lần và cuối cùng quay lại thành phố 1. Hãy chỉ
ra cho người đó hành trình với chi phí ít nhất. Bài toán đó gọi là bài toán người
bán hàng hay bài toán hành trình của một thương gia (Traveling Salesman)
Trong khoa học máy tính, đây là một vấn đề rất cơ bản và ứng dụng của nó thì
nhiều vô kể. Một ứng dụng rất điển hình là hoạt động hàng ngày của các công ty
chuyển phát. Công việc của họ là phải phân phát và tiếp nhận hàng hóa sao cho
ít tốn thời gian và chi phí nhiên liệu nhất trong khi vẫn đảm bảo không bỏ sót
bất cứ điểm đến nào. Ngoài ra, vấn đề trên còn được các nhà sản xuất điện tử áp
dụng để tạo ra những con chip siêu nhỏ tốt hơn hay được các nhà di truyền học
sử dụng vào chuỗi DNA.
3.1.2. Phân tích
Đây là bài toán tìm chu trình có trọng số nhỏ nhất trong một đơn đồ thị có
hướng có trọng số. Thuật toán tham lam cho bài toán là chọn thành phố có chi

phí nhỏ nhất tính từ thành phố hiện thời đến các thành phố chưa qua
Input C= (Cij)
output TOUR // Hành trình tối ưu,
Mô tả :
COST://Chi phí tương ứng
TOUR := 0; COST := 0; v := u; // Khởi tạo
Mọi k := 1 -> n ://Thăm tất cả các thành phố
// Chọn cạnh kề )
- Chọn <v, w> là đoạn nối 2 thành phố có chi phí nhỏ nhất tính từ thành phố v
đến các thành phố chưa qua.
24


- TOUR := TOUR + <v, w>; //Cập nhật lời giải
- COST := COST + Cvw ; //Cập nhật chi phí
// Chuyến đi hoàn thành TOUR := TOUR + <v, u>; COST := COST + Cvw
Minh họa:

25


×