#include #include #include #include #include #define MAX_NAME_LENGTH 24 //宣告函式原型 void init_f(); void insertStudent(char* name, int english, int math); void modify_list(); void add_data(char* name, int english, int math); void remove_data(char* name); void readStudentsFromFile(char* filename); void printStudentList(); void sort_name(); void sort_eng(); void sort_math(); void choose_subject(); void find_student(int number , int mode); // 定義學生節點結構 typedef struct Student { char name[MAX_NAME_LENGTH]; int english; int math; struct Student* llink; struct Student* rlink; } Student; Student *head, *tail, *current, *prev, *temp; FILE* file; //設一個 head,將左右鏈結皆指向本身 void init_f() { head = (Student *) malloc(sizeof(Student)); // 直接用 head head->llink = head; // 左鏈結指向自己 head->rlink = head; // 右鏈結指向自己 tail = head; } // 在尾部插入新學生節點 void insertStudent(char* name, int english, int math) { Student* newNode = (Student*)malloc(sizeof(Student)); strcpy(newNode->name, name); newNode->english = english; newNode->math = math; if(head->rlink == tail) { // 這是第一個插入的節點 head->rlink = newNode; tail->llink = newNode; newNode->llink = head; newNode->rlink = tail; } else { //不是第一筆 插入尾部 newNode->llink = tail->llink; newNode->rlink = tail; tail->llink->rlink = newNode; tail->llink = newNode; } } // 執行***後的增加或減少資料 void modify_list(){ char name[MAX_NAME_LENGTH]; int english, math; char line[100]; while(fgets(line, sizeof(line), file)!=NULL){ if(line[0]=='+'){ sscanf(line+2, "%23[^\t]\t%d\t%d\n", name, &english, &math); printf("\n*******************************\n"); printf("增加一筆資料\n\n"); add_data(name, english, math); printStudentList(); }else if(line[0]=='-'){ sscanf(line+2, "%23[^\t]\t%d\t%d\n", name, &english, &math); printf("\n*******************************\n"); printf("刪除一筆資料\n\n"); remove_data(name); printStudentList(); } } fclose(file); } //增加資料 void add_data(char* name, int english, int math){ Student* newNode = (Student*)malloc(sizeof(Student)); strcpy(newNode->name, name); newNode->english = english; newNode->math = math; newNode->llink = tail->llink; newNode->rlink = tail; tail->llink->rlink = newNode; tail->llink = newNode; printf("%s 已增加\n\n", name); } //減少資料 void remove_data(char* name){ prev = head; current = head->rlink; while(current!=tail &&strcmp(name,current->name)!=0){ prev=current; current=current->rlink; } if(current != tail){ current->rlink->llink = prev; prev->rlink = current->rlink; printf("%s 已刪除\n\n", name); free(current); }else /* 找不到資料則顯示錯誤 */ printf(" %s 不在串列中\n", name); } // 從文件讀取學生資料 void readStudentsFromFile(char* filename) { file = fopen(filename, "r"); if (file == NULL) { printf("無法開啟檔案 %s\n", filename); return; } char line[100]; int lineCount = 0; // 跳過前四行 while (lineCount < 4 && fgets(line, sizeof(line), file)) { lineCount++; } // 讀取學生資料 char name[MAX_NAME_LENGTH]; int english, math; // %[ ]表示要讀入一個字符集合 // %23[^\t]為「讀取最多23個字符,直到遇到\t為止」 ^為非 不是\t就讀 // 若成功讀取到資料會回傳3 若讀取失敗 只讀到name或沒讀到資料則回傳值不等於3 while(fgets(line, sizeof(line), file)!=NULL){ if(isalpha(line[0])){ sscanf(line, "%23[^\t]\t%d\t%d\n", name, &english, &math); insertStudent(name, english, math); }else if(line[0]=='*'){ break; } } // 此flocse為測試用 正常使用應在modify_list的尾端 // fclose(file); } // 列印學生列表 void printStudentList() { current = head->rlink; printf("\n從頭開始\n"); printf("\n姓名\t\t英文\t數學\n"); printf("--------------------------------\n"); while (current != tail) { printf("%-15s\t%d\t%d\n", current->name, current->english, current->math); current = current->rlink; } current = tail->llink; printf("\n從尾開始\n"); printf("\n姓名\t\t英文\t數學\n"); printf("--------------------------------\n"); while (current != head) { printf("%-15s\t%d\t%d\n", current->name, current->english, current->math); current = current->llink; } } //依照名字進行排序 void sort_name(){ //若沒有資料或只有一筆資料 if (head->rlink == tail || head->rlink->rlink == tail) { return; } int flag = 1; while(flag){ flag = 0; current = head->rlink; while(current->rlink != tail){ temp = current->rlink; if(strcmp(current->name,temp->name) > 0){ // 只使用兩個節點進行節點交換 // 要注意指標改變後的順序 // 若下列程式順序交換指標指向的位址會出錯 // 也可以多宣告兩個節點 紀錄current->llink及temp->rlink // 這樣就可以無腦不管順序 temp->llink = current->llink; current->rlink = temp->rlink; temp->rlink->llink = current; temp->rlink = current; current->llink->rlink = temp; current->llink = temp; flag = 1; } else{ current = current->rlink; } } } } //依照英文成績進行排序 void sort_eng(){ //若沒有資料或只有一筆資料 if (head->rlink == tail || head->rlink->rlink == tail) { return; } int flag = 1; while(flag){ flag = 0; current = head->rlink; while(current->rlink != tail){ temp = current->rlink; if(current->english < temp->english){ // 只使用兩個節點進行節點交換 // 要注意指標改變後的順序 // 若下列程式順序交換指標指向的位址會出錯 // 也可以多宣告兩個節點 紀錄current->llink及temp->rlink // 這樣就可以無腦不管順序 temp->llink = current->llink; current->rlink = temp->rlink; temp->rlink->llink = current; temp->rlink = current; current->llink->rlink = temp; current->llink = temp; flag = 1; } else{ current = current->rlink; } } } } //依照英文成績進行排序 void sort_math(){ //若沒有資料或只有一筆資料 if (head->rlink == tail || head->rlink->rlink == tail) { return; } int flag = 1; while(flag){ flag = 0; current = head->rlink; while(current->rlink != tail){ temp = current->rlink; if(current->math < temp->math){ // 只使用兩個節點進行節點交換 // 要注意指標改變後的順序 // 若下列程式順序交換指標指向的位址會出錯 // 也可以多宣告兩個節點 紀錄current->llink及temp->rlink // 這樣就可以無腦不管順序 temp->llink = current->llink; current->rlink = temp->rlink; temp->rlink->llink = current; temp->rlink = current; current->llink->rlink = temp; current->llink = temp; flag = 1; } else{ current = current->rlink; } } } } //選擇科目及排名 void choose_subject(){ int subject ,number , i=1; printf("\n輸入選擇的科目\n" "1 數學 2 英文 ==> "); subject = getche(); switch(subject){ case '1': printf("\n輸入排名(輸入完畢請按Enter) ==> "); scanf("%d",&number); sort_math(); find_student(number , 1); printf("\n驗證如下"); printf("\n排名\t姓名\t\t英文\t數學"); printf("\n-----------------------------------\n"); Student* current = head->rlink; while (current != tail) { printf("%d\t%-15s\t%d\t%d\n",i++, current->name, current->english, current->math); current = current->rlink; } break; case '2': printf("\n輸入排名(輸入完畢請按Enter) ==> "); scanf("%d",&number); sort_eng(); find_student(number , 2); printf("\n驗證如下"); printf("\n排名\t姓名\t\t英文\t數學"); printf("\n-----------------------------------\n"); current = head->rlink; while (current != tail) { printf("%d\t%-15s\t%d\t%d\n",i++, current->name, current->english, current->math); current = current->rlink; } break; default : printf("輸入錯誤"); break; } } //根據科目及排名找學生 void find_student(int number , int mode){ current = head->rlink; int i = 1; while (current != tail && i < number) { current = current->rlink; i++; } if(current!=tail && number != 0){ printf("\n*****************************\n"); if(mode == 1 ){ printf("數學"); }else if(mode == 2){ printf("英文"); } printf("排名第%d的學生為\n\n" "姓名\t\t英文\t數學\n%-15s\t%d\t%d", number, current->name, current->english, current->math); printf("\n*****************************\n"); }else{ printf("\n*****************************\n"); printf("資料不存在"); printf("\n*****************************\n"); } } //釋放所有節點 void freeAllNodes() { current = head->rlink; while (current != tail) { Student* temp = current; current = current->rlink; free(temp); } free(head); // 釋放 head 節點 } int main() { char mode ,sort_mode; init_f(); readStudentsFromFile("YunTechStudents2.txt"); printStudentList(); modify_list(); while(1){ printf("\n******* 選擇進行排序或是排名第n名的學生 *******\n" "*--------------------------------------*\n" "* 1 進行排序 2 選擇排名第n名的學生 3 結束 *\n" "\n請選擇功\能 ==> "); //不打成 功\能 在視窗中會變亂碼 mode = getche(); switch(mode){ case '1': printf("\n\n選擇排序方式\n" "*--------------------------------------*\n" "1 名字 2 英文成績 3 數學成績 \n" "\n請選擇排序方式 ==> "); sort_mode = getche(); switch(sort_mode){ case '1': printf("\n\n以名字進行排序\n"); sort_name(); printStudentList(); break; case '2': printf("\n\n以英文成績進行排序\n"); sort_eng(); printStudentList(); break; case '3': printf("\n\n以數學成績進行排序\n"); sort_math(); printStudentList(); break; default: printf("\n輸入錯誤\n"); break; } break; case '2': choose_subject(); break; case '3': printf("\n\n謝謝使用,Bye~\n"); freeAllNodes(); return 0; default: printf("\n輸入錯誤\n"); break; } } }