13.1. Mảng một chiều
Mảng là một tập gồm nhiều phần tử có cùng chung một kiểu dữ liệu. Mỗi phần tử của mảng có một đại lợng xác định vị trí tơng đối của phần tử đó so với các phần tử khác trong mảng, gọi là chỉ số. Các yếu tố để xác định một mảng gồm có:
- Tên mảng
- Kiểu dữ liệu chung của các phần tử trong mảng - Kiểu dữ liệu của chỉ số và phạm vi của chỉ số.
Kiểu dữ liệu của các phần tử mảng là mọi kiểu dữ liệu mà một biến có thể có. Tuy nhiên, kiểu dữ liệu của chỉ số thì không đợc là kiểu thực hay kiểu chuỗi, nó thờng là kiểu nguyên, ký tự, lôgic, liệt kê hay đoạn con.
13.1.1. Khai báo mảng một chiều
Có hai cách khai báo.
Cách 1: Khai báo trực tiếp theo cách sau: VAR
Tên mảng: Array [m1..m2] of Tênkiểudữliệu:
ở đây m1, m2 là hai hằng xác định phạm vi của chỉ số, chúng có chung một kiểu dữ liệu, và m1 ≤ m2.
Ví dụ: Cho khai báo dới đây: Var
A: Array [0..10] of real:
Htem: Array [1..5] of String[18]; B: Array [‘a’..’d’] of Integer:
Theo khai báo trên, ta có ba mảng:
- Mảng thứ nhất tên là A, gồm 11 phần tử cùng kiểu Real, ứng với các chỉ số 0, 1, 2, ..10, đó là:
- Mảng thứ hai tên là Hten gồm 5 phần tử cùng kiểu dữ liệu là String[18] ứng với các chỉ số từ 1 đến 5.
Hten[1], Hten[2], Hten[3], Hten[4], Hten[5].
- Mảng thứ ba tên là B, gồm 4 phần tử cùng kiểu Integer ứng với các chỉ số ‘a’, ‘b’, ‘c’, ‘d’.
B[‘a’], B[‘b’], B[‘c’], B[‘d’]
Để có một hình ảnh về mảng, đối với mảng A, ta hình dung có một dãy nhà một tầng, tên gọi là dãy A gồm 11 phòng liên tiếp giống hệt nhau đợc đánh số thứ tự từ 0,1,2 đến 10.
0 1 2 ... 1
0
A ...
Tơng tự, mảng B cũng giống nh dãy nhà B một tầng có 4 phòng đ- ợc đánh số thứ tự là các chữ a, b, c, d:
a b c d
B
Cách 2: Khai báo qua một kiểu dữ liệu mới, gồm hai bớc: Bớc 1: Định nghĩa kiểu dữ liệu mảng:
TYPE
Tênkiểumảng = Array [m1..m2] of Tênkiểudữliệu: Bớc 2: Khai báo biến có kiểu dữ liệu là kiểu mảng: VAR
Tênmảng: Tênkiểumảng;
Ví dụ, đối với các mảng A, B và Hten ở trên ta có thể khai báo theo cách 2, nh sau:
Type
Mang1 = array [0..10] of Real; Mang2 = array [1..5] of String[18]; Mang3 = array [‘a’..’d’] of Integer: Var
A: Mang1; Hten: Mang2; B: Mang3;
13.1.2. Khai báo mảng có gán trị ban đầu
Pascal cho phép vừa khai báo mảng vừa gán giá trị ban đầu cho các phần tử mảng, chẳng hạn nh dới đây:
Const
X: array [1..5] of Integer = (12, 14, 16, 18, 20);
Khi đó X là một mảng gồm 5 phần tử cùng kiểu nguyên và có giá trị X[1]=12, X[2]=14, X[3]=16, X[4]=18, X[5]=20.
Mặc dù từ khoá ở đây là Const song X lại đợc dùng nh là một biến, tức là các phần tử của X có thể thay đổi giá trị đợc. Ví dụ, trong chơng trình ta có thể gán: X[1]:=2; X[2]:=5 +20; 13.1.3. Truy xuất các phần tử mảng Các xử lý trên mảng đợc quy về xử lý từng phần tử mảng. Để xác định một phần tử của mảng, ta dùng cách viết: Tênmảng[chỉ số của phần tử] Ví dụ, có thể gán: A[0]:=15.8; A[1]:=2*A[0];
Hten[3]:= ‘Nguyen Thi Loan’; B[‘a’]:=100;
Chỉ số của một phần tử có thể là một biến, một hằng, hay một biểu thức. Ví dụ, cho i là biến kiểu nguyên, khi đó ta có thể dùng các lệnh:
i:=6;
Hai lệnh trên tơng đơng với một lệnh; A[6]:=100.25;
Nếu biến i có giá trị là 6 thì lệnh: A[i div 2+1]:=4.5;
Tơng đơng với lệnh:
A[4]:=4.5; vì biểu thức i div 2 + 1 có giá trị là 4.
Khi nhập dữ liệu cho các phần tử của một mảng, ta có thể dùng câu lệnh For, While hay Repeat.
Ví dụ, nhập dữ liệu cho các phần tử của mảng A: For i:=0 to 10 do
Begin
Write (‘Nhap phần tử thứ ‘,‘i’, ‘: ’); Readln(A[i]);
End;
Tơng tự để nhập dữ liệu cho các phần tử của mảng B, ta viết: For ch:= ‘a’ to ‘d’ do
Begin
Write (‘Nhap phần tử thứ ’, ch, ‘:’); Readln (B[ch]);
End;
Để in các giá trị của mảng A lên màn hình, ta viết: For i:= 0 to 10 do Write (A[i]:6:2);
Các giá trị của mảng A sẽ đợc in liên tiếp nhau trên cùng một dòng. Còn nếu muốn in mỗi phần tử trên một dòng, ta thay lệnh Write bằng Writeln.
Tơng tự, mảng B đợc in lên màn hình bằng lệnh: For ch:= ‘a’ to ‘d’ do Write (b[ch]:6:2);
Chú ý: Turbo Pascal cho phép gán một mảng này cho một mảng khác.
Nếu X, Y là hai biến mảng cùng một kiểu mảng thì lệnh: X:=Y;
Có nghĩa là lấy giá trị của từng phần tử của mảng Y gán cho phần tử tơng ứng trong mảng X. Ví dụ, cho khai báo:
Var
X, Y: Array [1..10] of Real; Khi đó lệnh:
x:=y;
tơng đơng với lệnh:
For i:=1 to 10 do x[i]:=Y[i];
13.1.4. Các bài toán cơ bản về mảng
Bài toán 1: Đếm số lần xuất hiện của giá trị x trong dãy A1, A2,.. An cho trớc:
Ví dụ giá trị x=6 xuất hiện 3 lần trong dãy 6 7 1 2 6 0 6 1.
Ta dùng biến Dem kiểu nguyên để đếm số lần xuất hiện của x. Đầu tiên ta gán Dem:=0, sau đó duyệt từng phần tử A1, A2,.., An, mỗi khi có một phần tử bằng x thì tăng biến Dem lên một đơn vị. Kết quả là biến Dem có giá trị đúng bằng số phần tử bằng x. Hai lệnh chính của thuật toán là:
Dem:=0;
For i:=1 to N do If A[i]=x then Dem:=Dem+1;
Ví dụ, đếm trong dãy số A có bao nhiêu số 0, ta viết: Dem:=0;
For i:=1 to N do if A[i] =0 then Dem:=Dem +1; Writeln (‘Có’, Dem, ‘số không’);
Bài toán 2: Tìm số lớn nhất của dãy A1, A2,.., An.
Trong Đ8, ta đã chỉ ra cách tìm số lớn nhất của hai số, của ba số. Có thể mở rộng thuật toán đó để tìm số lớn nhất của n số:
Bớc 1: Gán Max:=A[1];
Bớc 2: Nếu Max<A[2] thì gán Max:=A[2]; Bớc 3: Nếu Max<A[3] thì gán Max:=A[3]; ..
Bớc n: Nếu Max<A[n] thì gán Max:=A[n];
Khởi đầu, Max đợc gán giá trị A[1]. Sang bớc 2, Max đợc so sánh với A[2] để chọn ra số lớn nhất giữa A[1], A[2] và lu vào biến Max. Sang bớc 3, Max đợc tiếp tục so sánh với A[3]để tìm ra số lớn nhất giữa A[1], A[2], A[3], v.v. Kết quả, sau bớc n, biến Max sẽ chứa số lớn nhất của dãy A[1], A[2], .., A[n].
Quá trình trên đợc mô tả bằng hai lệnh: Max:=A[1];
For i:=2 to n do if Max<A[i] then Max: =A[i]: Nhận xét:
- Trong lệnh For trên, biến i chạy bắt đầu từ 2, nhng kết quả vẫn đúng nếu cho i chạy bắt đầu từ 1.
- Không nhất thiết phải gán giá trị ban đầu cho Max là A[1], mà có thể gán cho Max một phần tử tuỳ ý trong mảng, ví dụ phần tử A[n] chẳng hạn, nhng khi đó biến i trong lệnh For phải chạy bắt đầu từ 1.
Bài toán 3: Bài toán sắp xếp mảng tăng dần (hay giảm dần)
Cho dãy A[1], A[2], .. ,A[n], nói rằng A là một dãy tăng nếu A[1]≤ A[2]≤..≤A[n], tơng tự, A là dãy giảm nếu A[1]≥A[2]≥..≥A[n]. Dãy đồng nhất A[1]=A[2]=..A[n] là trờng hợp đặc biệt, vừa là dãy tăng, vừa là dãy giảm.
Ví dụ:
Dãy 1 3 3 5 5 6 6 6 là dãy tăng Dãy 9 9 8 5 5 4 0 0 là dãy giảm
Dãy 1 3 3 2 5 4 6 6 là dãy không tăng không giảm.
Bài toán đặt ra là: cho một dãy A[1], A[2],.., A[n] bất kỳ, hãy thực hiện các hoán đổi các giá trị của các phần tử trong mảng A để A lập thành một dãy tăng.
Ví dụ, cho dãy A có 5 phần tử A[1]=9, A[2]=7, A[3]=5, A[4]=8, và A[5]=2, cần thực hiện các hoán đổi nh thế nào để có A[1]=2, A[2]=5, A[3]=7, A[4]=8 và A[5]=9.
Có những phơng pháp sắp xếp mảng khác nhau, ở đây chỉ xin giới thiệu một phơng pháp, tuy cha phải là hay nhng đơn giản và dễ hiểu cho những ngời mới lập trình, đó là phơng pháp lựa chọn trực tiếp (Straight selection sort).
ý tởng của phơng pháp là nh sau:
Bớc 1: Tìm số nhỏ nhất trong các phần tử A[1], A[2],.., A[n] và để vào vị trí đầu tiên A[1].
Bớc 2: Tìm số nhỏ nhất trong các phần tử A[2], A[3],..A[n] và để vào vị trí thứ hai A[2].
.v.v.
Bớc n-1: Tìm số nhỏ nhất trong hai phần tử A[n-1], A[n] và để vào vị trí n-1. Sau bớc này thì A[n] sẽ là giá trị lớn nhất.
Chẳng hạn, xét dãy A có 4 phần tử: [5,3,4,1] Bớc 1: Nếu A[1]>A[2] thì đổi A[1] với A[2], đợc [3,5,4,1] Nếu A[1]>A[3] thì đổi A[1] với A[3]: không đổi Nếu A[1]>A[4] thì đổi A[1] với A[4], đợc: [1,5,4,3] Bớc 2: Nếu A[2]>A[3] thì đổi A[2] với A[3], đợc [1,4,5,3] Nếu A[2]>A[4] thì đổi A[2] với A[3], đợc: [1,4,5,3] Bớc 3: Nếu A[3]>A[4] thì đổi A[3] với A[4], đợc:[1,3,4,5] Sau ba bớc, dãy A đã đợc sắp xếp xong.
Tại bớc thứ i (i chạy từ 1 đến 3), ta phải so sánh A[i] lần lợt với A[j] (j chạy từ i+1 đến 4), nếu A[i]>A[j] thì hoán đổi các giá trị của A[i] và A[j], nói cho gọn là đổi chỗ A[j] với A[j]. Quá trình trên đợc thể hiện bằng hai vòng lặp For:
For i: =1 to 3 do For j:=i+1 to 4 do
Mảng A ở trên chỉ có 4 phần tử, trong trờng hợp tổng quát khi mảng A có N phần tử thì lệnh For thứ nhất sẽ có biến i chạy từ 1 đến N-1, và lệnh For thứ hai sẽ có biến j chạy từ i+1 đến N, tức là:
For i:=1 to N-1 do For j:=i+1 to N do
if A[i]>A[j] then Đổi chỗ A[i] và A[j];
Việc đổi chỗ các giá trị trong A[i] và A[j] đợc tiến hành bằng các dùng một biến Z trung gian cùng kiểu dữ liệu với A[i] và A[j]. Đầu tiên gởi tạm giá trị của A[i] vào biến Z, sau đó đa giá trị của A[j] vào A[i], và cuối cùng đa giá trị trong Z vào A[j], tức là phải làm 3 lệnh:
Z:=A[i]: A[i]:=A[j]; A[j]:=Z;
Tóm lại, thuật toán sắp xếp dãy A tăng đợc viết nh sau: For i:=1 to N-1 do
For j:=i +1 to N do if A[i]>A[j] then
begin (đổi chỗ A[i] và A[j] Z:=A[i];
A[i]:=A[j]; A[j]:=Z;
end;
Trong đó N là số phần tử của dãy A còn Z là một biến trung gian có cùng kiểu dữ liệu với các phần tử của mảng A.
Dới đấy là chơng trình cụ thể: PROGRAM VIDU42;
{Sắp xếp dãy A tăng dần} Use CRT;
Kmang = array [1..20] of Real; Var i, j, N: Integer; A: Kmang; z: Real; Begin Clrscr; Repeat Write (‘Nhập số phần tử N:’); Readln (N); Until (N>0) and (N<21); For i:=1 to N do (nhập mảng) Begin Write (‘Nhập A[‘,i,’]:’); Readln (A[i]); End;
For i:=1 to N-1 do {Sắp xếp tăng} For j:=i+1 to N do
If A[i]>A[j] then {23} Begin {đổi chỗ A[i] và A[j]} z:=A[i];
A[i]:=A[j]; A[j]:=z; End;
Writeln (‘dãy đã sắp tăng là:’); For i:=1 to N do write (A[i]:4:1); Readln;
Chú ý 1: Muốn sắp dãy A giảm dần thì trong chơng trình trên chỉ cần thay dòng {23}:
If A[i]>A[j] then ... {23} Bằng dòng:
If A[i]<A[j] then ...
Tức là thay dấu lớn hơn> bằng dấu nhỏ hơn<.
Bài toán 4: Kiểm tra mảng có thoả một tính chất không.
Ta thờng gặp bài toán kiểm tra xem mọi phần tử của mảng A có thoả mãn một điều kiện không, ví dụ mảng A có phải là dãy tăng không, có phải là dãy đối xứng không, có phải là một cấp số cộng không, ...
Mảng A thoả tính chất đang xét nếu mọi phần tử của nó thoả một điều kiện xác định nào đó, ngợc lại, mảng A không thoả tính chất đang xét nếu có một phần tử của nó không thoả điều kiện này.
Hai trạng thái thoả hay không thoả đợc thể hiện bằng hai giá trị TRUE hay FALSE của một biến Kiemtra kiểu lôgic. Đầu tiên ta gán giả định Kiemtra:=TRUE, sau đó ta xét từng phần tử của A, chỉ cần có một phần tử không thoả điều kiện thì gán ngay Kiemtra:=FALSE. Vậy hai lệnh cần dùng là:
Kiemtra:=TRUE; For i:=1 to N do
if A[i] không thoả điều kiện then Kiemtra:=FALSE; Việc xác định điều kiện là tuỳ từng bài toán cụ thể.
Ví dụ: Kiểm tra xem A có phải là một dãy đối xứng không? Dãy 1 3 5 4 5 3 1 là đối xứng.
Dãy 1 3 5 4 2 3 1 là không đối xứng vì A[3] khác A[5].
Nh vậy, dãy N phần tử A1, A2,..,An là đối xứng nếu A1=An, A2=An- 1, ..,An=Ai, tức A1=An-i+1 với mọi i =1, 2, .., n. Đẳng thức: Ai=An-i+1
chính là điều kiện mà mọi phần tử của dãy A phải thoả để A là một dãy đối xứng.
Giả thiết biến Kiemtra đã đợc khai báo kiểu Boolean. Trong ch- ơng trình ta dùng các lệnh sau:
Kiemtra:=TRUE; For i:=1 to N do
if A[i]<>A[N-i+1] then Kiemtra:=FALSE;
If Kiemtra=TRUE then writeln (‘Dãy A đối xứng’) else
writeln(‘Dãy A không đối xứng’);
Trong thuật toán trên, lệnh For có thể thay bằng lệnh While, tốc độ sẽ nhanh hơn song cũng khó hiểu hơn:
Kiemtra:=TRUE; i:=1;
While (i<=N) and (Kiemtra=TRUE) do if A[i]<>A[N-i+1] then Kiemtra:=FALSE else i:=i+1;
If Kiemtra=TRUE then writeln (‘Dãy A đối xứng’) Else
Writeln (‘Dãy A không đối xứng’);
13.2. Mảng hai chiều (ma trận)
13.2.1. Khai báo mảng hai chiều
Mảng hai chiều, còn gọi là ma trận, là sự mở rộng trực tiếp của mảng một chiều. Ta cũng có hai cách khai báo.
Cách 1: Khai báo trực tiếp: VAR
Tênmảng: Array [n1..n2, m1..m2) of Tênkiểudữliệu:
Trong đó n1, n2 là các hằng có cùng kiểu dữ liệu và n1 và n2, chúng xác định phạm vi của chỉ số thứ nhất, gọi là chỉ số dòng. Tơng tự m1, m2 là các hằng có cùng kiểu dữ liệu và m1 và m2, chúng xác định phạm vi của chỉ số thứ hai, gọi là chỉ số cột. Giống nh mảng một
chiều, kiểu dữ liệu của các chỉ số thờng là kiểu nguyên, hay ký tự, không đợc là kiểu thực hay chuỗi.
Ví dụ, cho khai báo: Var
X:array [1..2, 1..3] of Real;
Y:array [‘a’.. ‘c’, 1..3] of String[15]; Kết quả ta nhận đợc hai mảng hai chiều:
Mảng X gồm 6 phần tử cùng kiểu dữ liệu thực: X[1,1], X[1,2], X[1,3]
X[2,1], X[2,2], X[2,3]
Mảng Y gồm 9 phần tử cùng kiểu chuỗi String[15] Y[‘a’,1], Y[‘a’,2], Y[‘a’,3]
Y[‘b’,1], Y[‘b’,2], Y[‘b’,3] Y[‘c’,1], Y[‘c’,2], Y[‘c’,3]
Có thể ví X là một nhà hai tầng, mỗi tầng có ba phòng giống nhau. Các tầng đợc đánh số từ 1 đến 2, trong mỗi tầng, các phòng đợc đánh số từ 1 đến 3. Tơng tự, Y là một nhà ba tầng, các tầng đợc đánh số lần lợt là ‘a’, ‘b’, ‘c’, mỗi tầng có ba phòng đợc đánh số lần lợt là 1,2,3.
Cách 2: Biến mảng đợc khai báo thông qua một kiểu mảng đã đợc định nghĩa trớc đó bằng từ khoá TYPE, tức là:
TYPE
Tênkiểumảng=Array[n1..n2, m1..m2] of Tênkiểudữliệu; VAR
Tênmảng: Tênkiểumảng;
Ví dụ, Hai mảng X và Y nói trên có thể đợc khai báo theo hai bớc sau:
Type
Kmang1 = array [1..2, 1..3] of Real;
Kmang2 = array [‘a’.. ‘c’, 1..3] of String [15]; Var
X: Kmang1; Y: Kmang2;
Chú ý: Có thể xem mảng hai chiều là mảng một chiều mà mỗi phần tử của nó lại là một mảng một chiều.
Hai mảng X, Y nói trên có thể khai báo nh sau: Type
Kmang1=array 1..2] of array [1..3] of Real;
Kmang2 =array [‘a’..‘c’] of array[1..3] of String [15]; Var
X: Kmang1; Y: Kmang2;
Hiểu theo cách này thì X là một mảng gồm hai phần tử X[1] và X[2] mà mỗi phần tử này lại là một mảng gồm 3 phần tử;
X[1] là mảng có 3 phần tử kiểu thực X[1][1], X[1][2], X[1][3] X[2] là mảng có 3 phần tử kiểu thực X[2][1], X[2][2], X[2][3] Điều tơng tự cũng áp dụng cho biến mảng Y.
Hai cách viết X[i][j] và X[i,j] cũng chỉ một phần tử.
Có thể khai báo và gán giá trị ngay cho một mảng hai chiều, chẳng hạn:
Type
Kmang1 = array [1..2, 1..3] of Real; Const
x: Kmang1 = ((1.5, 2.5, 3.5), (5.0, 6.5, 7.0));
Khi đó X là một mảng hai chiều có 6 phần tử cùng kiểu thực và có giá trị là:
X[1,1]=1.5, X[1,2]=2.5, X[1,3]=3.5 X[2,1]=5.0, X[2,2]=6.5, X[2,3]=7.0
Cần nhấn mạnh rằng mặc dù từ khoá ở đây là Const song X và các phần tử của X có thể dùng nh các biến, tức là các phần tử của X có thể thay đổi giá trị đợc.
13.2.2. Các thao tác trên ma trận.
Để xác định một phần tử trong mảng hai chiều ta viết: Tênbiếnmảng[chỉ số 1, chỉ số 2]
Ví dụ:
X [1,1]:= 12.5;
X [2,1]:= X [1,1]+15;
Y [ ‘a’, 1] := ‘Tran Thi Mai’;
Để nhập dữ liệu cho một mảng hai chiều, ta phải dùng hai vòng lập duyệt theo hai chỉ số, chẳng hạn muốn nhập dữ liệu cho mảng X, ta viết: For i := 1 to 2 do For j := 1 to 3 do Begin Write (‘Nhập phần tử hàng’ , i, ‘ cột ’ , j , :’) Readln (x[i, j]); End;
Tơng tự, lệnh nhập dữ liệu cho mảng Y đợc viết là: For ch:=’a’ to ‘c’ do For j:=1 to 3 do Begin Write (‘Nhập phần tử hàng’, ch, ‘ cột ’, j, ‘:’); Readln (x[ch, j]) End;
Trong đó ch là biến kiểu tự, còn i và j là các biến nguyên.
Để in mảng X lên màn hình, trình bày giống nh cách viết ma trận, mỗi hàng in trên một dòng, ta dùng lệnh:
For i:=1 to 2 do Begin
{in hàng thứ i}
For j:=1 to 3 do write (x[i, j]:3:1);
{xuống dòng, chuẩn bị in hàng tiếp theo} Writeln;
End;
13.2.3. Các ví dụ
Vì ma trận là mảng một chiều của các mảng một chiều nên nhiều bài toán về mảng đợc mở rộng tự nhiên cho ma trận.
Ví dụ 4: Tính tổng của hai ma trận
Nhập vào hai ma trận A, B cấp NxM. Tính ma trận C là tổng của hai ma trận A và B, in ma trận C lên màn hình.
Công thức tính các phần tử của ma trận C=A+B. C[i,j] = A[i, j] + B[i, j] với i = 1,..,N, và j=1,..,M Chơng trình nh sau: PROGRAM VIDU46; {tính tổng hai ma trận} Var A, B, C: Array [1..10, 1..10] of Real; i, j, N, M: Integer; Begin Repeat Write (‘Nhập số hàng N, số cột M: ’); Readln (N, M);
Until (N>0) and (N<11) and (M>0) and (M<11); For i:=1 to N do For j:=1 to M do Begin Write (‘Nhập A[‘,i,’,‘,j,’]: ’); Readln (A[i, j]); End; {nhập B và tính C luôn} For i:=1 to N do For j:=1 to M do Begin Write (‘Nhập B‘,i,’,‘,j,’]:’); Readln (B[i, j]);
End;
{In ma trận C lên màn hình} Writeln (‘Ma trận C là:’); For i:=1 to N do
Begin
For j:=1 to M do write (C[i, j]:3:1); Writeln;
End; Readln; End.
Ví dụ 5: Tìm số lớn nhất (số nhỏ nhất) trong ma trận A.
Giả sử A là ma trận N hàng, M cột, và Max là biến chứa số lớn nhất phải tìm. Khởi đầu ga gán A[1,1] cho Max, sau đó duyệt tất cả các phần tử của ma trận, nếu phần tử nào lớn hơn Max thì lu nó vào