Bài 9.2. Các kỹ thuật duyệt mảng 2 chiều

Bài 9.2. Các kỹ thuật duyệt mảng 2 chiều
Chào mừng các bạn đã quay trở lại với series blog về Cấu trúc dữ liệu và Giải thuật của FullhouseDev! Sau khi tìm hiểu về mảng một chiều, chúng ta cùng nâng cấp lên một cấu trúc dữ liệu phổ biến và mạnh mẽ hơn: mảng 2 chiều, hay còn gọi là ma trận.
Mảng 2 chiều cho phép chúng ta lưu trữ dữ liệu dưới dạng bảng (table) hoặc lưới (grid), rất hữu ích trong việc biểu diễn các mối quan hệ không gian 2D như bản đồ, hình ảnh pixel, bảng tính, hoặc trạng thái của một số trò chơi (ví dụ: cờ vua, caro). Để làm việc với dữ liệu trong mảng 2 chiều, một thao tác cơ bản và quan trọng là duyệt (traversal) qua tất cả các phần tử của nó.
Tưởng chừng việc duyệt mảng 2 chiều chỉ đơn giản là dùng hai vòng lặp lồng nhau, nhưng thực tế có nhiều kỹ thuật duyệt khác nhau, mỗi kỹ thuật lại phù hợp với những mục đích và bài toán cụ thể. Việc nắm vững các kỹ thuật này sẽ giúp bạn xử lý dữ liệu 2D hiệu quả và linh hoạt hơn rất nhiều.
Trong bài viết này, chúng ta sẽ cùng nhau khám phá các kỹ thuật duyệt mảng 2 chiều từ cơ bản đến phức tạp, cùng với các ví dụ minh họa chi tiết bằng ngôn ngữ C++.
Hãy cùng bắt đầu hành trình khám phá thế giới 2D này nhé!
Duyệt Theo Hàng (Row-by-Row Traversal)
Đây là phương pháp duyệt mảng 2 chiều phổ biến nhất và tự nhiên nhất. Nó mô phỏng cách chúng ta đọc một cuốn sách: đi hết hàng này rồi mới sang hàng kế tiếp.
- Ý tưởng: Chúng ta sẽ sử dụng hai vòng lặp lồng nhau. Vòng lặp ngoài sẽ duyệt qua từng hàng của mảng (từ hàng 0 đến hàng cuối cùng), và vòng lặp trong sẽ duyệt qua từng cột trong hàng hiện tại (từ cột 0 đến cột cuối cùng).
Thứ tự duyệt sẽ là: (0,0), (0,1), ..., (0,cols-1), (1,0), (1,1), ..., (1,cols-1), ..., (rows-1, 0), ..., (rows-1, cols-1)
.
Hãy xem ví dụ C++ minh họa:
#include <iostream>
#include <vector>
int main() {
// Khởi tạo mảng 2 chiều (ví dụ 3x3)
std::vector<std::vector<int>> matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int rows = matrix.size(); // Số hàng
if (rows == 0) { // Xử lý trường hợp mảng rỗng
std::cout << "Mang rong!" << std::endl;
return 0;
}
int cols = matrix[0].size(); // Số cột (giả sử tất cả các hàng đều có cùng số cột)
std::cout << "--- Duyet theo hang (Row-by-Row Traversal) ---" << std::endl;
// Vong lap ngoai duyet qua cac hang (chi so i)
for (int i = 0; i < rows; ++i) {
// Vong lap trong duyet qua cac cot trong hang hien tai (chi so j)
for (int j = 0; j < cols; ++j) {
std::cout << matrix[i][j] << " "; // Truy cap phan tu tai (i, j)
}
// Xuong dong sau khi ket thuc mot hang
std::cout << std::endl;
}
std::cout << std::endl;
return 0;
}
Giải thích code:
- Chúng ta sử dụng
std::vector<std::vector<int>>
để tạo mảng 2 chiều động, có thể thay đổi kích thước. Đây là cách phổ biến trong C++ hiện đại. rows = matrix.size();
lấy số lượng vector con bên trong, chính là số hàng.cols = matrix[0].size();
lấy kích thước của vector con đầu tiên, chính là số cột. (Chúng ta cần đảm bảo mảng không rỗng trước khi truy cậpmatrix[0]
).- Vòng lặp
for (int i = 0; i < rows; ++i)
điều khiển việc chuyển từ hàng này sang hàng khác. Biếni
là chỉ số hàng hiện tại, chạy từ 0 đếnrows - 1
. - Vòng lặp
for (int j = 0; j < cols; ++j)
lồng bên trong điều khiển việc duyệt qua các cột của hàngi
. Biếnj
là chỉ số cột hiện tại, chạy từ 0 đếncols - 1
. matrix[i][j]
là cách truy cập phần tử tại hàng có chỉ sối
và cột có chỉ sốj
.- Lệnh
std::cout << std::endl;
sau vòng lặp trong giúp in các phần tử của mỗi hàng trên một dòng riêng biệt, dễ hình dung hơn thứ tự duyệt.
Kết quả của đoạn code trên sẽ in ra ma trận theo đúng cấu trúc ban đầu:
--- Duyet theo hang (Row-by-Row Traversal) ---
1 2 3
4 5 6
7 8 9
Kỹ thuật này đơn giản, hiệu quả, và thường là mặc định khi bạn cần xử lý từng phần tử theo thứ tự đọc thông thường.
Duyệt Theo Cột (Column-by-Column Traversal)
Đây là phương pháp đối lập với duyệt theo hàng. Thay vì đi hết hàng này sang hàng khác, chúng ta sẽ đi hết cột này sang cột khác.
- Ý tưởng: Vẫn sử dụng hai vòng lặp lồng nhau, nhưng vai trò của chúng bị đảo ngược. Vòng lặp ngoài sẽ duyệt qua từng cột của mảng (từ cột 0 đến cột cuối cùng), và vòng lặp trong sẽ duyệt qua từng hàng trong cột hiện tại (từ hàng 0 đến hàng cuối cùng).
Thứ tự duyệt sẽ là: (0,0), (1,0), ..., (rows-1,0), (0,1), (1,1), ..., (rows-1,1), ..., (0, cols-1), ..., (rows-1, cols-1)
.
Ví dụ C++:
#include <iostream>
#include <vector>
int main() {
// Khởi tạo mảng 2 chiều (ví dụ 3x3)
std::vector<std::vector<int>> matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int rows = matrix.size();
if (rows == 0) {
std::cout << "Mang rong!" << std::endl;
return 0;
}
int cols = matrix[0].size();
std::cout << "--- Duyet theo cot (Column-by-Column Traversal) ---" << std::endl;
// Vong lap ngoai duyet qua cac cot (chi so j)
for (int j = 0; j < cols; ++j) {
// Vong lap trong duyet qua cac hang trong cot hien tai (chi so i)
for (int i = 0; i < rows; ++i) {
std::cout << matrix[i][j] << " "; // Truy cap phan tu tai (i, j)
}
// Co the xuong dong sau moi cot neu can, hoac chi in tren 1 dong
// std::cout << std::endl;
}
std::cout << std::endl << std::endl;
return 0;
}
Giải thích code:
- Cấu trúc code tương tự như duyệt theo hàng, nhưng thứ tự của vòng lặp ngoài và vòng lặp trong đã được đổi chỗ.
- Vòng lặp ngoài
for (int j = 0; j < cols; ++j)
lặp qua các chỉ số cộtj
từ 0 đếncols - 1
. - Vòng lặp trong
for (int i = 0; i < rows; ++i)
lặp qua các chỉ số hàngi
từ 0 đếnrows - 1
cho cộtj
hiện tại. - Việc truy cập vẫn là
matrix[i][j]
, nhưng do thứ tự vòng lặp, chúng ta sẽ truy cậpmatrix[0][0]
,matrix[1][0]
,matrix[2][0]
(cột 0), sau đómatrix[0][1]
,matrix[1][1]
,matrix[2][1]
(cột 1), v.v.
Kết quả của đoạn code trên sẽ in ra các phần tử theo thứ tự cột (trên cùng một dòng trong ví dụ này):
--- Duyet theo cot (Column-by-Column Traversal) ---
1 4 7 2 5 8 3 6 9
Duyệt theo cột hữu ích khi bài toán yêu cầu xử lý dữ liệu theo từng cột, ví dụ như tính tổng các phần tử trên mỗi cột, hoặc khi dữ liệu được tổ chức theo cột trong bộ nhớ (ít phổ biến với mảng trong C++).
Duyệt Theo Đường Chéo (Diagonal Traversal)
Duyệt theo đường chéo phức tạp hơn một chút so với hai kỹ thuật trên, vì các phần tử trên cùng một đường chéo không liền kề nhau theo chỉ số hàng hoặc cột theo cách đơn giản. Có hai loại đường chéo chính mà chúng ta thường quan tâm: đường chéo chính và đường chéo phụ.
Đường Chéo Chính (Main Diagonal)
Đường chéo chính bao gồm các phần tử mà chỉ số hàng và chỉ số cột bằng nhau. Ví dụ: matrix[0][0]
, matrix[1][1]
, matrix[2][2]
, v.v. Kỹ thuật này áp dụng đơn giản nhất cho ma trận vuông (số hàng bằng số cột).
- Ý tưởng: Chỉ cần một vòng lặp duy nhất từ 0 đến kích thước (số hàng hoặc số cột) - 1, và truy cập phần tử
matrix[i][i]
.
Ví dụ C++ (cho ma trận vuông):
#include <iostream>
#include <vector>
#include <algorithm> // For std::min (optional, but good practice for non-square)
int main() {
// Khởi tạo ma trận vuông (ví dụ 3x3)
std::vector<std::vector<int>> matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int rows = matrix.size();
if (rows == 0) {
std::cout << "Mang rong!" << std::endl;
return 0;
}
int cols = matrix[0].size();
std::cout << "--- Duyet duong cheo chinh (Main Diagonal) ---" << std::endl;
// Duyet tren duong cheo chinh. Chi ap dung don gian cho ma tran vuong.
// Neu ma tran khong vuong, chi co the duyet phan chung nho nhat.
int size = std::min(rows, cols);
for (int i = 0; i < size; ++i) {
std::cout << matrix[i][i] << " "; // Truy cap phan tu tai (i, i)
}
std::cout << std::endl << std::endl;
return 0;
}
Giải thích code:
- Chúng ta sử dụng
std::min(rows, cols)
để lấy kích thước nhỏ nhất giữa số hàng và số cột. Điều này đảm bảo vòng lặp không đi ra ngoài giới hạn của mảng, ngay cả khi nó không vuông (chỉ duyệt được phần đường chéo chính nằm gọn trong cả hai chiều). - Vòng lặp
for (int i = 0; i < size; ++i)
lặp qua chỉ sối
. matrix[i][i]
truy cập phần tử tại vị trí(i, i)
, tức là các phần tử trên đường chéo chính.
Kết quả (với ma trận 3x3):
--- Duyet duong cheo chinh (Main Diagonal) ---
1 5 9
Duyệt Các Đường Chéo Phụ (Anti-Diagonals)
Các đường chéo phụ chạy theo hướng ngược lại, từ góc trên bên phải xuống góc dưới bên trái. Các phần tử trên cùng một đường chéo phụ có tổng chỉ số hàng và chỉ số cột là một hằng số. Ví dụ, trong ma trận 3x3, các đường chéo phụ có tổng chỉ số là:
- Tổng 0:
(0,0)
- Tổng 1:
(0,1), (1,0)
- Tổng 2:
(0,2), (1,1), (2,0)
- Tổng 3:
(1,2), (2,1)
- Tổng 4:
(2,2)
Với ma trận kích thước M x N, tổng chỉ số i + j
sẽ chạy từ 0 (cho matrix[0][0]
) đến (M-1) + (N-1) = M + N - 2
(cho matrix[M-1][N-1]
).
- Ý tưởng: Vòng lặp ngoài duyệt qua tất cả các giá trị tổng chỉ số có thể có (
k
từ 0 đếnrows + cols - 2
). Vòng lặp trong duyệt qua các chỉ số hàngi
(hoặc cộtj
) sao choi + j = k
. Nếu ta chọn duyệt theo hàngi
, thì chỉ số cột tương ứng làj = k - i
. Chúng ta cần đảm bảo cải
vàj
đều nằm trong phạm vi hợp lệ của mảng.
Ví dụ C++ (cho ma trận bất kỳ):
#include <iostream>
#include <vector>
#include <algorithm> // For std::max and std::min
int main() {
// Khởi tạo mảng 2 chiều (ví dụ 4x3)
std::vector<std::vector<int>> matrix = {
{ 1, 2, 3},
{ 4, 5, 6},
{ 7, 8, 9},
{10, 11, 12}
};
int rows = matrix.size();
if (rows == 0) {
std::cout << "Mang rong!" << std::endl;
return 0;
}
int cols = matrix[0].size();
std::cout << "--- Duyet theo duong cheo phu (Anti-Diagonals) ---" << std::endl;
// Tong chi so i + j = k. k chay tu 0 den rows + cols - 2
for (int k = 0; k <= rows + cols - 2; ++k) {
// Duyet qua cac hang i. Chi so cot j = k - i.
// i phai tu 0 den rows - 1 => i >= 0 && i < rows
// j = k - i phai tu 0 den cols - 1 => k - i >= 0 && k - i < cols
// => i <= k && i > k - cols
// Ket hop cac dieu kien: i phai nam trong khoang [max(0, k - cols + 1), min(rows - 1, k)]
int start_i = std::max(0, k - cols + 1);
int end_i = std::min(rows - 1, k);
for (int i = start_i; i <= end_i; ++i) {
int j = k - i;
std::cout << matrix[i][j] << " ";
}
// Co the xuong dong sau moi duong cheo phu neu can
// std::cout << std::endl;
}
std::cout << std::endl << std::endl;
return 0;
}
Giải thích code:
- Vòng lặp ngoài
for (int k = 0; k <= rows + cols - 2; ++k)
duyệt qua tất cả các giá trị tổngk
mài + j
có thể nhận. - Đối với mỗi giá trị
k
, vòng lặp trong duyệt qua các giá trịi
(chỉ số hàng). Chỉ số cột tương ứngj
được tính làk - i
. - Điều mấu chốt là xác định phạm vi duyệt của
i
.i
phải đủ lớn đểj = k - i
không âm (k - i >= 0
=>i <= k
), vài
phải đủ nhỏ đểj = k - i
nhỏ hơncols
(k - i < cols
=>i > k - cols
). Kết hợp với giới hạn tự nhiên củai
(từ 0 đếnrows - 1
), phạm vi hợp lệ củai
là từmax(0, k - cols + 1)
đếnmin(rows - 1, k)
. - Các hàm
std::max
vàstd::min
giúp tính toán chính xác các giới hạnstart_i
vàend_i
. matrix[i][j]
truy cập phần tử tại vị trí hợp lệ trên đường chéo phụ có tổng chỉ sốk
.
Kết quả (với ma trận 4x3):
--- Duyet theo duong cheo phu (Anti-Diagonals) ---
1 2 4 3 5 7 6 8 10 9 11 12
Thứ tự duyệt này đi qua từng đường chéo phụ một.
Duyệt Xoắn Ốc (Spiral Traversal)
Duyệt xoắn ốc là một kỹ thuật thú vị và thử thách hơn, nơi chúng ta đi qua các phần tử theo hình xoắn ốc, thường bắt đầu từ góc trên bên trái và di chuyển vào trong cho đến khi duyệt hết mảng.
Ý tưởng: Duy trì bốn biến để đánh dấu các biên của vùng mảng chưa được duyệt:
top
(hàng trên cùng),bottom
(hàng dưới cùng),left
(cột trái cùng),right
(cột phải cùng). Ban đầu, chúng là các chỉ số của các biên ngoài cùng của mảng. Chúng ta lặp đi lặp lại bốn bước:- Duyệt từ trái sang phải dọc theo hàng
top
. Sau khi duyệt xong, tăngtop
lên 1. - Duyệt từ trên xuống dưới dọc theo cột
right
. Sau khi duyệt xong, giảmright
xuống 1. - Duyệt từ phải sang trái dọc theo hàng
bottom
. Sau khi duyệt xong, giảmbottom
xuống 1. - Duyệt từ dưới lên trên dọc theo cột
left
. Sau khi duyệt xong, tăngleft
lên 1.
Sau mỗi bước, chúng ta cần kiểm tra xem vùng chưa duyệt có còn hợp lệ không (tức là
top <= bottom
vàleft <= right
). Nếu không, dừng lại.- Duyệt từ trái sang phải dọc theo hàng
Ví dụ C++:
#include <iostream>
#include <vector>
int main() {
// Khởi tạo mảng 2 chiều (ví dụ 4x4)
std::vector<std::vector<int>> matrix = {
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9, 10, 11, 12},
{13, 14, 15, 16}
};
int rows = matrix.size();
if (rows == 0) {
std::cout << "Mang rong!" << std::endl;
return 0;
}
int cols = matrix[0].size();
std::cout << "--- Duyet xoan oc (Spiral Traversal) ---" << std::endl;
int top = 0, bottom = rows - 1;
int left = 0, right = cols - 1;
while (top <= bottom && left <= right) {
// 1. Duyet tu trai sang phai (tren hang top)
for (int j = left; j <= right; ++j) {
std::cout << matrix[top][j] << " ";
}
top++; // Da duyet xong hang top, tang gioi han top len 1
// Kiem tra dieu kien dung sau moi buoc de xu ly truong hop mang chu nhat hoac le
if (top > bottom || left > right) break;
// 2. Duyet tu tren xuong duoi (tren cot right)
for (int i = top; i <= bottom; ++i) {
std::cout << matrix[i][right] << " ";
}
right--; // Da duyet xong cot right, giam gioi han right xuong 1
// Kiem tra dieu kien dung
if (top > bottom || left > right) break;
// 3. Duyet tu phai sang trai (tren hang bottom)
for (int j = right; j >= left; --j) {
std::cout << matrix[bottom][j] << " ";
}
bottom--; // Da duyet xong hang bottom, giam gioi han bottom xuong 1
// Kiem tra dieu kien dung
if (top > bottom || left > right) break;
// 4. Duyet tu duoi len tren (tren cot left)
for (int i = bottom; i >= top; --i) {
std::cout << matrix[i][left] << " ";
}
left++; // Da duyet xong cot left, tang gioi han left len 1
}
std::cout << std::endl << std::endl;
return 0;
}
Giải thích code:
- Chúng ta khởi tạo 4 biến
top
,bottom
,left
,right
lần lượt bằng các chỉ số của hàng/cột biên ngoài cùng. - Vòng lặp
while (top <= bottom && left <= right)
tiếp tục miễn là còn một vùng (dù chỉ 1x1) chưa được duyệt. - Bên trong vòng
while
là 4 vòng lặpfor
, mỗi vòng lặp thực hiện duyệt theo một hướng và sau đó điều chỉnh biến biên tương ứng để thu hẹp vùng chưa duyệt:- Vòng
for
đầu tiên duyệt hàngtop
từleft
đếnright
, sau đótop
được tăng. - Vòng
for
thứ hai duyệt cộtright
từ hàng mớitop
đếnbottom
, sau đóright
được giảm. - Vòng
for
thứ ba duyệt hàngbottom
từ cột mớiright
vềleft
, sau đóbottom
được giảm. - Vòng
for
cuối cùng duyệt cộtleft
từ hàng mớibottom
vềtop
, sau đóleft
được tăng.
- Vòng
- Điều kiện
if (top > bottom || left > right) break;
sau mỗi vòng lặpfor
rất quan trọng. Nó xử lý các trường hợp khi số hàng hoặc số cột là lẻ, hoặc khi mảng có dạng chữ nhật (không vuông), đảm bảo chúng ta không cố gắng duyệt vào vùng đã bị thu hẹp và thoát khỏi vòng lặpwhile
đúng lúc. - Quá trình lặp lại, vùng chưa duyệt sẽ co lại dần cho đến khi
top > bottom
hoặcleft > right
, lúc đó toàn bộ mảng đã được duyệt.
Kết quả (với ma trận 4x4):
--- Duyet xoan oc (Spiral Traversal) ---
1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10
Kỹ thuật duyệt xoắn ốc thường xuất hiện trong các bài toán phỏng vấn hoặc các tác vụ xử lý hình ảnh đặc thù.
Duyệt Ziczac (Zig-zag Traversal)
Duyệt Ziczac, đôi khi còn gọi là Snake Traversal (duyệt kiểu rắn bò), là một biến thể của duyệt theo hàng nhưng có thêm một "twist" (sự thay đổi hướng).
- Ý tưởng: Duyệt qua từng hàng. Đối với các hàng có chỉ số chẵn (0, 2, 4, ...), duyệt các cột theo thứ tự tăng dần (từ trái sang phải). Đối với các hàng có chỉ số lẻ (1, 3, 5, ...), duyệt các cột theo thứ tự giảm dần (từ phải sang trái).
Thứ tự duyệt sẽ là: (0,0), (0,1), ..., (0,cols-1), (1,cols-1), (1,cols-2), ..., (1,0), (2,0), (2,1), ..., (2,cols-1), ...
.
Ví dụ C++:
#include <iostream>
#include <vector>
int main() {
// Khởi tạo mảng 2 chiều (ví dụ 3x4)
std::vector<std::vector<int>> matrix = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int rows = matrix.size();
if (rows == 0) {
std::cout << "Mang rong!" << std::endl;
return 0;
}
int cols = matrix[0].size();
std::cout << "--- Duyet ziczac (Zig-zag Traversal) ---" << std::endl;
for (int i = 0; i < rows; ++i) { // Duyet qua cac hang
if (i % 2 == 0) {
// Hang co chi so chan (0, 2, 4...): duyet tu trai sang phai
for (int j = 0; j < cols; ++j) {
std::cout << matrix[i][j] << " ";
}
} else {
// Hang co chi so le (1, 3, 5...): duyet tu phai sang trai
for (int j = cols - 1; j >= 0; --j) {
std::cout << matrix[i][j] << " ";
}
}
}
std::cout << std::endl << std::endl;
return 0;
}
Giải thích code:
- Vòng lặp ngoài
for (int i = 0; i < rows; ++i)
duyệt qua các chỉ số hàng. - Bên trong vòng lặp hàng, chúng ta sử dụng điều kiện
if (i % 2 == 0)
để kiểm tra xem chỉ số hàngi
là chẵn hay lẻ. - Nếu
i
là chẵn, chúng ta dùng vòng lặpfor (int j = 0; j < cols; ++j)
để duyệt các cột từ 0 đếncols - 1
. - Nếu
i
là lẻ, chúng ta dùng vòng lặpfor (int j = cols - 1; j >= 0; --j)
để duyệt các cột từcols - 1
về 0. - Trong cả hai trường hợp, chúng ta truy cập phần tử bằng
matrix[i][j]
, vớij
thay đổi theo hướng duyệt của hàng hiện tại.
Kết quả (với ma trận 3x4):
--- Duyet ziczac (Zig-zag Traversal) ---
1 2 3 4 8 7 6 5 9 10 11 12
Bài tập ví dụ:
Ma trận xoáy ốc
Xây dựng ma trận xoáy ốc cấp n.
Input Format
Số nguyên dương n là cấp của ma trận xoáy ốc cần xây dựng.(2≤N≤100)
Constraints
.
Output Format
In ra ma trận xoáy ốc.
Ví dụ:
Dữ liệu vào
4
Dữ liệu ra
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
Hướng dẫn giải bài Ma trận xoáy ốc bằng C++ (ngắn gọn):
Khởi tạo:
- Tạo một ma trận vuông kích thước
n x n
(ví dụ: dùngstd::vector<std::vector<int>>
). - Khởi tạo các biến biên giới:
row_start = 0
,row_end = n-1
,col_start = 0
,col_end = n-1
. - Khởi tạo biến đếm
current_number = 1
.
- Tạo một ma trận vuông kích thước
Vòng lặp điền số:
- Sử dụng vòng lặp
while (row_start <= row_end && col_start <= col_end)
. Vòng lặp này tiếp tục khi vùng làm việc còn hợp lệ. - Trong mỗi lần lặp, thực hiện 4 bước di chuyển theo xoáy ốc:
- Điền từ trái sang phải: Điền các số từ
current_number
vào hàngrow_start
, từ cộtcol_start
đếncol_end
. Tăngcurrent_number
sau mỗi lần điền. Sau khi xong, tăngrow_start
lên 1 (vì hàng trên cùng đã được điền). - Điền từ trên xuống dưới: Điền các số vào cột
col_end
, từ hàngrow_start
đếnrow_end
. Tăngcurrent_number
. Sau khi xong, giảmcol_end
đi 1 (vì cột ngoài cùng bên phải đã được điền). - Điền từ phải sang trái: Kiểm tra điều kiện
if (row_start <= row_end)
trước khi thực hiện. Nếu điều kiện đúng, điền các số vào hàngrow_end
, từ cộtcol_end
vềcol_start
. Tăngcurrent_number
. Sau khi xong, giảmrow_end
đi 1 (vì hàng dưới cùng đã được điền). - Điền từ dưới lên trên: Kiểm tra điều kiện
if (col_start <= col_end)
trước khi thực hiện. Nếu điều kiện đúng, điền các số vào cộtcol_start
, từ hàngrow_end
vềrow_start
. Tăngcurrent_number
. Sau khi xong, tăngcol_start
lên 1 (vì cột ngoài cùng bên trái đã được điền).
- Điền từ trái sang phải: Điền các số từ
- Sử dụng vòng lặp
In kết quả:
- Sau khi vòng lặp kết thúc (khi
current_number
đã điền hết hoặc các biên giới đã giao nhau/vượt qua nhau), in ma trận đã điền theo định dạng yêu cầu (số cách nhau bằng dấu cách, mỗi hàng trên một dòng mới).
- Sau khi vòng lặp kết thúc (khi
Lưu ý: Hai bước kiểm tra điều kiện trước khi điền từ phải sang trái và từ dưới lên trên là quan trọng để xử lý đúng các trường hợp ma trận có kích thước lẻ ở lớp trong cùng.
Comments