Tải bản đầy đủ (.pdf) (194 trang)

Bài giảng cấu trúc dữ liệu và giải thuật

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 (2.27 MB, 194 trang )

-p[o0pppppp744444444444444444444/

ĐẠI HỌC CÔNG NGHỆ GIAO THÔNG VẬN TẢI

CẤU TRÚC DỮ LIỆU VÀ
GIẢI THUẬT
(Lưu hành nội bộ)
Chủ biên: ThS. Hoàng Thế Phương

Hà Nội, 2019


MỤC LỤC
Chương 1: Các khái niệm cơ bản ..................................................................................... 4
1.1. Các thành phần cơ bản của ngơn ngữ lập trình C ............................................... 4
1.1.1. Tập ký tự ............................................................................................................. 4
1.1.2. Từ khóa ............................................................................................................... 4
1.1.3. Tên ...................................................................................................................... 5
1.1.4. Kiểu dữ liệu ........................................................................................................ 6
1.1.5. Hằng .................................................................................................................... 9
1.1.6. Biến: .................................................................................................................. 15
1.2. Các khái niệm cơ bản về giải thuật ..................................................................... 16
1.2.1. Khái niệm về giải thuật và cấu trúc dữ liệu ...................................................... 16
1.2.2. Cấu trúc dữ liệu và các vấn đề liên quan .......................................................... 17
1.2.3. Diễn đạt giải thuật ............................................................................................. 19
1.3. Phân tích và thiết kế giải thuật ............................................................................ 24
1.3.1. Từ bài toán đến chương trình ............................................................................ 24
1.3.2. Phân tích, thiết kế giải thuật ............................................................................. 30
Chương 2. Các thành phần cơ bản và cấu trúc điều khiển chương trình .................. 36
2.1. Các lệnh vào ra dữ liệu ......................................................................................... 36
2.1.1. Các hàm vào ra chuẩn ....................................................................................... 36


2.1.2. Đưa kết quả lên màn hình ................................................................................. 38
2.1.3. Vào dữ liệu từ bàn phím ................................................................................... 43
2.2. Biểu thức ................................................................................................................ 48
2.2.1. Khái niệm .......................................................................................................... 48
2.2.2. Lệnh gán và biểu thức: ...................................................................................... 48
2.2.3. Các phép toán.................................................................................................... 49
2.2.4. Chuyển đổi kiểu giá trị : ................................................................................... 55
2.3. Cấu trúc cơ bản của chương trình....................................................................... 58
2.3.1. Lời chú thích ..................................................................................................... 58
2.3.2. Lệnh và khối lệnh : ........................................................................................... 59
2.3.3. Lưu đồ thuật toán .............................................................................................. 62
1


2.3.4. Cấu trúc cơ bản của chương trình: .................................................................... 64
2.3.5. Quy tắc khi viết chương trình ........................................................................... 66
2.4. Cấu trúc điều kiện if ............................................................................................. 67
2.4.1. Lệnh if-else : ..................................................................................................... 67
2.4.2. Lệnh else-if : .................................................................................................... 70
2.5. Cấu trúc rẽ nhánh switch…case .......................................................................... 72
2.6. Cấu trúc lặp for : ...................................................................................................

76

2.7. Cấu trúc lặp while ................................................................................................. 81
2.7.1. Cấu trúc while ................................................................................................... 81
2.7.2. Cấu trúc do-while.............................................................................................. 84
2.8. Câu lệnh nhảy ........................................................................................................

86


2.8.1. Lệnh nhảy không điều kiện - toán tử goto: ....................................................... 86
2.8.2. Câu lệnh break: ................................................................................................. 88
2.8.3. Câu lệnh continue ............................................................................................. 89
Chương 3. Hàm và con trỏ ..............................................................................................

92

3.1. Hàm ........................................................................................................................ 92
3.1.1. Khái niệm, khai báo hàm .................................................................................. 92
3.1.2. Cách tổ chức hàm ............................................................................................. 92
3.1.3. Cách truyền tham số khi gọi hàm ..................................................................... 96
3.2. Con trỏ .................................................................................................................. 105
3.2.1. Con trỏ và địa chỉ ............................................................................................ 105
3.2.2. Con trỏ và mảng một chiều ............................................................................. 108
3.2.3. Con trỏ và mảng nhiều chiều .......................................................................... 114
3.2.4. Các phép toán trên con trỏ .............................................................................. 117
3.2.5. Mảng con trỏ ................................................................................................... 120
3.2.6. Con trỏ tới hàm ............................................................................................... 123
Chương 4. Cấu trúc dữ liệu .......................................................................................... 128
4.1. Mảng và danh sách .............................................................................................. 128
4.1.1. Các khái niệm ................................................................................................. 128
4.1.2. Cấu trúc lưu trữ mảng ..................................................................................... 130
2


4.1.3. Danh sách tuyến tính............................................................................................................... 132
4.2. Ngăn xếp .............................................................................................................................................. 139
4.2.1. Định nghĩa ngăn xếp ............................................................................................................... 139
4.2.2. Lưu trữ ngăn xếp....................................................................................................................... 139

4.2.3. Ứng dụng của ngăn xếp ......................................................................................................... 144
4.3. Hàng đợi .............................................................................................................................................. 146
4.3.1. Định nghĩa hàng đợi ................................................................................................................ 146
4.3.2. Lưu trữ hàng đợi ....................................................................................................................... 147
4.4. Cây .......................................................................................................................................................... 152
4.4.1. Các khái niệm............................................................................................................................. 152
4.4.2. Cây nhị phân ............................................................................................................................... 153
4.4.3. Cây tổng quát ............................................................................................................................. 157
4.5. Đồ thị ..................................................................................................................................................... 162
4.5.1. Các khái niệm............................................................................................................................. 162
4.5.2. Biểu diễn đồ thị ......................................................................................................................... 164
4.5.3. Phép duyệt một đồ thị ............................................................................................................. 167
4.5.4. Áp dụng ........................................................................................................................................ 171
Chương 5. Giải thuật sắp xếp và tìm kiếm .................................................................................... 173
5.1. Sắp xếp ................................................................................................................................................. 173
5.1.1. Đặt vấn đề .................................................................................................................................... 173
5.1.2. Sắp xếp chọn trực tiếp ............................................................................................................ 173
5.1.3. Sắp xếp chèn trực tiếp ............................................................................................................ 176
5.1.4. Sắp xếp đổi chỗ trực tiếp....................................................................................................... 180
5.1.5. Sắp xếp trộn ................................................................................................................................ 184
5.2. Tìm kiếm ............................................................................................................................................. 187
5.2.1. Bài tốn tìm kiếm ..................................................................................................................... 187
5.2.2. Tìm kiếm tuần tự ...................................................................................................................... 188
5.2.3. Tìm kiếm nhị phân ................................................................................................................... 191

3


Chương 1: Các khái niệm cơ bản
1.1. Các thành phần cơ bản của ngơn ngữ lập trình C


1.1.1. Tập ký tự
Mọi ngơn ngữ lập trình đều được xây dựng từ một bộ ký tự nào đó. Các ký tự
được nhóm lại theo nhiều cách khác nhau để tạo nên các từ. Các từ lại được liên kết với
nhau theo một qui tắc nào đó để tạo nên các câu lệnh. Một chương trình bao gồm nhiều
câu lệnh và thể hiện một thuật tốn để giải một bài tốn nào đó. Ngôn ngữ C được xây
dựng trên bộ ký tự sau :
26 chữ cái hoa : A B C .. Z
26 chữ cái thường : a b c .. z
10 chữ số : 0 1 2 .. 9
Các ký hiệu toán học : + - * / = ( )
Ký tự gạch nối : _
Các ký tự khác : . , : ; [ ] {} ! \ & % # $ ...
Dấu cách (space) dùng để tách các từ. Ví dụ chữ VIET NAM có 8 ký tự, cịn
VIETNAM chỉ có 7 ký tự.
Chú ý :
Khi viết chương trình, ta không được sử dụng bất kỳ ký tự nào khác ngồi các ký
tự trên.
Ví dụ như khi lập chương trình giải phương trình bậc hai ax2 +bx+c=0 , ta cần tính
biệt thức Delta= b2 - 4ac, trong ngơn ngữ C khơng cho phép dùng ký tự, vì vậy ta
phải dùng ký hiệu khác để thay thế.
1.1.2. Từ khóa

Từ khố là những từ được sử dụng để khai báo các kiểu dữ liệu, để viết các

toán tử và các câu lệnh. Bảng dưới đây liệt kê các từ khoá của TURBO C :
asm

break


case

cdecl

char

const

continue

default

do

double

else

enum

extern

far

float

for

4



goto

huge

if

int

interrupt

long

near

pascal

register

return

short

signed

sizeof

static

struct


switch

typedef

union

unsigned

void

volatile

while

Ý nghĩa và cách sử dụng của mỗi từ khoá sẽ được đề cập sau này, ở đây ta
cần chú ý :
- Khơng được dùng các từ khố để đặt tên cho các hằng, biến, mảng, hàm ...
- Từ khoá phải được viết bằng chữ thường, ví dụ : viết từ khố khai
báo kiểu ngun là int chứ khơng phải là INT.

1.1.3. Tên

Tên là một khái niệm rất quan trọng, nó dùng để xác định các đại lượng khác

nhau trong một chương trình. Chúng ta có tên hằng, tên biến, tên mảng, tên hàm,
tên con trỏ, tên tệp, tên cấu trúc, tên nhãn,...
Tên được đặt theo qui tắc sau :
Tên là một dãy các ký tự bao gồm chữ cái, số và gạch nối. Ký tự đầu tiên
của tên phải là chữ hoặc gạch nối. Tên không được trùng với khoá. Độ dài cực đại

của tên theo mặc định là 32 và có thể được đặt lại là một trong các giá trị từ 1 tới
32 nhờ chức năng : Option-Compiler-Source-Identifier length khi dùng TURBO C.
Ví dụ :
Các tên đúng :
a_1

delta x1
5

_step GAMA


Các tên sai

:

3MN

Ký tự đầu tiên là số

m#2

Sử dụng ký tự #

f(x)

Sử dụng các dấu ( )

do


Trùng với từ khoá

te ta

Sử dụng dấu trắng

Y-3

Sử dụng dấu -

Chú ý :
Trong TURBO C, tên bằng chữ thường và chữ hoa là khác nhau ví dụ
tên AB khác với ab. trong C, ta thường dùng chữ hoa để đặt tên cho các hằng và
dùng chữ thường để đặt tên cho hầu hết cho các đại lượng khác như biến, biến
mảng, hàm, cấu trúc. Tuy nhiên đây không phải là điều bắt buộc.
1.1.4. Kiểu dữ liệu

Trong C sử dụng các các kiểu dữ liệu sau :

a. Kiểu ký tự (char) :

Một giá trị kiểu char chiếm 1 byte ( 8 bit ) và biểu diễn được một ký tự
thơng qua bảng mã ASCII. Ví dụ :
Ký tự

Mã ASCII

0

048


1

049

6


2

050

A

065

B

066

a

097

b

098

Có hai kiểu dữ liệu char : kiểu signed char và unsigned char.
Kiểu


Phạm vi biểu diễn

Số ký tự

Kích
thước

char (Signed char)

-128 đến 127

256

1 byte

0 đến 255

256

1 byte

unsigned char

Ví dụ sau minh hoạ sự khác nhau giữa hai kiểu dữ liệu trên : Xét đoạn chương
trình sau :
char ch1;
unsigned char ch2;
......
ch1=200; ch2=200;

Khi đó thực chất :
ch1=-56;
7


ch2=200;
Nhưng cả ch1 và ch2 đều biểu diễn cùng một ký tự có mã 200.
Phân loại ký tự :
Có thể chia 256 ký tự làm ba nhóm :
Nhóm 1: Nhóm các ký tự điều khiển có mã từ 0 đến 31. Chẳng hạn ký tự mã
13 dùng để chuyển con trỏ về đầu dòng, ký tự 10 chuyển con trỏ xuống dòng dưới
( trên cùng một cột ). Các ký tự nhóm này nói chung khơng hiển thị ra màn hình.
Nhóm 2 : Nhóm các ký tự văn bản có mã từ 32 đến 126. Các ký tự này có
thể được đưa ra màn hình hoặc máy in.
Nhóm 3 : Nhóm các ký tự đồ hoạ có mã số từ 127 đến 255. Các ký tự này có
thể đưa ra màn hình nhưng khơng in ra được ( bằng các lệnh DOS ).
b. Kiểu nguyên :

Trong C cho phép sử dụng số nguyên kiểu int, số nguyên dài kiểu long và số
ngun khơng dấu kiểu unsigned. Kích cỡ và phạm vi biểu diễn của chúng được
chỉ ra trong bảng dưới đây :
Kiểu

Phạm vi biểu diễn

Kích
thước

int
unsigned int

long

-32768 đến 32767

2 byte

0 đến 65535

2 byte

-2147483648 đến

4 byte

2147483647
unsigned long

0 đến 4294967295
8

4 byte


Chú ý :
Kiểu ký tự cũng có thể xem là một dạng của kiểu nguyên.
c. Kiểu dấu phảy động :

Trong C cho phép sử dụng ba loại dữ liệu dấu phảy động, đó là float, double
và long double. Kích cỡ và phạm vi biểu diễn của chúng được chỉ ra trong bảng
dưới đây :

Kiểu

Phạm vi biểu diễn

Số chữ số

Kích thước

có nghĩa
Float
Double

3.4E-38 đến 3.4E+38

7 đến 8

4 byte

1.7E-308 đến

15 đến 16

8 byte

17 đến 18

10 byte

1.7E+308
long double


3.4E-4932 đến
1.1E4932

Giải thích :
Máy tính có thể lưu trữ được các số kiểu float có giá trị tuyệt đối từ 3.4E-38
đến 3.4E+38. Các số có giá trị tuyệt đối nhỏ hơn3.4E-38 được xem bằng 0. Phạm
vi biểu diễn của số double được hiểu theo nghĩa tương tự.
1.1.5. Hằng

Hằng là các đại lượng mà giá trị của nó khơng thay đổi trong q trình tính

tốn.

9


a. Tên hằng :

Nguyên tắc đặt tên hằng ta đã xem xét trong mục 1.3.
Để đặt tên một hằng, ta dùng dịng lệnh sau :
#define tên hằng giá trị
Ví dụ :
#define MAX 1000
Lúc này, tất cả các tên MAX trong chương trình xuất hiện sau này đều được
thay bằng 1000. Vì vậy, ta thường gọi MAX là tên hằng, nó biểu diễn số 1000.
Một ví dụ khác :
#define pi 3.141593
Đặt tên cho một hằng float là pi có giá trị là 3.141593.
b. Các loại hằng :


- Hằng int :
Hằng int là số nguyên có giá trị trong khoảng từ -32768 đến 32767.
Ví dụ :
#define number1 -50

Định nghiã hằng int number1 có giá trị là -50

#define sodem

Định nghiã hằng int sodem có giá trị là 2732

2732

Chú ý :
Cần phân biệt hai hằng 5056 và 5056.0 : ở đây 5056 là số nguyên còn
5056.0 là hằng thực.

10


- Hằng long :
Hằng long là số nguyên có giá trị trong khoảng từ -2147483648 đến
2147483647.
Hằng long được viết theo cách :
1234L hoặc 1234l
( thêm L hoặc l vào đuôi )
Một số nguyên vượt ra ngoài miền xác định của int cũng được xem là long.
Ví dụ :
#define sl 8865056L


Định nghiã hằng long sl có giá trị là 8865056

#define sl 8865056

Định nghiã hằng long sl có giá trị là 8865056

- Hằng int hệ 8 :
Hằng int hệ 8 được viết theo cách

0c1c2c3....ở đây ci là một số nguyên

dương trong khoảng từ 1 đến 7. Hằng int hệ 8 luôn luôn nhận giá trị dương.
Ví dụ :
#define h8 0345

Định nghiã hằng int hệ 8 có giá trị là
3*8*8+4*8+5=229

- Hằng int hệ 16 :
Trong hệ này ta sử dụng 16 ký tự : 0,1..,9,A,B,C,D,E,F.

11


Cách viết

Giá trị

a hoặc A


10

b hoặc B

11

c hoặc C

12

d hoặc D

13

e hoặc E

14

f hoặc F

15

Hằng số hệ 16 có dạng 0xc1c2c3... hặc 0Xc1c2c3... ở đây ci là một số trong
hệ 16.
Ví dụ :
#define h16 0xa5
#define h16 0xA5
#define h16 0Xa5
#define h16 0XA5

Cho ta các hắng số h16 trong hệ 16 có giá trị như nhau. Giá trị của chúng trong hệ
10 là :
10*16+5=165.
- Hằng ký tự :
Hằng ký tự là một ký tự riêng biệt được viết trong hai dấu nháy đơn, ví dụ
'a'.
12


Giá trị của 'a' chính là mã ASCII của chữ a. Như vậy giá trị của 'a' là 97. Hằng ký
tự có thể tham gia vào các phép tốn như mọi số nguyên khác. Ví dụ :
'9'-'0'=57-48=9
Ví dụ :
#define kt 'a'

Định nghiã hằng ký tự kt có giá trị là 97

Hằng ký tự cịn có thể được viết theo cách sau :
' \c1c2c3'
trong đó c1c2c3 là một số hệ 8 mà giá trị của nó bằng mã ASCII của ký tự cần biểu
diễn.
Ví dụ: chữ a có mã hệ 10 là 97, đổi ra hệ 8 là 0141. Vậy hằng ký tự 'a' có thể
viết dưới dạng '\141'. Đối với một vài hằng ký tự đặc biệt ta cần sử dụng cách viết
sau ( thêm dấu \ ) :
Cách viết

Ký tự

'\''


'

'\"'

"

'\\'

\

'\n'

\n (chuyển dòng )

'\0'

\0 ( null )

'\t'

Tab

'\b'

Backspace
13


'\r'


CR ( về đầu dòng )

'\f'

LF ( sang trang )

Chú ý :
Cần phân biệt hằng ký tự '0' và '\0'. Hằng '0' ứng với chữ số 0 có mã ASCII
là 48,
còn hằng '\0' ứng với kýtự \0 ( thường gọi là ký tự null ) có mã ASCII là 0.
Hằng ký tự thực sự là một số nguyên, vì vậy có thể dùng các số nguyên hệ
10 để biểu diễn các ký tự, ví dụ lệnh printf("%c%c",65,66) sẽ in ra AB.
-

Hằng xâu ký tự :
Hằng xâu ký tự là một dãy ký tự bất kỳ đặt trong hai dấu nháy kép.

Ví dụ :
#define xau1 "Ha noi"
#define xau2 "My name is Giang"
Xâu ký tự được lưu trữ trong máy dưới dạng một bảng có các phần tử là các
ký tự riêng biệt. Trình biên dịch tự động thêm ký tự null \0 vào cuối mỗi xâu ( ký
tự \0 được xem là dấu hiệu kết thúc của một xâu ký tự ).
Chú ý :
Cần phân biệt hai hằng 'a' và "a". 'a' là hằng ký tự được lưu trữ trong 1 byte,
còn "a" là hằng xâu ký tự được lưu trữ trong 1 mảng hai phần tử : phần tử thứ nhất
chứa chữ a còn phần tử thứ hai chứa \0.

14



1.1.6. Biến:

Mỗi biến cần phải được khai báo trước khi đưa vào sử dụng. Việc khai báo

biến được thực hiện theo mẫu sau :
Kiểu dữ liệu của biến tên biến ;
Ví dụ :
int a,b,c;

Khai báo ba biến int là a,b,c

long dai,mn;

Khai báo hai biến long là dai và mn

char kt1,kt2;

Khai báo hai biến ký tự là kt1 và kt2

float x,y

Khai báo hai biến float là x và y

double canh1, canh2;

Khai báo hai biến double là canh1 và canh2

Biến kiểu int chỉ nhận được các giá trị kiểu int. Các biến khác cũng có ý
nghĩa tương tự. Các biến kiểu char chỉ chứa được một ký tự. Để lưu trữ được một

xâu ký tự cần sử dụng một mảng kiểu char.
Vị trí của khai báo biến :
Các khai báo cần phải được đặt ngay sau dấu { đầu tiên của thân hàm và cần
đứng trước mọi câu lệnh khác. Sau đây là một ví dụ về khai báo biến sai :
( Khái niệm về hàm và cấu trúc chương trình sẽ nghiên cứu sau này)
main()
{
int a,b,c;
a=2;
int d; /* Vị trí của khai báo sai */
15


.....
}
Khởi đầu cho biến :
Nếu trong khai báo ngay sau tên biến ta đặt dấu = và một giá trị nào đó thì
đây chính là cách vừa khai báo vừa khởi đầu cho biến.
Ví dụ :
int a,b=20,c,d=40;
float e=-55.2,x=27.23,y,z,t=18.98;
Việc khởi đầu và việc khai báo biến rồi gán giá trị cho nó sau này là hồn tồn
tương đương.
Lấy địa chỉ của biến :
Mỗi biến được cấp phát một vùng nhớ gồm một số byte liên tiếp. Số hiệu
của byte đầu chính là địa chỉ của biến. Địa chỉ của biến sẽ được sử dụng trong một
số hàm ta sẽ nghiên cứu sau này ( ví dụ như hàm scanf ).
Để lấy địa chỉ của một biến ta sử dụng phép toán :
& tên biến
1.2. Các khái niệm cơ bản về giải thuật

1.2.1. Khái niệm về giải thuật và cấu trúc dữ liệu
Có thể, có lúc, khi nói tới việc giải quyết bài tốn trên máy tính điện tử, người ta
chỉ chú ý đến giải thuật (algorithms). Đó là một dãy các câu lệnh (statements) chặt chẽ và
rõ ràng xác định một trình tự các thao tác trên một số đối tượng nào đó sao cho sau một
số hữu hạn bước thực hiện ta đạt được kết quả mong muốn.
Nhưng, xét cho cùng, giải thuật chỉ phản ánh các phép xử lý, cịn đối tượng để xử lý
trên máy tính điện tử, chính 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ữ kiện đưa vào, các kết quả trung gian… Khơng thể nói tới giải thuật

16


mà khơng nghĩ tới: giải thuật đó được tác động trên dữ liệu nào, còn khi xét tới dữ liệu thì
cũng phải hiểu: dữ liệu ấy cần được tác động giải thuật gì để đưa tới kết quả mong muốn.
Bản thân các phần tử của dữ liệu thường có mối quan hệ với nhau, ngoài ra nếu lại biết
“tổ chức” theo các cấu trúc thích hợp thì việc thực hiện các phép xử lý trên các dữ liệu sẽ
càng thuận lợi hơn, đạt hiệu quả cao hơn. Với một cấu trúc dữ liệu đã chọn ta sẽ có giải
thuật xử lý tương ứng. Cấu trúc dữ liệu thay đổi, giải thuật cũng thay đổi theo. Ta sẽ thấy
rõ điều đó qua ví dụ sau: Giả sử ta có một danh sách gồm những cặp “Tên đơn vị, số điện
thoại”: (a1, b1), (a2, b2),…,(an, bn).
Ta muốn viết một chương trình cho máy tính điện tử để khi cho biết “tên đơn vị”
máy sẽ in cho ta “số điện thoại”. Đó là một bài toán mà phép xử lý cơ bản là “tìm kiếm”
- Một cách đơn giản là cứ điểm lần lượt các tên trong danh sách a1, a2, .v.v.. cho
tới lúc tìm thấy tên đơn vị ai nào đó, đã chỉ định, thì đối chiếu ra số điện thoại tương ứng
bi của nó. Nhưng việc đó chỉ làm được khi danh mục điện thoại ngắn, nghĩa là với n nhỏ,
còn với n lớn thì rất mất thời gian.
- Nếu trước đó danh mục điện thoại đã được sắp xếp theo thứ tự từ điển đối với
tên đơn vị, tất nhiên sẽ áp dụng một giải thuật tìm kiếm khác tốt hơn, như ta vẫn thường
làm khi tra từ điển.
- Nếu tổ chức thêm một bảng mục lục chỉ dẫn theo chữ cái đầu tiên của “tên đơn

vị”, chắc rằng khi tìm số điện thoại của Đại Học Bách Khoa ta sẽ bỏ qua được các tên
đơn vị mà chữ đầu không phải là chữ Đ.
Như vậy: giữa cấu trúc dữ liệu và giải thuật có mối quan hệ mật thiết, có thể coi
chúng như hình với bong. Khơng thể nói tới cái này mà khơng nhắc tới cái kia.
Chính điều đó đã dẫn tới việc cần nghiên cứu các cấu trúc dữ liệu (data structures)
đi đôi với việc xác lập các giải thuật xử lý trên các cấu trúc ấy.
1.2.2. Cấu trúc dữ liệu và các vấn đề liên
quan a) Dữ liệu nguyên tử
Trong một bài toán, dữ liệu bao gồm một tập cá phần tử cơ sở, mà ta gọi là dữ
liệu nguyên tử (atoms). Nó có thể là một chữ số, một kí tự… nhưng cũng có thể là một
con số, hay một từ,… điều đó tùy thuộc vào từng bài toán.
Trên cơ sở của các dữ liệu nguyên tử, các cung cách (manners) khả dĩ theo đó liên
kết chúng lại với nhau, sẽ dẫn tới các cấu trúc dữ liệu khác nhau.

17


Lựa chọn một cấu trúc dữ liệu thích hợp để tổ chức dữ liệu vào và trên cơ sở đó
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 tốn, đó là
một khâu rất quan trọng.
Cần chú ý rằng, trong những năm gần đây, lớp các khái niệm về cấu trúc dữ liệu
đã tăng lên đáng kể. Thoạt đầu, khi ứng dụng của máy tính điện tử chỉ mới có trong phạm
vi các bài tốn khoa học kỹ thuật thì ta chỉ gặp các cấu trúc dữ liệu đơn giản như biến,
vecto, ma trận ..v.v.. nhưng khi các ứng dụng đó đã mở rộng sang các lĩnh vực khác mà ta
thường gọi là các bài toán phi số, với đặc điểm thể hiện ở chỗ: khối lượng dữ liệu lớn, đa
dạng, biến động; phép xử lý thường không phải chỉ là các phép số học… thì các cấu trúc
này khơng đủ đặc trưng cho các mối quan hệ mới của dữ liệu nữa. Việc đi sâu thêm vào các
cấu trúc dữ liệu phức tạp hơn, chính là sự quan tâm của ta trong giáo trình này.
b) Mối quan hệ giữa cấu trúc dữ liệu và giải thuật
Đối với các bài toán phi số đi đôi với các cấu trúc dữ liệu mới cũng xuất hiện

các phép toán mới tác động trên các cấu trúc ấy: phép tạo lập hay hủy bỏ một cấu trúc,
phép truy cập (access) vào từng phần tử của cấu trúc, phép bổ sung (insertion) hoặc loại
bỏ (deletion) một phần tử trên cấu trúc .v.v..
Các phép đó sẽ có những tác dụng khác nhau đối với từng cấu trúc. Có phép
hữu hiệu đối với cấu trúc này nhưng lại tỏ ra khơng hữu hiệu trên cấu trúc khác.
Vì vậy chọn một cấu trúc dữ liệu phải nghĩ ngay tới các phép toán tác động trên
cấu trúc ấy. Và ngược lại, nói tới phép tốn thì lại phải chú ý tới phép đó được tác động
trên cấu trúc nào. Cho nên cũng khơng có gì lạ khi người ta quan niệm: nói tới cấu trúc
dữ liệu là bao hàm ln cả phép toán tác động trên các cấu trúc ấy. Ở giáo trình này tuy ta
tách riêng hai khái niệm đó nhưng cấu trúc dữ liệu và các phép toán tương ứng vẫn ln
được trình bày cùng với nhau.
c) Cấu trúc lưu trữ
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ữ
(storage structures). Đó chính là cách cài đặt cấu trúc ấy trên máy tính điện tử 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ý. Sự phân biệt giữa cấu trúc dữ
liệu và cấu trúc lưu trữ tương ứng, cần phải được đặt ra. Có thể có nhiều cấu trúc lưu trữ
khác nhau cho cùng một cấu trúc dữ liệu, cũng như có thể có những cấu trúc dữ liệu khác
nhau mà được thể hiện trong bộ nhớ bởi cùng một kiểu cấu trúc lưu trữ. Thường khi xử
lý, mọi chú ý đều hướng tới cấu trúc lưu trữ, nên ta dễ quên mất cấu trúc dữ liệu tương
ứng.
Khi đề cập tới cấu trúc lưu trữ, ta cũng 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, hay ứng với bộ nhớ ngoài – lưu trữ ngoài. Chúng đều
có những đặc điểm riêng và kéo theo các cách xử lý khác nhau
18


1.2.3. Diễn đạt giải thuật
Để diễn đạt các giải thuật ta cần lựa chọn một ngơn ngữ lập trình. Có thể nghĩ
ngay tới việc sử dụng một ngôn ngữ cấp cao hiện có, chẳng hạn PASCAL, C, C++…
nhưng như vậy ta sẽ gặp một số hạn chế sau:

- Phải luôn luôn tuân thủ các quy tắc chặt chẽ về cú pháp của ngơn ngữ đó khiến
cho việc trình bày về giải thuật và cấu trúc dữ liệu có thiên hướng nặng nề, gị bó.
- Phải phụ thuộc vào cấu trúc dữ liệu tiền định của ngơn ngữ nên có lúc không
thể hiện được đầy đủ các ý về cấu trúc mà ta muốn biểu đạt.
- Ngôn ngữ nào được chọn cũng khơng hẳn đã được mọi người u thích và
muốn sử dụng
Vì vậy, ở đây ta sẽ dùng một ngơn ngữ “thơ hơn”, có đủ khả năng diễn đạt được
giải thuật trên các cấu trúc đề cập đến (mà ta giới thiệu bằng tiếng Việt), với một mức độ
linh hoạt nhất định, khơng q gị bó, khơng câu nệ nhiều về cú pháp nhưng cũng gần gũi
với các ngôn ngữ chuẩn để khi cần thiết dễ dàng chuyển đổi. Ta tạm gọi nó bằng tên:
“ngơn ngữ tựa PASCAL”. Sau đây là một số quy tắc bước đầu, ở các chương sau sẽ có
thể bổ sung thêm.
a) Quy cách về cấu trúc chương trình
Mỗi chương trình đều được gán một tên để phân biệt, tên này được viết bằng chữ
in hoa, có thể có thêm dấu gạch nối và bắt đầu bằng từ khóa Program.
Ví dụ:
Program NHAN_MA_TRAN
Độ dài tên khơng hạn chế.
Sau tên có thể kèm theo lời thuyết minh (ở đây ta quy ước dùng tiếng Việt) để giới
thiệu tóm tắt nhiệm vụ của giải thuật hoặc một số chi tiết cần thiết. Phần thuyết minh
được đặt giữa hai dấu {……}
b) Ký tự và biểu thức
 Ký tự dùng ở đây cũng giống như trong các ngôn ngữ chuẩn, nghĩa là




gồm: 26 chữ cái Latin in hoa hoặc in thường
10 chữ số thập phân
Các dấu phép toán số học +, -, *, /,

Các dấu phép toán quan hệ <, =, >, ≤, ≥, ≠,
19


Giá trị logic: TRUE, FALSE
Dấu phép toán logic: AND, OR, NOT
Tên biến: dãy chữ cái và chữ số, bắt đầu bằng chữ cái.
Biến chỉ số có dạng A[i], B[j], .v.v..
 Biểu thức: cũng như thứ tự ưu tiên của các phép toán trong biểu thức cũng theo quy tắc
như trong PASCAL hay các ngôn ngữ chuẩn khác.
c) Các câu lệnh (hay các chỉ thị)
Các câu lệnh trong chương trình được viết cách nhau bởi dấu ; chúng bao gồm:
1. Câu lệnh gán
Có dạng A:=B
Với A chỉ tên biến, tên hàm
B chỉ biểu thức
Ở đây cho phép dùng phép gán chung.
Ví dụ:
A:=B:=0.1
2. Câu lệnh ghép
Có dạng:
Begin S1, S2, …; Sn; end
Với Si, i=1,…,n là các câu lệnh.
Nó cho phép ghép nhiều câu lệnh lại để được coi như một câu lệnh
3. Câu lệnh điều kiện
Có dạng:
if B then S
với B là biểu thức logic
S là một câu lệnh khác
Có thể diễn tả bởi sơ đồ


20


Hoặc
if B then S1 else S2

4. Câu lệnh
tuyển Case
B1: S1;
B2: S2;
……. Bn:
Sn; else:
Sn+1;
end case
với Bi (i=1,2,…,n) là các điều kiện
Si (i=1,2,…,n+1) là các câu lệnh

21


Câu lệnh này cho phép phân biệt các tình huống xử lý khác nhau trong các điều
kiện khác nhau mà không phải dùng tới các câu lệnh if – then – else lồng nhau. Có thể
diễn tả bởi sơ đồ:

* Chú ý:
- else có thể khơng có mặt
- Si (i=1,2,…,n) có thể được thay bằng một dãy các câu lệnh thể hiện một dãy xử lý khi
có điều kiện Bi mà không cần phải đặt giữa begin và end
5. Câu lệnh lặp

 Với số lần lặp biết trước
for i:=m to n do S
Nhằm thực hiện câu lệnh S với I lấy giá trị nguyên từ m tới n (m<=n), với bước
nhảy tăng bằng 1;
Hoặc:
for i:=n downto m do S
tương tự như câu lệnh trên với bước nhảy giảm bằng 1.
 Với số lần lặp không biết trước
while B do S

Hoặc:
repeat S until B

22


6. Câu lệnh chuyển
goto n
Với n là một nhãn được gắn trước. Câu lệnh cho phép nhảy ngay lập tức đến nhãn n

7. Câu lệnh vào ra
Có dạng:
read( danh sách biến )
write( danh sách biến hoặc dịng kí tự )
Các biến trong danh sách cách nhau bởi dấu phẩy.
Dịng kí tự là một dãy các kí tự đặt giữa hai dấu nháy ‘’
8. Câu lệnh kết thúc chương
trình end
d) Chương trình con
 Chương trình con hàm

Có dạng:

function <tên hàm>( <danh sách tham số>
) begin
S1;
S2;
….
Sn;
return
end

23


 Chương trình con thủ tục
Có dạng:

procedure <tên thủ tục>( <danh sách tham số> )
begin
S1;
S2;
….
Sn;
end
Sự khác nhau cơ bản và duy nhất của hai loại chương trình con này là
FUNCTION trả về một giá trị kết quả vô hướng thông qua tên function và do đó nó có
thể dử dụng như một biến, hằng, biểu thức. Cịn PROCEDURE thì khơng trả về kết quả
nào nên nó cũng khơng được viết trong biểu thức.

1.3. Phân tích và thiết kế giải thuật


1.3.1. Từ bài tốn đến chương trình
a) Mơ – đun hóa và việc giải quyết bài toán
Các bài toán giải được trên máy tính điện tử ngày càng đa dạng và phức tạp. Các
giải thuật và chương trình để giải chúng cũng ngày càng có quy mơ lớn và càng khó khi
thiết lập cũng như khi muốn tìm hiểu.
Tuy nhiên, ta cũng thấy rằng mọi việc sẽ đơn giản hơn nếu như có thể phân chia
bài tốn lớn của ta thành các bài tốn nhỏ. Điều đó cũng có nghĩa là nếu coi bài tốn của
ta như một mơ-đun chính thì cần chia nó thành các mơ – đun con, và dĩ nhiên, với tinh
thần như thế, đến lượt nó, mỗi mô – đun con này lại được phân chia tiếp cho tới những
mô-đun ứng với các phần việc cơ bản mà ta đã biết cách giải quyết. Như vậy việc tổ chức
lời giải của bài toán sẽ được thể hiện theo một cấu trúc phân cấp, có dạng như hình sau:

24


×