ĐỀ TÀI 1: SỬ DỤNG CẤU TRÚC HÀNG ĐỢI ĐỂ XÂY DỰNG HỆ THỐNG QUẢN LÝ BÁN VÉ TÀU I KHÁI NIỆM HÀNG ĐỢI VÀ CÁC ĐẶC TRƯNG CỦA HÀNG ĐỢI 1 Khái niệm hàng đợi Hàng đợi là một cấu trúc dữ liệu gần giống với ngăn xếp, nhưng khác với ngăn xếp ở nguyên tắc chọn phần tử cần lấy ra khỏi tập phần tử Trái ngược với ngăn xếp, phần tử được lấy ra khỏi hàng đợi không phải là phần tử mới nhất được đưa vào mà là phần tử đã được lưu trong hàng đợi lâu nhất Quy luật này của hàng đợi còn được gọi là Vào trước – Ra trước (First in – First out) Như vậy ta có thể định nghĩa hàng đợi là một dạng đặc biệt của danh sách mà việc lấy một phần tử ra được thực hiện ở 1 đầu (gọi là đầu hàng), còn việc bổ sung một phần tử mới được thực hiện ở một đầu (gọi là cuối hàng) 2 Một số thao tác trong hàng đợi - Enqueue: thêm phần tử vào hàng đợi - Dequeue: xóa phần tử khỏi hàng đợi - Clear: xóa tất cả phần tử của hàng đợi - Count: đếm số phần tử trong hàng đợi - IsEmpty: kiểm tra hàng đợi có rỗng hay không 3 Các đặc trưng của hàm đợi Thứ tự mà mà theo đó các công việc trong hàng xếp được phục vụ gọi là quy tắc phục vụ Hầu như các hệ thống xếp hàng ngày nay được sử dụng để phục vụ khách hàng theo trình tự mà chúng tới Trình tự phục vụ đó là FIFS( First Come First Served) Ngoài ra, còn có một số kiểu phục vụ khác như là: - LCFS( Last Come First Served) hay LIFO (Last In First Out): Theo đó các khách hàng đến sau sẽ được phục vụ trước - LCFSPR( LCFS With Pre- Emptive): Khi khách hàng đến sau nhất sẽ thế chỗ của khách hàng đang được phục vụ xong thì phục vụ có thể tiếp tục đối với khách hàng bị thế chỗ ngay nơi mà nó bị ngắt trước đây, trong trường hợp đó ta có dịch vụ với quyền ưu tiên phục vụ trước – LIFOPR - RR(Round Robin): Phục vụ vòng tròn, thời gian tại một tài nguyên được phân chia thành một số khoản nhỏ có độ dài cố định và được gọi là các lượng tử II THUẬT TOÁN 1 Ý tưởng của thuật toán Các khách hàng đến được sắp xếp vào hàng đợi đến lượt phục vụ mua vé tàu Để đơn giản ta thiết kế một hàng đợi Các khách hàng được chọn để phục vụ theo nguyên tắc” đến trước phục vụ trước” nghĩa là phục vụ cho khách hàng đứng đầu hàng Khi khách hàng đứng đầu hàng được phục vụ xong thì đến lượt khách hàng tiếp theo tiến hành thủ tục mua vé Khi có khách hàng mới đến mua vé thì sẽ được sắp xếp vào hàng đợi rỗng hoặc hàng đợi chưa đầy Cần xây dựng các chức năng cần thiết cho chương trình: - Khởi tạo hàng đợi - Kiểm tra hàng đợi có rỗng không - Thêm một phần tử mới vào hàng, giả sử hàng đợi chưa đầy - Loại một phần tử ra khỏi hàng, phần tử bị loại là phần tử đầu hàng, thường là phần tử vừa được xử lý xong - Xem phần tử tại đầu hàng - Phần tử đầu hàng được phục vụ trước 2 Thuật toán Queue là hàng đợi để lưu các phần tử, có quy tắc phần tử vào trước thì ra trước Head là vị trí của phần tử đầu tiên trong Queue Coun: Lưu số lượng các phần tử trong Queue Thuật toán ta có thể viết riêng rẻ ra như sau, nếu muốn tổng hợp thì có thể gộp tuần tự các đoạn lại với nhau 1 Thêm phần tử vào hàng đợi B1: if(IsFull()) B3; else B2; B2: Coun++; Jump(B4); B3: printf(“Hàng đợi đã full”); B4: Thoát chương trình 2 Xóa một phần tử ra khỏi Queue B1: If(IsEmpty()) b4;else B2; B2: Queue[Head].check = false; B3: Head = (Head+1)%MAX; Coun ; Jump(B5); B4: printf(“Hàng đợi đã Empty”); B5: Thoát chương trình 3 Kiểm tra hàng đợi có Empty hay không? B1: if(CounMAX) return true; else return false; 5 In một phần tử x của Queue 3 Đánh giá độ phức tạp của thuật toán - Độ phức tạp của câu lệnh: O(1) - Độ phức tạp của vòng for nhỏ bao câu lệnh: O(n) Áp dụng quy tắc 4 (quy tắc nhân) T(n) = O(1 n) = O(n) Vậy độ phức tạp của thuật toán là O(n) III CHƯƠNG TRÌNH MINH HỌA Đầu tiên khai báo thư viện mà ta sử dụng để chứa các lớp: List.h #include #include "list.h" + Thao tác khởi tạo hàng đợi: Tạo hàng đợi có tên là l Thực hiện việc gán giá trị bằng NULL cho biến head, giá trị NULL cho biến tail và giá trị 0 cho biến count, cho biết hàng đợi đang ở trạng thái rỗng Biến count cho biết số phần tử hiện tại của hàng đợi để thuận tiện cho việc kiểm tra hàng đợi đầy hoặc rỗng Khi chúng ta cần 1 chuỗi có kích thước của danh sách hàng đợi vì thế cần phải xin số ô nhớ đủ cho kích thước của hàng đợi bằng lệnh malloc(sizeof(list)) list* list_create() { list *l = (list*)malloc(sizeof(list)); l->head = NULL; l->tail = NULL; l->count = 0; return l; } + Thao tác kiểm tra hàng đợi: dùng hàm trả về giá trị True hoặc false để kiểm tra hàng đợi, nếu số phần tử bằng 0, giá trị count bằng 0 thì trả về kết quả là đúng (True)-> hàng đợi rỗngcòn không thì trả về kết quả sai (False) -> hàng đợi không rỗng bool list_isNull(list *pl) { if (pl->count == 0) return true; else return false; } + Thao tác thêm vào hàng đợi: thêm 1 phần tử mới vào cuối hàng đợi (giả sử hàng đợi chưa đầy) Cấp phát bộ nhớ cho phần tử mới Gán giá trị NULL cho phần tử tiếp theo của hàng đợi Gán giá trị value cho phần tử mới void list_push_end(list *pl, void *value) { node *p = (node*)malloc(sizeof(node)); p->next = NULL; p->value = value; if (pl->tail == NULL) pl->head = p; else pl->tail->next = p; pl->tail = p; pl->count++; } + Thao tác kiểm tra hàng đợi: sau khi thêm phần tử, kiểm tra hàng đợi đã đầy chưa void list_remove_head(list *pl) { node *p = pl->head; pl->head = p->next; pl->count ; free(p); if (pl->head == NULL) pl->tail = NULL; } + Thao tác loại bỏ 1 phần tử ra khỏi hàng đợi: bắt đầu thực hiện thao tác loại bỏ phần tử đầu tiên của hàng đợi void* list_pop_begin(list *pl) { if (pl->head == NULL) return NULL; void *v = pl->head->value; list_remove_head(pl); return v; } + Thao tác xem phần tử đầu: thực hiện thao tác xem phần tử đầu, nếu phần tử đầu có giá trị NULL thì trả về kết quả hàng đợi là NULL, nếu không thì phần tử mới là phần tử đầu tiên của hàng đợi void* list_begin(list *pl) { if (pl->head == NULL) return NULL; return pl->head->value; } + Thao tác hiển thị các phần tử trong hàng đợi void list_clear(list *pl) { node *p = pl->head; while (p) { pl->head = p->next; free(p); p = pl->head; } pl->tail = NULL; pl->count = 0; } + Thao tác hủy hàng đợi: thực hiện thao tác hủy hàng đợi void list_destroy(list *pl) { list_clear(pl); free(pl); } - Sau đó, để thực định nghĩa các thao tác : List.cpp #ifndef LIST_H #define LIST_H #include typedef struct node_s { void *value; node_s *next; } node; typedef struct list_s { node *head, *tail; int count; } list; list* list_create(); bool list_isNull(list *pl); void list_push_end(list *pl, void *value); void list_remove_head(list *pl); void* list_pop_begin(list *pl); void* list_begin(list *pl); void list_clear(list *pl); void list_destroy(list *pl); #endif - Đây là đoạn code thực thi chương trình: main.cpp #include #include "list.h" int main() { int soLuong = 9; int khach_hang[] = { 1,3,5,77,9,2,4,6,8 }; // Khoi tao list *queue = list_create(); // kiem tra queue bool b = list_isNull(queue); // Them vao queue for (int i = 0; i < soLuong; i++) { list_push_end(queue, khach_hang + i); } // kiem tra queue b = list_isNull(queue); // so luong int soLuongQueue = queue->count; // Loai bo mot phan tu ra queue list_pop_begin(queue); // kiem tra so luong sau khi loai bo soLuongQueue = queue->count; // Xem phan tu dau int phanTuDau = *(int*)list_begin(queue); printf("Phan tu dau: %d\n", phanTuDau); // Hien thi cac phan tu trong queue printf("Cac phan tu co trong queue: "); for (int i = 0; i < soLuongQueue; i++) { int *v = (int*)list_pop_begin(queue); printf("%d ", *v); } // Huy queue list_destroy(queue); // printf("\n"); system("pause"); return 0; }