Bỏ qua điều hướng, tới nội dung chính
Học C
Bài 60.328 phút đọc

Level 3: Quản lý thư viện

Sau bài này bạn sẽ làm được

  • Cài danh sách liên kết cho tài liệu và độc giả
  • Dùng bảng băm tra cứu theo mã trong thời gian trung bình O(1)
  • Sắp xếp danh sách liên kết bằng merge sort
  • Cài hoàn tác thao tác cuối bằng ngăn xếp

Dự án thứ ba nâng độ khó về cấu trúc dữ liệu: danh sách liên kết tự cài, bảng băm tra cứu nhanh, merge sort trên danh sách, và hoàn tác bằng ngăn xếp. Đây là chỗ Phần 5 tới 11 gặp nhau trong một hệ thống thật.

#Mục tiêu

Cấu trúc dữ liệu
typedef enum { GIAO_KHOA, THAM_KHAO, TAP_CHI, LUAN_VAN } LoaiTaiLieu;

typedef struct TaiLieu {
    char ma[16], ten[128], tac_gia[64], isbn[20];
    int  nam, so_luong, con_lai;
    LoaiTaiLieu loai;
    struct TaiLieu *next;           /* danh sach lien ket */
} TaiLieu;

typedef struct {
    char ma_the[16], ho_ten[64];
    /* danh sach dang muon */
} DocGia;

typedef struct {
    char   ma_tl[16], ma_the[16];
    time_t ngay_muon, han_tra;
} PhieuMuon;
Chức năngCấu trúcChương
Lưu tài liệu, độc giảDanh sách liên kết tự càiPhần 6, 7
Tra cứu theo mãBảng bămPhần 11
Sắp xếpMerge sort trên danh sách liên kếtPhần 8, 10
Mượn, trả, phí trễtime_t và logic nghiệp vụPhần 5
Hoàn tác thao tác cuốiNgăn xếpPhần 6

#Danh sách liên kết

#Bảng băm tra cứu

thuvien.c, danh sách + bảng băm
typedef struct TaiLieu {
    char ma[16], ten[64];
    int  con_lai;
    struct TaiLieu *next;           /* danh sach lien ket chinh */
    struct TaiLieu *hash_next;      /* xich trong o bang bam */
} TaiLieu;

#define HASH_CO 16
typedef struct {
    TaiLieu *dau;                   /* dau danh sach */
    TaiLieu *bang[HASH_CO];         /* bang bam, moi o mot xich */
    int so;
} ThuVien;

static size_t bam(const char *s) {
    size_t h = 5381;                /* thuat toan djb2 */
    for (; *s; ++s) h = h * 33 + (unsigned char)*s;
    return h % HASH_CO;
}

static TaiLieu *tra(ThuVien *tv, const char *ma) {
    for (TaiLieu *t = tv->bang[bam(ma)]; t; t = t->hash_next)
        if (strcmp(t->ma, ma) == 0) return t;   /* trung binh O(1) */
    return NULL;
}
terminal
./thuvien_thu
da them 3 tai lieu
tra T002: Cau truc du lieu (con 2)
tra T099: khong thay
duyet danh sach: T003 T002 T001

#Merge sort danh sách

  1. Chia đôi bằng con trỏ nhanh chậm

    Một con trỏ đi hai bước, một con trỏ đi một bước. Khi con trỏ nhanh tới cuối, con trỏ chậm ở giữa. Cắt danh sách ở đó.
  2. Đệ quy sắp hai nửa

    Gọi merge sort trên nửa đầu và nửa sau.
  3. Trộn hai nửa đã sắp

    Đi song song hai danh sách, mỗi bước lấy nút nhỏ hơn nối vào kết quả. Chỉ nối con trỏ, không cấp bộ nhớ mới.

#Hoàn tác bằng ngăn xếp

Ghi lại thao tác để hoàn tác
typedef enum { TT_THEM, TT_XOA, TT_SUA } LoaiThaoTac;

typedef struct {
    LoaiThaoTac loai;
    TaiLieu     ban_sao;        /* du lieu du de dao nguoc thao tac */
} ThaoTac;

typedef struct {
    ThaoTac muc[100];           /* hoac ngan xep dong */
    int     dinh;
} NganXepHoanTac;

/* Moi thao tac day mot muc vao ngan xep: */
static void ghi_lai(NganXepHoanTac *nx, LoaiThaoTac loai, TaiLieu ban_sao) {
    if (nx->dinh < 100) {
        nx->muc[nx->dinh].loai = loai;
        nx->muc[nx->dinh].ban_sao = ban_sao;
        nx->dinh++;
    }
}

/* Hoan tac: lay muc tren cung, DAO NGUOC no */
static void hoan_tac(ThuVien *tv, NganXepHoanTac *nx) {
    if (nx->dinh == 0) return;
    ThaoTac t = nx->muc[--nx->dinh];
    switch (t.loai) {
        case TT_THEM: /* da them -> gio xoa */    xoa(tv, t.ban_sao.ma); break;
        case TT_XOA:  /* da xoa  -> gio them lai */ them(tv, t.ban_sao); break;
        case TT_SUA:  /* da sua  -> khoi phuc ban cu */ khoi_phuc(tv, t.ban_sao); break;
    }
}

#Yêu cầu

Tự làm thử

  1. Danh sách liên kết cho tài liệu và độc giả, tự cài, không dùng mảng.
  2. Bảng băm tra cứu nhanh theo mã.
  3. Mượn và trả sách, tính phí trễ hạn, giới hạn số sách mượn.
  4. Tìm kiếm đa tiêu chí, sắp xếp danh sách liên kết bằng merge sort.
  5. Báo cáo: sách mượn nhiều nhất, độc giả quá hạn, thống kê theo tháng.
  6. Lưu trữ bền vững, khôi phục được sau khi thoát.
  7. Hoàn tác thao tác cuối bằng ngăn xếp.

Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.

Tóm tắt

  • Danh sách liên kết để lưu và duyệt, bảng băm để tra cứu nhanh; một tài liệu nằm trong cả hai với hai con trỏ next.
  • Bốn thao tác con trỏ: thêm đầu, xoá giữ con trỏ trước, duyệt giữ next trước khi free, không đọc sau khi free.
  • Merge sort là thuật toán sắp tự nhiên của danh sách liên kết vì chỉ nối con trỏ.
  • Ngăn xếp là cấu trúc đúng cho hoàn tác; mỗi thao tác lưu đủ thông tin để đảo ngược.
  • Bản sao hoàn tác phải đủ sâu để không tạo con trỏ treo sau khi giải phóng.