Mục tiêu của đoạn code là xây dựng cấu trúc dữ liệu và các hàm để quản lý thông tin sinh viên trong một danh sách liên kết đơn.I.4.. Hàm CreateNode nhận đối số là một biến kiểu SV sinh v
MÔ TẢ CHƯƠNG TRÌNH
Viết chương trình quản lý sinh viên (sử dụng danh liên kết), thông tin mỗi sinh viên gồm:
Mã SV – Kiểu số nguyên
Họ và Tên – Kiểu chuỗi tối đa 40 kí tự
Điểm trung bình – Kiểu số thực
Chương trình thực hiện có thiết kế menu bao gồm các chức năng sau:
1 Tạo 1 danh sách gồm n sinh viên (n là nhập từ bàn phìm, thông tin của mỗi sinh viên nhập từ bàn phím).
2 Xuất danh sách sinh viên.
3 Xuất thông tin các sinh viên có điểm trung bình lớn hơn hoặc bằng 5.
4 Tìm sinh viên có tên là X
5 Tìm sinh viên có điểm trung bình lớn nhất.
6 Sắp xếp danh sách tăng dần theo điểm trung bình.
7 Xoá sinh viên đầu danh sách
8 Xoá sinh viên cuối danh sách.
9 Xoá toàn bộ danh sách.
Chương trình cần được mở rộng và đảm bảo các yếu tố kiểm tra đầu vào, tối ưu hóa đầu ra, cũng như giải quyết các vấn đề mở rộng phát sinh.
- Tìm sinh viên có tên X, nếu nhiều hơn 1 sinh viên có tên X, phải xuất thông tin tất cả sinh viên tên X.
- Tìm sinh viên có điểm trung bình lớn nhất nhưng nếu có nhiều sinh viên đạt điểm cao nhất cũng phải xuất thông tin của các sinh viên đó
- Sắp xếp tăng dần theo điểm trung bình, nếu có nhiều hơn 1 sinh viên trùng điểm sẽ sắp xếp theo mã số sinh viên.
NỘI DUNG CHƯƠNG TRÌNH VÀ CÁC THUẬT TOÁN
Các tác vụ
//dinh nghia sv struct SinhVien
{ char masv[10]; char hoten[40]; float dtb;
} ; typedef struct SinhVien SV; typedef struct node
SV info; //luu thông tin 1 SV struct node* next; //luu dia chi nut tiep theo
I.1 Lỗi và sửa lỗi: Giải thuật không có lỗi
Định nghĩa cấu trúc SinhVien để lưu trữ thông tin của mỗi sinh viên bao gồm mã sinh viên (masv), họ tên (hoten) và điểm trung bình (dtb).
Sử dụng typedef để tạo định danh cho cấu trúc SinhVien và cấu trúc node.
Cấu trúc node trong danh sách liên kết đơn được định nghĩa để lưu trữ thông tin của mỗi nút Mỗi nút chứa một biến kiểu SinhVien và một con trỏ trỏ đến nút tiếp theo trong danh sách.
Sử dụng typedef để tạo định danh cho con trỏ đến Node, gọi là NodeSV.
Mục tiêu của đoạn code là xây dựng cấu trúc dữ liệu và các hàm để quản lý thông tin sinh viên trong một danh sách liên kết đơn.
I.4 Độ phức tạp: Độ phức tạp của phần khởi tạo danh sách là O(1). I.5 Nhận xét:
Sử dụng typedef cho cấu trúc SinhVien và node giúp tạo ra các định danh tiện lợi, dễ đọc và dễ sử dụng lại trong mã nguồn.
2.1.Lỗi và sửa lỗi: Giải thuật không có lỗi
Hàm Init nhận đối số là một tham chiếu đến con trỏ phead, biểu thị cho đầu danh sách liên kết đơn.
Trong hàm này, phead được gán giá trị NULL, chỉ ra rằng danh sách liên kết đơn hiện đang rỗng và không có nút nào được kết nối.
Mục tiêu của đoạn code là khởi tạo một danh sách liên kết đơn cho việc quản lý thông tin sinh viên.
2.4.Độ phức tạo Độ phức tạp của hàm Init là O(1), vì không có vòng lặp hoặc thao tác phức tạp nào được thực hiện Việc gán giá trị NULL cho con trỏ phead chỉ tốn một bước thực hiện cố định.
2.5.Nhận xét Ưu điểm: Thực hiện mục tiêu của việc khởi tạo danh sách liên kết một cách nhanh chóng.
Nhược điểm: Thiếu linh hoạt: Hàm Init chỉ khởi tạo danh sách liên kết đơn với giá trị NULL cho phead. int IsEmpty(NodeSV phead){ return (phead==NULL);
3.1.Lỗi và sửa lỗi: Giải thuật không có lỗi.
Hàm IsEmpty nhận đối số là một con trỏ phead, biểu thị cho đầu danh sách liên kết đơn.
Hàm này kiểm tra giá trị của phead để xác định xem danh sách có trống hay không Nếu phead bằng NULL, hàm sẽ trả về 1 (true), cho thấy danh sách đang trống Ngược lại, nếu phead khác NULL, hàm sẽ trả về 0 (false), chứng tỏ danh sách có ít nhất một phần tử.
Mục tiêu của đoạn code là kiểm tra xem danh sách liên kết đơn có rỗng không.
3.4.Độ phức tạp: Độ phức tạp của hàm IsEmpty là O(1).
Code dễ hiểu, thực hiện chức năng kiểm tra trạng thái rỗng của danh sách liên kết một cách nhanh chóng.
NodeSV p=new Node; p->info=x; p->next=NULL; return p;
4.1.Lỗi và sửa lỗi: Giải thuật không có lỗi
Hàm CreateNode nhận đối số là một biến kiểu SV (sinh viên) được gọi là x, chứa thông tin của sinh viên cần thêm vào nút mới.
Trong hàm này, một con trỏ p được cấp phát bộ nhớ để tạo ra một nút mới trong danh sách liên kết đơn.
Thông tin của sinh viên x được lưu trữ trong trường info của nút mới, trong khi trường next của nút này được gán giá trị NULL, cho thấy rằng nút này chưa kết nối với nút nào khác.
Sau đó, con trỏ p được trả về để biểu diễn nút mới đã được tạo.
Mục tiêu của đoạn code là tạo một nút mới trong danh sách liên kết đơn chứa thông tin của một sinh viên được truyền vào.
4.4.Độ phức tạp:Độ phức tạp của hàm CreateNode là O(1).
// Tác vu InsertFirst void InsertFirst(NodeSV &phead, SV x){
NodeSV p=CreateNode(x); p->next=phead; phead=p;
5.1.Lỗi và sửa lỗi: Giải thuật không có lỗi.
Hàm InsertFirst nhận tham chiếu phead đến con trỏ đầu danh sách và biến x đại diện cho thông tin sinh viên cần chèn Đầu tiên, hàm sẽ tạo một nút mới bằng cách gọi hàm CreateNode(x), nhằm lưu trữ thông tin của sinh viên x.
Cuối cùng, con trỏ phead được cập nhật để trỏ đến nút mới, biểu thị rằng nút mới đã trở thành nút đầu tiên của danh sách.
Chức năng của đoạn code là chèn một nút mới vào đầu danh sách liên kết đơn.
5.5.Độ phức tạp: Độ phức tạp của hàm InsertFirst là O(1), bởi vì nó chỉ thực hiện một số thao tác gán hằng số.
- Code dễ hiểu, chỉ chèn một nút mới vào đầu danh sách.
- Thời gian thực thi nhanh: Do độ phức tạp O(1), thời gian thực thi của hàm là nhanh chống, không phụ thuộc vào kích thước của danh sách. Nhược điểm:
Hàm không kiểm tra tính hợp lệ của con trỏ phead trước khi thực hiện việc chèn nút mới, dẫn đến khả năng xảy ra lỗi nếu phead là NULL Việc tiếp tục quá trình chèn trong trường hợp này có thể gây ra lỗi truy cập vào con trỏ không hợp lệ.
// Tác vu InsertAfter void InsertAfter (NodeSV &p, SV x){
NodeSV tam; if(p!=NULL){ tam=CreateNode(x); tam->next=p->next; p->next=tam;
B1: Khai báo hàm: void InsertAfter(NodeSV &p, SV x){
Hàm InsertAfter nhận tham chiếu đến con trỏ p, trỏ đến nút trong danh sách liên kết, nơi nút mới sẽ được chèn vào Đối tượng SV chứa thông tin sinh viên cần thêm vào danh sách Trước khi thực hiện chèn, cần kiểm tra điều kiện if (p != NULL) để đảm bảo con trỏ p không trống.
Trước khi thêm nút mới, cần xác minh rằng con trỏ p đang trỏ đến một nút hợp lệ Nếu p không rỗng (không trỏ đến NULL), chúng ta sẽ tiến hành chèn nút mới ngay sau nút p.
B3: Tạo nút mới: tam = CreateNode(x);
Sử dụng hàm CreateNode(x) để tạo một nút mới và trả về con trỏ đến nút này.
Thông tin của sinh viên x được chuyển vào hàm CreateNode để lưu vào nút mới.
B4: Liên kết nút mới với nút tiếp theo của nút p: tam->next = p->next;
Con trỏ next của nút mới (tam->next) được gán bằng con trỏ next của nút p, giúp nút mới trỏ đến nút tiếp theo của nút p trong danh sách liên kết.
B5: Liên kết nút p với nút mới: p->next = tam;
Con trỏ tiếp theo của nút p được cập nhật để trỏ đến nút mới (tam), thay vì trỏ đến nút tiếp theo trong danh sách liên kết.
6.4.Mục tiêu của đoạn code:
Chức năng của đoạn code là chèn một nút mới vào sau một nút đã cho trong danh sách liên kết đơn.
6.5.Đánh giá độ phức tạp: Độ phức tạp của hàm InsertAfter là O(1) trong trường hợp trung bình, tuy nhiên, trong trường hợp tồi tệ nhất khi nút cần chèn là nút cuối cùng, độ phức tạp có thể là O(n) với n là số lượng nút trong danh sách.
Code dễ hiểu, chỉ chèn một nút mới vào sau một nút đã cho trong danh sách.
Thời gian thực thi nhanh do độ phức tạp O(1) trong trường hợp trung bình.
// Tác vu InsertLast - void InsertLast(NodeSV &phead, SV x){
NodeSV p; p = phead; if (phead==NULL) phead=tam; else
NodeSV ptemp=phead; while (ptemp->next!=NULL) ptemp = ptemp->next; ptemp->next=tam;
7.1.Lỗi và sữa lỗi: Giải thuật không có lỗi.
B1: Khai báo hàm: void InsertLast(NodeSV &phead, SV x){
Hàm InsertLast nhận tham chiếu đến con trỏ phead, đại diện cho đầu danh sách liên kết, và một đối tượng SV chứa thông tin sinh viên cần chèn vào danh sách.
Sử dụng hàm CreateNode(x) để tạo một nút mới và trả về con trỏ đến nút này.
Thông tin của sinh viên x được chuyển vào hàm CreateNode để lưu vào nút mới.
B3: Duyệt danh sách để tìm nút cuối cùng:
NodeSV p; p = phead; if (phead == NULL) phead = tam; else {
NodeSV ptemp = phead; while (ptemp->next != NULL) ptemp = ptemp->next; ptemp->next = tam;
- Trong trường hợp danh sách rỗng (không có nút nào), nút mới (tam) được chèn trực tiếp vào đầu danh sách (phead).
Trong trường hợp danh sách không rỗng, con trỏ ptemp được sử dụng để duyệt qua từng phần tử, bắt đầu từ đầu danh sách cho đến nút cuối cùng, nơi mà con trỏ next có giá trị NULL.
Khi ptemp đến nút cuối cùng, nút mới (tam) sẽ được chèn vào sau nút cuối cùng bằng cách gán con trỏ next của nút cuối cùng với con trỏ của nút mới.
7.4.Mục tiêu của đoạn code:
Chức năng của đoạn code là chèn một nút mới vào cuối danh sách liên kết đơn.
Các chức năng
//Nhap 1 SV void nhap1SV(SV &x)
{ fflush(stdin); printf("\n Masv: "); gets(x.masv); printf("\n Ho ten: "); gets(x.hoten); printf("\n Diem TB: "); float t; scanf("%f",&t); x.dtb=t;
1.1.Lỗi và sửa lỗi: Đoạn code không có lỗi.
Hàm “nhap1SV” nhận vào một tham chiếu x đến một biến kiểu SV, đại diện cho sinh viên cần nhập thông tin.
Sử dụng hàm gets để nhập mã sinh viên và họ tên của sinh viên từ bàn phím.
Sử dụng hàm scanf để nhập điểm trung bình của sinh viên. Sau đó, giá trị điểm trung bình được gán vào trường dtb của biến x.
1.4.Mục tiêu của đoạn code:
Chức năng của đoạn code là nhập thông tin của một sinh viên từ người dùng.
1.5.Đánh giá độ phức tạp: Độ phức tạp của hàm nhap1SV là O(1), vì số lần lặp và số lần gán không phụ thuộc vào kích thước của dữ liệu đầu vào.
- Chức năng đơn giản và hiệu quả: Hàm cung cấp chức năng cần thiết để nhập thông tin của một sinh viên từ người dùng.
- Dễ hiểu và sử dụng: Code ngắn gọn và dễ hiểu, sử dụng các hàm nhập chuẩn của ngôn ngữ để thuận tiện cho người dùng.
Hàm không thực hiện kiểm tra tính hợp lệ của dữ liệu đầu vào, như việc xác minh định dạng mã sinh viên hoặc kiểm tra xem điểm trung bình có nằm trong khoảng giá trị hợp lệ hay không Điều này có thể dẫn đến việc nhập dữ liệu sai, gây ra lỗi trong quá trình xử lý thông tin.
2 Nhập danh sách sinh viên
Code ban đầu Code đã sửa
//Nhap danh sach sinh vien void nhapds(NodeSV &phead)
{ int n; SV x; printf("Nhap so luong sinh vien:
//Nhap danh sach sinh vien void nhapds(NodeSV &phead) { int n; SV x; printf("Nhap so luong sinh vien: "); scanf("%d", &n); for(int i=0; inext) { if (p->info.dtb >5) xuat1SV(p->info);
=NULL;p=p->next) { if (p->info.dtb >5) xuat1SV(p->info); }
Đoạn code này ban đầu thiếu “else”.
Việc thêm câu lệnh else vào đoạn code là cần thiết để đảm bảo rằng dữ liệu được xử lý đúng cách trong cả hai trường hợp Nếu không sử dụng else, chỉ khi điều kiện p == NULL thỏa mãn thì mới có xử lý, trong khi trường hợp không thỏa mãn sẽ không có xử lý rõ ràng.
Trường hợp không thêm “else” vào đoạn code thì phải return hoặc exit hàm này khi p == NULL
B1: Nếu danh sách rỗng Thực hiện in ra màn hình dòng thông báo
B2.1 Khởi tạo NODE p và gán p cho NODE đầu danh sách. B2.2 Khởi tạo vòng lặp từ p,kiểm tra điều kiện kết thúc vòng lặp là p=NULL.
B2.3 Nếu điểm của phần data tại NODE hiện tại lớn hơn 5=> In ra màn hình data của node sinh viên đang duyệt.
B2.4 Gán NODE hiện tại khác NULL.
B2.5 Nếu NODE hiện tại khác NULL.Quay lại bước B2.3
Tìm kiếm điểm trung bình lớn hơn 5 trong danh sách.
Xuất thông tin của những sinh viên có điểm trung bình lớn hơn
Nếu danh sách không có phần tử nào, in ra thông báo "Danh sách rỗng" Duyệt qua từng phần tử trong danh sách.
Nếu phần tử đó có điểm trung bình (> 5), thì in ra thông tin của sinh viên đó.
5.5.Đánh giá độ khó: Đoạn code trên có độ phức tạp thời gian là O(n), trong đó n là số lượng phần tử trong danh sách.
Đoạn mã code ngắn gọn và dễ hiểu này thực hiện việc duyệt qua danh sách sinh viên chỉ một lần, nhằm in ra thông tin của những sinh viên có điểm trung bình trên 5.
Mã code này thực hiện một phép so sánh cho mỗi sinh viên trong danh sách để kiểm tra xem điểm trung bình có vượt quá 5 hay không Nhờ vào cách thức này, hiệu suất của mã code được đánh giá là tốt.
Mã code không thực hiện kiểm tra tính hợp lệ của phead như một danh sách sinh viên Nếu phead không phải là danh sách sinh viên hợp lệ, chương trình có nguy cơ gặp phải lỗi.