Chuyển tới nội dung chính

List (Danh sách)

Giai đoạn 2 – Cấu Trúc Dữ Liệu & Hàm · Mục tiêu: Thành thạo List — cấu trúc dữ liệu linh hoạt và được sử dụng nhiều nhất trong Python.


1. List là gì?

List (danh sách) là cấu trúc dữ liệu có thứ tự, thay đổi được (mutable), và cho phép trùng lặp.

# List lưu trữ nhiều giá trị trong một biến
diem = [8.5, 7.2, 9.0, 6.8]
mon_hoc = ["Toán", "Lý", "Hóa", "Tin"]
hon_hop = [1, "hello", 3.14, True] # Nhiều kiểu dữ liệu
rong = [] # List rỗng

# Tạo list bằng hàm list()
ds = list(range(1, 6)) # [1, 2, 3, 4, 5]
chu = list("Python") # ['P', 'y', 't', 'h', 'o', 'n']
Đặc điểmMô tả
Có thứ tựPhần tử có vị trí (index) cố định
Thay đổi đượcCó thể thêm, xóa, sửa phần tử
Cho phép trùng[1, 2, 2, 3, 3] là hợp lệ
Chứa mọi kiểu[1, "abc", 3.14, True, [1, 2]]

2. Truy cập phần tử

Chỉ mục (Index)

List: ["Toán", "Lý", "Hóa", "Anh", "Tin"]
Index: 0 1 2 3 4
Âm: -5 -4 -3 -2 -1
mon = ["Toán", "Lý", "Hóa", "Anh", "Tin"]

mon[0] # "Toán" – phần tử đầu tiên
mon[2] # "Hóa" – phần tử thứ 3
mon[-1] # "Tin" – phần tử cuối cùng
mon[-2] # "Anh" – phần tử kế cuối

Truy cập index ngoài phạm vi sẽ gây IndexError:

mon[10] # IndexError: list index out of range

Cắt list (Slicing)

Cú pháp: list[start:stop:step]

ds = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

ds[2:5] # [2, 3, 4] – từ index 2 đến 4
ds[:4] # [0, 1, 2, 3] – từ đầu đến index 3
ds[6:] # [6, 7, 8, 9] – từ index 6 đến cuối
ds[1:8:2] # [1, 3, 5, 7] – bước nhảy 2
ds[::-1] # [9, 8, 7, ..., 0] – đảo ngược list
ds[::3] # [0, 3, 6, 9] – mỗi 3 phần tử
Cú phápÝ nghĩa
ds[a:b]Từ a đến b-1
ds[:b]Từ đầu đến b-1
ds[a:]Từ a đến cuối
ds[a:b:n]Từ a đến b-1, bước nhảy n
ds[::-1]Đảo ngược toàn bộ

3. Thêm phần tử

ds = [1, 2, 3]

# append() – thêm 1 phần tử vào CUỐI
ds.append(4) # [1, 2, 3, 4]

# insert() – chèn vào VỊ TRÍ cụ thể
ds.insert(0, 0) # [0, 1, 2, 3, 4]
ds.insert(2, 99) # [0, 1, 99, 2, 3, 4]

# extend() – nối thêm NHIỀU phần tử
ds.extend([5, 6, 7]) # [0, 1, 99, 2, 3, 4, 5, 6, 7]

# Nối bằng toán tử +
ds_moi = [1, 2] + [3, 4] # [1, 2, 3, 4]

# Nhân bản bằng toán tử *
ds_lap = [0] * 5 # [0, 0, 0, 0, 0]

append() vs extend() — Sự khác biệt quan trọng:

a = [1, 2, 3]
a.append([4, 5]) # [1, 2, 3, [4, 5]] ← thêm cả list

b = [1, 2, 3]
b.extend([4, 5]) # [1, 2, 3, 4, 5] ← nối từng phần tử

4. Xóa phần tử

ds = [10, 20, 30, 40, 50, 30]

# remove() – xóa theo GIÁ TRỊ (phần tử đầu tiên tìm thấy)
ds.remove(30) # [10, 20, 40, 50, 30]

# pop() – xóa theo VỊ TRÍ và trả về giá trị
phan_tu = ds.pop() # Xóa cuối → phan_tu = 30
phan_tu = ds.pop(0) # Xóa vị trí 0 → phan_tu = 10

# del – xóa theo vị trí hoặc slice
del ds[0] # Xóa phần tử đầu
del ds[1:3] # Xóa từ index 1 đến 2

# clear() – xóa tất cả
ds.clear() # []
Phương thứcXóa theoTrả về giá trị?
remove(x)Giá trị
pop(i)Vị tríTrả về phần tử bị xóa
del ds[i]Vị trí
clear()Tất cả

5. Sửa phần tử

ds = ["Toán", "Lý", "Hóa", "Anh"]

# Sửa 1 phần tử
ds[1] = "Sinh" # ["Toán", "Sinh", "Hóa", "Anh"]

# Sửa nhiều phần tử bằng slicing
ds[1:3] = ["Lý", "Tin"] # ["Toán", "Lý", "Tin", "Anh"]

# Thay slice bằng số phần tử khác
ds[1:3] = ["A", "B", "C"] # ["Toán", "A", "B", "C", "Anh"]

6. Các phương thức & hàm hữu ích

Hàm tích hợp (Built-in)

diem = [7, 9, 5, 8, 6, 9, 7, 10]

len(diem) # 8 – số phần tử
min(diem) # 5 – giá trị nhỏ nhất
max(diem) # 10 – giá trị lớn nhất
sum(diem) # 61 – tổng

Phương thức của List

diem = [7, 9, 5, 8, 6, 9, 7, 10]

# Đếm & Tìm
diem.count(9) # 2 – đếm số lần xuất hiện
diem.index(8) # 3 – vị trí đầu tiên tìm thấy

# Sắp xếp
diem.sort() # [5, 6, 7, 7, 8, 9, 9, 10] – tăng dần
diem.sort(reverse=True) # [10, 9, 9, 8, 7, 7, 6, 5] – giảm dần

# sorted() – tạo list mới (không thay đổi list gốc)
diem_goc = [7, 9, 5, 8]
diem_sx = sorted(diem_goc) # diem_sx = [5, 7, 8, 9]
# diem_goc vẫn = [7, 9, 5, 8]

# Đảo ngược
ds = [1, 2, 3, 4]
ds.reverse() # [4, 3, 2, 1]

sort() vs sorted():

  • sort() thay đổi list gốc, trả về None
  • sorted() trả về list mới, giữ nguyên list gốc

Kiểm tra phần tử

mon = ["Toán", "Lý", "Hóa"]

"Toán" in mon # True – có trong list
"Anh" in mon # False – không có
"Anh" not in mon # True

7. Duyệt List (Lặp qua List)

mon = ["Toán", "Lý", "Hóa", "Tin"]

# Cách 1: Duyệt trực tiếp
for m in mon:
print(m)

# Cách 2: Duyệt bằng index
for i in range(len(mon)):
print(f"Môn {i + 1}: {mon[i]}")

# Cách 3: enumerate() (lấy cả index và giá trị)
for i, m in enumerate(mon):
print(f"{i}. {m}")

# Cách 4: enumerate() với start
for stt, m in enumerate(mon, start=1):
print(f"{stt}. {m}")

Output Cách 4:

1. Toán
2. Lý
3. Hóa
4. Tin

8. List Comprehension

Cách viết ngắn gọn để tạo list mới từ list hoặc iterable có sẵn.

Cú pháp

# [biểu_thức for biến in iterable]
# [biểu_thức for biến in iterable if điều_kiện]
# [biểu_thức_1 if điều_kiện else biểu_thức_2 for biến in iterable]

Ví dụ

# Bình phương từ 1 đến 5
bp = [i ** 2 for i in range(1, 6)]
# [1, 4, 9, 16, 25]

# Lọc số chẵn từ 1 đến 20
chan = [i for i in range(1, 21) if i % 2 == 0]
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# Chẵn / Lẻ
ket_qua = ["Chẵn" if i % 2 == 0 else "Lẻ" for i in range(1, 6)]
# ['Lẻ', 'Chẵn', 'Lẻ', 'Chẵn', 'Lẻ']

# Chữ hoa tên
ten = ["an", "bình", "châu"]
ten_hoa = [t.capitalize() for t in ten]
# ['An', 'Bình', 'Châu']

# Lọc điểm >= 5
diem = [3, 7, 4, 9, 2, 8, 5]
dat = [d for d in diem if d >= 5]
# [7, 9, 8, 5]

So sánh với vòng lặp thường

# Cách dài (vòng lặp for)
binh_phuong = []
for i in range(1, 6):
binh_phuong.append(i ** 2)

# Cách ngắn (list comprehension)
binh_phuong = [i ** 2 for i in range(1, 6)]

9. Nested List (List lồng nhau)

List có thể chứa list khác bên trong — thường dùng để biểu diễn bảng hoặc ma trận.

# Ma trận 3x3
ma_tran = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]

# Truy cập phần tử
ma_tran[0] # [1, 2, 3] – hàng 1
ma_tran[0][1] # 2 – hàng 1, cột 2
ma_tran[2][2] # 9 – hàng 3, cột 3

# Duyệt ma trận
for hang in ma_tran:
for phan_tu in hang:
print(phan_tu, end=" ")
print()

Output:

1 2 3
4 5 6
7 8 9
# Bảng điểm
bang_diem = [
["An", 8.5, 7.0, 9.0],
["Bình", 6.5, 8.0, 7.5],
["Châu", 9.0, 9.5, 8.0],
]

for hs in bang_diem:
ten = hs[0]
tb = sum(hs[1:]) / len(hs[1:])
print(f"{ten}: Trung bình = {tb:.2f}")

10. Copy List — Sao chép đúng cách

# Sai — gán tham chiếu (cùng trỏ đến 1 list)
a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4] ← a cũng bị thay đổi!

# Đúng — tạo bản sao mới
a = [1, 2, 3]
b = a.copy() # Cách 1: .copy()
b = a[:] # Cách 2: slicing
b = list(a) # Cách 3: list()

b.append(4)
print(a) # [1, 2, 3] ← a không bị ảnh hưởng
print(b) # [1, 2, 3, 4]

Lưu ý: copy()[:] chỉ sao chép nông (shallow copy). Với list lồng nhau, dùng copy.deepcopy():

import copy
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a) # Sao chép sâu

11. Unpack List

# Gán nhiều biến cùng lúc
diem = [8.5, 7.2, 9.0]
toan, ly, hoa = diem

print(toan) # 8.5
print(ly) # 7.2
print(hoa) # 9.0

# Dùng * để gom phần còn lại
ds = [1, 2, 3, 4, 5]
dau, *giua, cuoi = ds
print(dau) # 1
print(giua) # [2, 3, 4]
print(cuoi) # 5

Bài tập thực hành

Trắc nghiệm

  1. [1, 2, 3] + [4, 5] trả về gì?

    • A. [1, 2, 3, [4, 5]]
    • B. [1, 2, 3, 4, 5]
    • C. [5, 7]
    • D. Lỗi
  2. a = [1, 2, 3]; a.append([4, 5]); len(a) trả về gì?

    • A. 5
    • B. 4
    • C. 3
    • D. 6
  3. [1, 2, 3, 4, 5][1::2] trả về gì?

    • A. [1, 3, 5]
    • B. [2, 4]
    • C. [2, 3]
    • D. [1, 2]
  4. Sau khi chạy a = [1,2,3]; b = a; b[0] = 99, giá trị a[0] là gì?

    • A. 1
    • B. 99
    • C. [99, 2, 3]
    • D. Lỗi

Thực hành

Bài 1: Cho danh sách A = [100, 50, 400, 500]. Hãy thực hiện các yêu cầu sau:

  • Thay đổi phần tử: Thay đổi phần tử thứ hai của danh sách thành 200 và in danh sách đã cập nhật.
  • Thêm phần tử: Thêm 600 vào cuối danh sách và in danh sách mới.
  • Chèn phần tử: Chèn số 300 vào vị trí thứ ba (chỉ số 2) của một danh sách và in kết quả.
  • Xóa phần tử (theo giá trị): Xóa phần tử 600 khỏi danh sách và in danh sách đó ra.
  • Xóa phần tử (theo chỉ mục): Xóa phần tử tại chỉ mục 0 khỏi danh sách, in danh sách.

Kết quả mong đợi:

Đã cập nhật (Thay đổi): [100, 200, 400, 500]
Đã cập nhật (Thêm): [100, 200, 400, 500, 600]
Đã cập nhật (Chèn): [100, 200, 300, 400, 500, 600]
Đã cập nhật (Xóa 600): [100, 200, 300, 400, 500]
Đã cập nhật (Xóa chỉ mục 0): [200, 300, 400, 500]

Bài 2: Tính tổng tất cả các số nguyên trong một dãy và tìm trung bình cộng.

Yêu cầu

  1. Tạo một danh sách các số nguyên ngẫu nhiên với số lượng tùy ý.
  2. Tính tổng của tất cả các số trong danh sách.
  3. Tính trung bình cộng của chúng.
  4. In kết quả ra màn hình với định dạng rõ ràng.

Kết quả mong đợi:

Danh sách: [10, 20, 30, 40, 50]
Tổng: 150
Trung bình: 30.0

Bài 3: Viết chương trình tìm giá trị nhỏ nhất, lớn nhất của danh sách số nguyên.

Yêu cầu

  1. Tạo một danh sách các số nguyên ngẫu nhiên với số lượng tùy ý.
  2. Tìm giá trị nhỏ nhất trong danh sách.
  3. Tìm giá trị lớn nhất trong danh sách.
  4. In kết quả ra màn hình với định dạng rõ ràng.

Kết quả mong đợi:

Danh sách: [8, 3, 6, 2, 9, 1]
Giá trị nhỏ nhất: 1
Giá trị lớn nhất: 9

Bài 4: Viết chương trình kiểm tra danh sách có rỗng hay không.

Yêu cầu

  1. Tạo một danh sách các số nguyên ngẫu nhiên với số lượng tùy ý.
  2. Kiểm tra danh sách có rỗng hay không.
  3. Nếu danh sách không rỗng, in kết quả ra màn hình với định dạng rõ ràng.

Kết quả mong đợi:

Danh sách: [8, 3, 6, 2, 9, 1]
Danh sách không rỗng, độ dài của danh sách trên là 6

Danh sách: []
Danh sách rỗng

Bài 5: Cho một danh sách các số nguyên, hãy duyệt qua các phần tử và đếm xem có bao nhiêu số chẵn và bao nhiêu số lẻ.

Dữ liệu đầu vào: A = [10, 21, 4, 45, 66, 93, 11]

Kết quả mong đợi:

Số chẵn: 3
Số lẻ: 4

Bài 6: Cho một mảng số nguyên, hãy đảo ngược thứ tự các phần tử của mảng.

Dữ liệu đầu vào: A = [10, 21, 4, 45, 66, 93, 11]

Kết quả mong đợi:

Đã cập nhật (Đảo ngược): [11, 93, 66, 45, 4, 21, 10]

Bài 7: Cho một mảng số nguyên A hãy tạo và in ra mảng số nguyên B chứa bình phương của các số trong mảng A

Dữ liệu đầu vào: A = [1, 2, 3, 4, 5]

Kết quả mong đợi:

Mảng B: [1, 4, 9, 16, 25]