
Bài viết này sẽ hướng dẫn bạn cách giải quyết bài toán tìm số lớn nhất và tìm số nhỏ nhất trong một danh sách (list) bằng ngôn ngữ lập trình Python, từ cách dùng hàm có sẵn đến việc tự xây dựng thuật toán duyệt mảng nền tảng.
💡 Trả lời nhanh: Để tìm số lớn nhất và nhỏ nhất trong một list Python, cách ngắn gọn nhất là sử dụng hai hàm built-in max(list) và min(list). Nếu bạn muốn tối ưu tốc độ cho danh sách khổng lồ hoặc đang ôn luyện tư duy thuật toán, hãy khởi tạo hai biến max_val và min_val bằng phần tử đầu tiên, sau đó dùng một vòng lặp for duyệt qua danh sách đúng một lần để cập nhật giá trị.
Đề bài
Input: Một danh sách (list) có thể chứa các số nguyên (int) hoặc số thực (float). Danh sách này có thể rỗng hoặc vô tình chứa các kiểu dữ liệu không hợp lệ như chuỗi (string) trong môi trường thực tế.
Output: Một tuple chứa hai giá trị: số lớn nhất và số nhỏ nhất được trích xuất từ danh sách đầu vào.
Ràng buộc: Cần xử lý được trường hợp danh sách rỗng (tránh chương trình bị crash) và bỏ qua các phần tử không phải là số (áp dụng cho mức độ lập trình trung cấp).
Phân tích
Bài toán tìm số lớn nhất và tìm số nhỏ nhất trong Python yêu cầu trích xuất giá trị cực đại và cực tiểu từ danh sách, thường được giải quyết bằng hàm built-in max() và min(), hoặc tự thiết kế thuật toán duyệt mảng một lần (1-pass) để rèn luyện tư duy.
Về mặt bản chất, để biết một phần tử có phải là lớn nhất hay nhỏ nhất hay không, máy tính bắt buộc phải so sánh nó với các phần tử khác. Sự khác biệt giữa các cách tiếp cận chủ yếu nằm ở việc chúng ta nhờ Python tự làm điều đó ngầm định dưới nền (C-level) hay tự tay viết logic so sánh từng bước một. Đầu vào của chúng ta là một cấu trúc dữ liệu tuyến tính, do đó thời gian thực thi lý tưởng nhất sẽ tỷ lệ thuận với số lượng phần tử (độ phức tạp O(N)).
📌 Góc nhìn thực tế: Trong thực tế giảng dạy, tôi thấy sinh viên năm 1 CNTT hay nhầm ở chỗ luôn mặc định list truyền vào là một danh sách các số hoàn hảo. Tuy nhiên, khi làm dự án thực tế hoặc lấy dữ liệu từ API, list của bạn hoàn toàn có thể rỗng [] hoặc chứa các giá trị nhiễu như [1, 5, "N/A", 9]. Nếu không có bước xác thực (validation), code sẽ ngay lập tức văng lỗi.
Giả định
Trong bài viết này, chúng ta giả định rằng danh sách đầu vào có thể bị bẩn (chứa string hoặc None) và có thể rỗng. Do đó, cả hai cách giải dưới đây đều sẽ đi kèm với một bước lọc dữ liệu sử dụng list comprehension và cơ chế bắt lỗi try...except để đảm bảo tính chuẩn xác cho chương trình.
Cách giải 1: Sử dụng hàm built-in max() và min()
Sử dụng hàm built-in max() và min() là cách tiếp cận chuẩn Pythonic, tận dụng tối đa các thư viện cốt lõi đã được tối ưu hóa bằng ngôn ngữ C bên dưới của Python.
Ý tưởng
Thay vì phải tự viết logic so sánh, chúng ta giao phó hoàn toàn công việc này cho hai hàm có sẵn của Python. Tuy nhiên, để đảm bảo chương trình không bị lỗi khi gặp dữ liệu rác, chúng ta sẽ kết hợp hàm isinstance() để lọc ra các phần tử thực sự là số (integer hoặc float) trước khi ném danh sách vào hàm tính toán. Nếu danh sách sau khi lọc bị rỗng, chương trình sẽ chủ động trả về thông báo lỗi thân thiện thay vì crash đột ngột.
Các bước
-
Làm sạch danh sách: Tạo một list mới chỉ chứa các phần tử là số (
inthoặcfloat). -
Kiểm tra danh sách rỗng: Nếu list vừa làm sạch không có phần tử nào, ném ra ngoại lệ
ValueErrorvới thông báo tùy chỉnh. -
Gọi hàm
max()trên danh sách đã làm sạch để lấy số lớn nhất. -
Gọi hàm
min()trên danh sách đã làm sạch để lấy số nhỏ nhất. -
Trả về kết quả dưới dạng một tuple
(gia_tri_max, gia_tri_min).
Minh họa tay
Giả sử chúng ta có danh sách đầu vào: lst = [4, "lỗi", -1, 7, 9]
-
Bước 1: Lọc danh sách
[x for x in lst if isinstance(x, (int, float))]->cleaned_lst = [4, -1, 7, 9] -
Bước 2: Kiểm tra
not cleaned_lst-> Sai (danh sách có 4 phần tử), đi tiếp. -
Bước 3: Gọi
max([4, -1, 7, 9])-> Kết quả lưu vào biếnmax_vallà9. -
Bước 4: Gọi
min([4, -1, 7, 9])-> Kết quả lưu vào biếnmin_vallà-1. -
Bước 5: Trả về
(9, -1).
Đánh giá
-
Phù hợp người mới vì: Cú pháp trực quan, dễ đọc, thể hiện rõ tư duy “tái sử dụng” mã nguồn (code reuse) trong Python.
-
Ưu điểm: Viết code cực nhanh, code ngắn gọn, ít rủi ro sinh ra lỗi logic ngớ ngẩn (bug).
-
Nhược điểm: Phải duyệt qua danh sách tổng cộng 3 lần (1 lần lọc, 1 lần tìm max, 1 lần tìm min).
-
Độ phức tạp:
O(N) thời gian/O(N) bộ nhớ(do tạo ra một danh sách phụ để chứa dữ liệu đã làm sạch).
Cách giải 2: Tự viết thuật toán vòng lặp for
Khác với Cách 1, cách này yêu cầu chúng ta tự thiết kế logic so sánh bằng tay thông qua một vòng lặp for, giúp duyệt mảng và cập nhật kết quả đồng thời chỉ trong một lần duy nhất.
Ý tưởng
Khác với Cách 1 ở chỗ chúng ta không gọi hai hàm tách biệt. Thuật toán này sử dụng kỹ thuật “cắm cờ” (flag) hoặc “giả định ban đầu”. Chúng ta sẽ giả định phần tử hợp lệ đầu tiên chính là số lớn nhất và đồng thời cũng là số nhỏ nhất. Sau đó, duyệt qua các phần tử còn lại; nếu gặp số lớn hơn max_val thì cập nhật lại max_val, nếu gặp số nhỏ hơn min_val thì cập nhật lại min_val. Bằng cách này, chúng ta gộp chung 2 nhiệm vụ vào một vòng lặp.
Các bước
-
Khởi tạo một generator hoặc một list tạm để lấy các giá trị thực sự là số.
-
Kiểm tra nếu không có số nào hợp lệ, báo lỗi
ValueError. -
Lấy phần tử hợp lệ đầu tiên gán cho hai biến
max_valvàmin_val. -
Mở vòng lặp
forchạy từ phần tử thứ hai trở đi. -
Trong vòng lặp: So sánh phần tử hiện tại với
max_valvàmin_val, cập nhật hai biến này nếu điều kiện thỏa mãn. -
Kết thúc vòng lặp, trả về
(max_val, min_val).
Minh họa tay
Giả sử danh sách đã làm sạch là cleaned = [4, -1, 7, 9]
-
Bắt đầu: Gán
max_val = 4,min_val = 4. -
Lần lặp 1 (x = -1):
-
-1 có lớn hơn 4? Không.
-
-1 có nhỏ hơn 4? Có -> Cập nhật
min_val = -1.
-
-
Lần lặp 2 (x = 7):
-
7 có lớn hơn 4? Có -> Cập nhật
max_val = 7. -
7 có nhỏ hơn -1? Không.
-
-
Lần lặp 3 (x = 9):
-
9 có lớn hơn 7? Có -> Cập nhật
max_val = 9. -
9 có nhỏ hơn -1? Không.
-
-
Kết thúc: Trả về
(9, -1).
Khi nào nên dùng Cách 2?
Bạn nên áp dụng cách này khi đang học môn Cấu trúc dữ liệu & Giải thuật, đi phỏng vấn xin việc (nơi nhà tuyển dụng cấm dùng built-in), hoặc khi phải xử lý luồng dữ liệu cực kỳ khổng lồ (streaming data) mà việc duyệt lại 2-3 lần (như Cách 1) gây lãng phí CPU nghiêm trọng.
Đánh giá
-
Ưu điểm: Giúp lập trình viên rèn luyện tư duy nền tảng vững chắc, linh hoạt kiểm soát từng bước, tối ưu số lần duyệt (chỉ 1 pass).
-
Nhược điểm: Code dài hơn, dễ bị lỗi logic nếu khởi tạo biến sai cách (ví dụ gán
max_val = 0). -
Độ phức tạp:
O(N) thời gian/O(1) bộ nhớ(không cần tạo list phụ nếu dùng generator, tối ưu bộ nhớ cực tốt).
So sánh nhanh 2 cách
Bảng này giúp bạn quyết định nên dùng cách nào mà không cần đọc lại toàn bài.
| Tiêu chí | Cách 1: Sử dụng hàm built-in max() và min() | Cách 2: Tự viết thuật toán vòng lặp for (1-pass) |
| Ý tưởng cốt lõi | Dùng hàm có sẵn của Python để trích xuất | Duyệt list, cập nhật biến max_val/min_val liên tục |
| Độ phức tạp | O(N) thời gian / O(N) bộ nhớ | O(N) thời gian / O(1) bộ nhớ |
| Dễ đọc / dễ hiểu | ★★★★★ | ★★★☆☆ |
| Hiệu năng tối ưu bộ nhớ | ★★★☆☆ | ★★★★★ |
| Phù hợp khi | Làm dự án thực tế, cần code nhanh gọn | Ôn thi thuật toán, phỏng vấn, dữ liệu siêu lớn |
| Không phù hợp khi | Bị cấm dùng hàm built-in trong bài tập | Cần hoàn thành tính năng nhanh, ít thời gian test |
Code đầy đủ
Cách 1 — Sử dụng hàm built-in max() và min():

# Tên biến nhất quán với phần minh họa tay
def tim_max_min_builtin(lst):
"""
Hàm này lọc các phần tử là số trong list,
sau đó trả về một tuple chứa (số lớn nhất, số nhỏ nhất).
"""
# Bước 1: Lọc dữ liệu hợp lệ (int, float)
cleaned_lst = [x for x in lst if isinstance(x, (int, float))]
# Bước 2: Kiểm tra list rỗng
if not cleaned_lst:
raise ValueError("Danh sách không chứa số hợp lệ hoặc bị rỗng.")
# Bước 3 & 4: Dùng hàm built-in
max_val = max(cleaned_lst)
min_val = min(cleaned_lst)
# Bước 5: Trả về tuple
return (max_val, min_val)
# --- TEST NHANH (xóa hoặc comment lại trước khi đăng) ---
# assert tim_max_min_builtin([4, "lỗi", -1, 7, 9]) == (9, -1)
# print("Test Cách 1 pass!")
if __name__ == "__main__":
try:
sample_list = [4, -1, "test", 7.5, 9]
ket_qua = tim_max_min_builtin(sample_list)
print(f"Cách 1 -> Số lớn nhất: {ket_qua[0]}, Số nhỏ nhất: {ket_qua[1]}")
except ValueError as e:
print("Lỗi:", e)
Cách 2 — Tự viết thuật toán vòng lặp for (1-pass):

# Điểm khác với Cách 1: Chỉ duyệt qua danh sách đã làm sạch đúng một lần bằng vòng lặp.
def tim_max_min_loop(lst):
"""Lọc dữ liệu và duyệt 1 lần để tìm max, min."""
# Lọc thành một list chứa các số hợp lệ
cleaned_lst = [x for x in lst if isinstance(x, (int, float))]
if not cleaned_lst:
raise ValueError("Danh sách rỗng hoặc không có số.")
# Khởi tạo giá trị ban đầu là phần tử hợp lệ đầu tiên
max_val = cleaned_lst[0]
min_val = cleaned_lst[0]
# Duyệt từ phần tử thứ 2 trở đi
for x in cleaned_lst[1:]:
if x > max_val:
max_val = x
elif x < min_val:
min_val = x
return (max_val, min_val)
# --- TEST NHANH ---
# assert tim_max_min_loop([-10, -5, -100]) == (-5, -100)
# print("Test Cách 2 pass!")
if __name__ == "__main__":
try:
sample_list = [4, -1, "test", 7.5, 9]
ket_qua = tim_max_min_loop(sample_list)
print(f"Cách 2 -> Số lớn nhất: {ket_qua[0]}, Số nhỏ nhất: {ket_qua[1]}")
except ValueError as e:
print("Lỗi:", e)
Ví dụ chạy thử
| STT | Input | Output | Giải thích |
| 1 | [4, -1, 7, 9] |
(9, -1) |
Dữ liệu chuẩn, list toàn số nguyên bình thường. |
| 2 | [-5, -10, -2] |
(-2, -10) |
List chứa toàn số âm. Số lớn nhất là -2 vì nó gần điểm 0 nhất. |
| 3 | [4, "A", -1, None] |
(4, -1) |
Mixed types. Code đã tự động lọc bỏ "A" và None trước khi tìm. |
| 4 | [] hoặc ["A", "B"] |
ValueError |
Danh sách bị rỗng hoặc không có số nào hợp lệ, kích hoạt Exception bảo vệ code. |
Lỗi thường gặp
Lỗi 1: Gặp lỗi ValueError khi truyền list rỗng
Truyền một danh sách không có phần tử nào vào hàm tìm kiếm sẽ khiến Python bối rối vì không có điểm mốc để so sánh.
Hiện tượng: Output văng lỗi ValueError: max() arg is an empty sequence thay vì in ra kết quả.
Nguyên nhân: Vì hàm built-in max() và min() không biết phải trả về giá trị gì khi danh sách trống rỗng. Bạn phải kiểm tra độ dài danh sách trước khi gọi hàm hoặc dùng tham số default.
Code sai:
# output sai là: ValueError: max() arg is an empty sequence
danh_sach = []
lon_nhat = max(danh_sach)
Code đúng:
# output đúng là: None (không bị crash)
danh_sach = []
lon_nhat = max(danh_sach, default=None)
Lỗi 2: Khởi tạo giá trị ban đầu là số 0 cho thuật toán duyệt tay
Đây là lỗi logic kinh điển nhất của sinh viên năm nhất khi viết thuật toán vòng lặp, dẫn đến kết quả sai hoàn toàn với danh sách số âm.
Hiện tượng: Output ra 0 là số lớn nhất thay vì -5.
Nguyên nhân: Vì bạn gán max_val = 0 trước khi lặp. Khi danh sách toàn số âm (như [-10, -5]), không có phần tử nào lớn hơn 0, nên biến max_val bị kẹt ở mức 0 – một con số thậm chí không tồn tại trong danh sách.
Code sai:
# output sai là: max_val = 0
lst = [-10, -5, -20]
max_val = 0
for x in lst:
if x > max_val: max_val = x
Code đúng:
# output đúng là: max_val = -5
lst = [-10, -5, -20]
max_val = lst[0] # Lấy chính phần tử đầu làm mốc
for x in lst:
if x > max_val: max_val = x
Lỗi 3: Lỗi TypeError khi danh sách chứa chuỗi văn bản
Khi danh sách bị trộn lẫn dữ liệu nhiễu, thuật toán so sánh lớn bé sẽ bị phá vỡ.
Hiện tượng: Output văng lỗi TypeError: '>' not supported between instances of 'str' and 'int'.
Nguyên nhân: Vì Python là ngôn ngữ strongly typed, nó cấm việc so sánh toán học một con số với một chuỗi ký tự. Hệ thống không thể biết 10 hay "Apple" lớn hơn.
Code sai:
# output sai là: TypeError
lst = [10, 5, "Apple", 20]
print(max(lst))
Code đúng:
# output đúng là: 20
lst = [10, 5, "Apple", 20]
cleaned = [x for x in lst if isinstance(x, (int, float))]
print(max(cleaned))
Lỗi 4: Vô tình ghi đè tên hàm built-in (Shadowing built-ins)
Lỗi này làm mất đi khả năng sử dụng hàm có sẵn của Python trong toàn bộ file code của bạn.
Hiện tượng: Output báo lỗi TypeError: 'int' object is not callable khi bạn gọi hàm max() ở dòng tiếp theo.
Nguyên nhân: Vì trước đó bạn đã đặt tên một biến là max = 100. Lúc này, từ khóa max trong Python không còn là một hàm nữa mà trở thành số nguyên 100. Khi bạn gọi max(list), Python hiểu là bạn đang gọi 100(list), gây ra lỗi.
Code sai:
# output sai là: TypeError: 'int' object is not callable
max = 50
lst = [1, 2, 3]
ket_qua = max(lst)
Code đúng:
# output đúng là: 3
gia_tri_max = 50 # Đổi tên biến, không dùng từ khóa max
lst = [1, 2, 3]
ket_qua = max(lst)
Lỗi 5: Quên không gán biến khi nhận nhiều giá trị (Unpacking Error)
Lỗi này xảy ra khi bạn định nghĩa hàm trả về hai kết quả (tuple) nhưng lại chỉ dùng một biến để đón dữ liệu ở ngoài.
Hiện tượng: Output in ra cả một cụm (9, -1) thay vì lấy được đúng số lớn nhất như mong muốn.
Nguyên nhân: Vì hàm của bạn đang return (max_val, min_val), tức là cấu trúc Tuple. Nếu bạn chỉ gán kq = tim_max_min(), biến kq sẽ ôm toàn bộ Tuple đó. Bạn cần “mở gói” (unpack) nó bằng 2 biến riêng biệt.
Code sai:
# output sai là: (9, -1) (Bị dính dạng tuple)
kq = tim_max_min_loop([4, -1, 7, 9])
print("Max là:", kq)
Code đúng:
# output đúng là: 9
max_v, min_v = tim_max_min_loop([4, -1, 7, 9])
print("Max là:", max_v)
Câu hỏi thường gặp
Bài toán tìm số lớn nhất và nhỏ nhất trong list là gì?
Tìm số lớn nhất và nhỏ nhất trong list là quá trình xử lý đầu vào của một danh sách chứa các giá trị số, tiến hành so sánh chúng với nhau nhằm xác định và trích xuất ra phần tử có giá trị toán học cao nhất (cực đại) và phần tử có giá trị thấp nhất (cực tiểu) để phục vụ cho các phép toán thống kê sau đó.
Hàm max() và min() hoạt động như thế nào đằng sau hậu trường?
Về mặt khái niệm liên quan đến cấu trúc của Python, hai hàm built-in này sử dụng vòng lặp viết bằng ngôn ngữ C ở bên dưới, tuần tự đi qua toàn bộ danh sách để thực hiện phép so sánh. Do đó, chúng chạy rất nhanh và tối ưu, sở hữu độ phức tạp thời gian luôn là tuyến tính O(N).
Làm thế nào để tìm số lớn nhất trong Python nếu danh sách bị lồng nhau?
Nếu bạn có một danh sách lồng nhau (nested list) dạng ma trận, bạn không thể dùng hàm max() trực tiếp ngay được. Trước tiên, bạn phải dùng kỹ thuật “flatten” (làm phẳng danh sách) bằng list comprehension hoặc module itertools.chain để gộp tất cả thành list một chiều, sau đó mới tiến hành gọi hàm max().
Cách tự viết thuật toán tìm min max Python có phức tạp không?
Tự viết vòng lặp tìm giá trị không hề phức tạp nếu bạn nhớ rõ quy tắc khởi tạo biến: luôn gán giá trị mốc ban đầu bằng chính phần tử đầu tiên của danh sách, ví dụ max_val = lst[0]. Sau đó, chỉ cần một vòng lặp for và hai lệnh if/elif là bạn có thể hoàn thành việc so sánh.
Nên dùng hàm built-in hay tự viết vòng lặp để tìm max min trong mảng Python?
Nếu bạn xử lý bài toán trên môi trường sản phẩm thực tế, hãy dùng built-in max() vì nó an toàn và tốn ít dòng code. Nhưng nếu bạn phải tham gia kỳ thi cấu trúc dữ liệu, hoặc file log bạn đang xử lý có kích thước hàng chục Gigabytes, hãy tự viết vòng lặp một lần (1-pass) để giảm thiểu áp lực cho CPU.
Sự khác biệt giữa việc gọi max() hai lần và dùng một vòng lặp for là gì?
Sự khác biệt cốt lõi nằm ở số lần duyệt qua danh sách. Việc gọi hai hàm có sẵn sẽ ép Python phải đi qua lại danh sách tổng cộng hai lần. Trong khi đó, việc tự viết một vòng lặp for duy nhất sẽ giúp chương trình chỉ cần duyệt mảng đúng một lần là thu thập đủ cả hai kết quả.
Tại sao code tìm số lớn nhất bị lỗi khi danh sách có số và chữ?
Lỗi so sánh kiểu dữ liệu diễn ra vì Python là một ngôn ngữ có tính an toàn cao (strongly-typed). Nó nghiêm cấm việc dùng toán tử so sánh > và < giữa kiểu chuỗi văn bản (str) và kiểu số (int). Do đó, bạn bắt buộc phải có bước tiền xử lý, lọc bỏ các chuỗi ra khỏi mảng trước khi tiến hành tính toán.
Kết luận
Bài toán tìm số lớn nhất và tìm số nhỏ nhất trong list Python tuy đơn giản nhưng lại chứa đựng nhiều bẫy ngầm về kiểu dữ liệu và danh sách rỗng. Hãy luôn ưu tiên dùng Cách 1 (hàm max()/min()) cho công việc hàng ngày để đảm bảo code gọn gàng chuẩn Pythonic. Chỉ chuyển sang Cách 2 khi bạn cần tối đa hóa hiệu năng cho các luồng dữ liệu cực lớn nhằm tiết kiệm chu kỳ quét mảng của CPU. Đừng quên bước xác thực đầu vào bằng try...except để chương trình luôn vững chãi trước mọi lỗi phát sinh nhé!
Các khóa học liên quan:
Một số sản phẩm từ Python:
Một số sách lập trình Python bạn hãy tham khảo