DỮ LIỆU KIỂU MẢNG
I. MẢNG MỘT CHIỀU
1. Khái niệm chung về cấu trúc dữ liệu:
Chúng ta đã làm quen với các kiểu dữ liệu đơn giản là các kiểu vô hướng (Integer, Char,
Boolean, Real, kiểu liệt kê) và đoạn con. Trong Pascal tồn tại các kiểu dữ liệu có cấu trúc là
các kiểu dữ liệu được tạo ra từ các phần tử có kiểu dữ liệu đơn giản bằng một cách nào đó.
Chúng được đặc trưng bằng kiểu dữ liệu của các phần tử và quan trọng hơn cả là phương
pháp cấu thành kiểu dữ liệu mới (điều đó cũng có nghĩa là phương pháp truy nhập vào kiểu
dữ liệu có cấu trúc). Tính có cấu trúc của dữ liệu là một đặc trưng của ngôn ngữ lập trình có
cấu trúc.
Pascal có tất cả 4 kiểu dữ liệu có cấu trúc mà chúng ta sẽ lần lượt ngiên cứu : mảng
(Array), tập (Set), bản ghi (Record) và tệp (File).
2. Kiểu dữ liệu có cấu trúc : Mảng (Array)
Một mảng dữ liệu gồm một số hữu hạn phần tử có cùng kiểu gọi là kiểu cơ bản. Số phần
tử của mảng được xác định ngay từ khi định nghĩa ra mảng. Mỗi phần tử của mảng đựoc
truy nhập trực tiếp thông qua tên mảng cùng với chỉ dẫn truy nhập được để giữa hai ngoặc
vuông [ ].
Định nghĩa kiểu mảng T có kiểu các phần tử là KPT, có kiểu chỉ dẫn KCD để hướng dẫn
cách tổ chức mảng cũng như cách truy nhập vào các phần tử mảng được viết trong Pascal
như sau :
Type
Kiểu_mảng T = Array[ Kiểu_chỉ_dẫn KCD ] Of Kiểu_phần_tử KPT ;
hay viết tắt thành :
T = Array[ KCD ] Of KPT ;
Khi đó việc khai báo một biến A có kiểu là Kiểu_mảng có thể được viết như sau :
Var
A : Kiểu_mảng T ;
hoặc ta có thể khai báo trực tiếp biến A cùng với kiểu của mảng trong phần khai báo biến
khi không có định nghĩa kiểu trong phần Type :
Var
MM[ Red ] := True ;
MC[ 'B' ] := 5 ;
Do thời gian truy nhập vào một phần tử của mảng không phụ thuộc vào giá trị của chỉ
dẫn nên cấu trúc mảng thuộc kiểu cấu trúc truy nhập trực tiếp.
Ví dụ1:
Gán tất cả 5 giá trị của mảng B ( như đã định nghĩa ở trên ) qua bàn phím, ta sẽ dùng
thêm một biến I có kiểu là Integer để làm biến chỉ dẫn :
Writeln (' Vao so lieu cho mang B ' ) ;
For I := 1 To 5 Do
Begin
2
Write (' B[ ', I, ' ] = ') ;
Readln ( B[ I ] ) ;
End ;
Kết quả thể hiện ra màn hình với các con số là do người sử dụng gõ vào :
Vao so lieu cho mang B
B[1] = 2
B[2] = 12
B[3] = 612
B[4] = 2
B[5] = 34
Nếu bạn muốn vào số liệu của cả một hàng của Array trên cùng một dòng màn hình thì
bạn phải ghi rõ tất cả các biến cần đọc ( là các phần tử của mảng ) trong thủ tục Readln. Khi
này sẽ không áp dụng vòng For được nữa :
Write (' Vao so lieu hang 1 ') ;
Readln ( B[1], B[2], B[3], B[4], B[5] ) ;
Kết quả hiện ra màn hình :
Vao so lieu hang 1 : 2 12 612 2 34
Giữa các số gõ là các dấu cách với số lượng tùy ý nhưng ít nhất phải là 1.
END.
Kết quả hiện ra trên màn hình có dạng :
So chu A = 2
So chu C = 68
........................
So chu Z = 8
II. SẮP XẾP CÁC PHẦN TỬ CỦA MẢNG:
Sắp xếp các phần tử của mảng theo một trật tự tăng hoặc giảm là một ví dụ rất bổ ích cho
việc nắm vững các phép xử lí mảng. Sau đây sẽ trình bày một số phương pháp sắp xếp
mảng qua các ví dụ.
Giả sử ta có một dãy dữ liệu ( số thực, số nguyên, kí tự ... ) được chứa trong một mảng.
Sau đây là một số vì dụ về một số phương pháp xếp mảng các số nguyên. Việc các phần tử
mảng là các số nguyên chỉ là một ví dụ, nó có thể là mảng các số thực hoặc mảng các xâu
kí tự.
Cách làm:
Đầu tiên đem phần tử thứ nhất so sánh với các phần tử tiếp theo, nếu nó lớn hơn thì đem
đổi chỗ giá trị của hai phần tử so sánh. Kết quả sau lượt đầu tiên giữ giá trị nhỏ nhất. Tiếp
theo vòng 2, Đem phần tử thứ 2 so sánh với các phần tử tiếp theo.
Program SAP_XEP ;
Const
N = 5 ;
Var
MI : Array[ 1.. N ] Of Integer ;
4
T : Integer ; (* T là biến trung gian *)
I, J : Integer ;
BEGIN
(* Đọc các số cần sắp xếp vào mảng MI *)
For I := 1 To N Do
Begin
II. MẢNG HAI CHIỀU:
Kiểu phần tử của mảng không bị hạn chế nhiều như kiểu chỉ dẫn. Nó còn có thể là các
kiểu có cấu trúc. Ví dụ sau cho thấy việc khai báo một mảng có các phần tử cũng là mảng.
Ví dụ:
Type
PT = Array [ 1.. 5 ] Of Real ;
Color = ( Red, Blue, Green, White, Black ) ;
Var
MPT : Array [ 1.. 3 ] Of Pt ;
Z : Array [ 1.. 3, 'A'.. 'C' ] Of Color ;
hoặc viết một lần như sau :
Var
MPT : Array [ 1.. 3 ] Of Array [ 1.. 5 ] Of Real ;
hoặc thường được viết gọn lại :
Var
MPT : Array [ 1.. 3, 1.. 5 ] Of Real ;
MPT được định nghĩa như trên chính là ma trận hai chiều 3 hàng và 5 cột.
Việc truy nhập đối với mảng có định nghĩa phức tạp như MPT được tiến hành qua hai lần
đóng mở ngoặc vuông. Ví dụ MPT [3] [5] hoặc MPT [ 3, 5 ] biểu diễn phần tử ở hàng 3 và
cột 5.
Cách viết MPT[i] [j] và MPT[ i, j ] là tương đương nhau. Mảng được định nghĩa như
trên có thể hiểu là ma trận nhiều chiều. Phần tử MPT[ i, j ] sẽ là phần tử ở hàng thứ I, cột
thứ J của MPT
Ví dụ:
Chương trình nhân hai ma trận vuông cấp N
C = A * B
Phần tử của ma trận tích được tính theo công thức :
Const
N = 3 ;
For I := 1 To N Do
Begin
For J := 1 To N Do Write ( C[ I, J ] : 5 ) ;
Writeln ;
End ;
END.
Trong chương trình trên, việc đọc ma trận được tiến hành qua từng dòng cho mỗi phần tử
của mảng. Bạn có thể sửa lại đọc vào ma trận dưới dạng từng dòng tương ứng với từng
hàng của ma trận :
(* Đọc vào giá trị của ma trận A theo từng hàng *)
For I := 1 To N Do
Begin
Write (' Hang ', I, ' : ' ) ;
Readln ( A [ I, 1 ], A [ I, 2 ], A [ I, 3 ] ) ;
End ;
7
Một cách khác lười hơn đỡ phải đọc ma trận trong khi thử, hãy làm một đoạn chương
trình tạo số ngẫu nhiên cho các phần tử của ma trận. Bạn cần nhớ lại hoặc tra cứu hàm
Random và Randomize.
Mảng có thể dùng làm tham số cho chương trình con và mảng không bao giờ dùng làm
kết quả của Function . Tuy nhiên cần lưu ý khai báo kiểu của tham số trong vùng khai báo
Type chứ không định nghĩa trực tiếp ngay trong phần khai báo tham số của chương trình
con.
Ví dụ:
Cộng hai ma trận C = A + B
Type
MT = Array [ 1.. 3, 1.. 5 ] Of Real ;
Var
X, Y, Z : MT ;
Truy nhập vào các phần tử của xâu kí tự : ta có thể truy nhập vào từng kí tự một của xâu
kí tự với tên biến và chỉ số đặt trong ngoặc vuông như khik truy nhập vào phần tử của
mảng. Chỉsố này có thể chạy từ 1 đến độ dài cực đại của xâu kí tự
If filename[3] = 'D' then
Writeln (' Chu thu ba cua xau ki tu la D ') ;
Nếu vị trí kí tự đó nằm ngoài độ dài thực của xâu kí tự thì phần tử đó của xâu không có
giátrị xác định. Vì vậy, khi truy nhập vào từng phần tử của xâu chữ, ta còn cần phải kiểm
tra xem vị trí đó có nằm trong khoảng độ dài thực của xâu hay không.
If 3 < Length(Filename) then
Writeln (' Chu thu ba cua Filename la : ', Filename[3]) ;
9
1. Phép cộng xâu:
Xâu kí tự có thể sử dụng như các toán hạng trong các biểu thức để ghép xâu kí tự qua
dấu "+".
Ví duï:
Filename := ' Vidu.pas ' ;
Filename := ' C:\ ' + Filename ;
Cho kết quả của Filename là : ' C:\Vidu.pas ' ;
hoặc Filename := ' Ten ' + ' file ' + '.pas ' ;
Cho kết quả của Filename là : ' Tenfile.pas ' ;
Rõ ràng với kiểu mảng kí tự , chúng ta không thể thực hiện được phép cộng để ghép hai
mảng với nhau vì độ dài của chúng đã cố định.
* Lưu ý: không có phép tính trừ, nhân, chia cho xâu kí tự.
2. Khai báo String làm tham số chương trình con :
Tương tự Array , String có thể dùng làm tham số cho chương trình con ,
Ví dụ:
Type
St20 : String[20] ;
St30 : String[30] ;
Var
_ Delete(St , Pos , Num) là thủ tục xóa đi một số kí tự Num kể từ vị trí Pos trong xâu St.
Ví dụ : Sau khi gọi Delete (St , 2 , 3) ; St sẽ còn ' Fname ' vì bị xóa đi 3 kí tự ' ile '. Nếu
Pos + Num > Length (St) thì St sẽ bị xóa toàn bộ kí tự kể từ vị trí Pos.
_ Insert(Obj , St , Pos) là thủ tục xen xâu kí tự Obj vào xâu St tại vị trí Pos.
Ví dụ: Sau khi gọn Insert(' 12 ' , St , 4) ; thì St sẽ thành ' Fil12ename '. Nếu Length
(Obj) + Length (St) vượt quá độ dài cực đại cho phép thì chỉ những kí tự nào nằm
trong khoảng độ dài cực đại cho phép mới được giữ lại.
_ Str(Value , St) là thủ tục biến đổi giá trị bằng số nguyên hoặc thực Value thành một
dãy kí tự biểu diễn số đó
Ví dụ: I là biến kiểu số nguyên , X là biến kiểu số thực :
I := 512 ;
Str(I : 5 , St) ; sẽ cho ra St là ' 512 ' (* 5 kí tự *)
X := 123.4567890 ;
Str(X : 12 : 5 , St) ; cho ra St là ' 123.45678 ' (* 12 kí tự *)
_ Val(St , Var1 , Code) là thủ tục biến đổi một xâu kí tự St (biểu diễn một số nguyên
hoặc thực) thành một số nguyên hoặc thực chứa trong Var1. Code là số nguyên để
phát hiện lỗi.
Ví dụ:
St1 := ' 123.45 ' ;
St2 := ' 123X ' ;
11
Val (St1 , Var1 , Code1) ; cho ta Var1 = 123.45 và Code1 = 0 ;
Val (St2 , Var2 , Code2) ; cho ta Var2 không xác định và Code2 = 4 ;
Lưu ý : Các biến Var1 , Var2 , Code1 , Code2 đều phải được khai báo trước.
_ Copy(St , Pos , Size) nhận Size kí tự trong St từ vị trí Pos.
Ví dụ:
St := ' 12345678 ' ;
St1 := Copy (St , 3 , 2) ; sẽ cho St1 = ' 34 ' ;
Nếu Pos > Length (St) thì Copy sẽ cho một xâu rỗng.
Nếu Pos + Size > Length (St) thì Copy sẽ chỉ nhận các kí tự nằm trong xâu St.
Để mô tả một kiểu T có cấu trúc Record với danh sách các trường có tên là S1 , S2 ,... ,
Sn và có các mô tả kiểu trường tương ứng là T1 , T2 ,... , Tn , ta có thể viết tổng quát sau :
Ví dụ 1: Một địa chỉ bao gồm các dữ liệu như số nhà, tên phố, thành phố. Ta mô tả
Record Dia_chi như sau :
Type
Dia_chi = Record
So_nha : Integer ;
Pho : String[20] ;
Thanh_pho : String[15] ;
End ;
Như vậy chúng ta có 3 trường là So_nha, Pho và Thanh_pho với kiểu khác nhau
(integer, string[20], string[15]) và chúng được liên kết lại với nhau để mô tả địa chỉ.
Ví dụ 2 : Để mô tả thời gian Date, ta có 3 trường : Ngày, Tháng và Năm.
Type
Date = Record
Ngay : 1.. 31 ;
Thang : 1.. 12 ;
13
Nam : integer ;
End ;
hoặc theo kiểu tiếng Anh, người ta hay dùng tháng với tên gọi tháng :
Type
Date = Record
Day : 1.. 31 ;
Month : (Jan, Fer, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov ,Dec) ;
Year : integer ;
End ;
Ví dụ 3: Để miêu tả nhân sự hay phiếu cán bộ của phòng tổ chức, ta phải dùng các
trường Họ tên, Ngày sinh, Chỗ ở, Lương... Ở đây ta ví dụ với 5 trường. Giả sử