使用單鏈表數據結構存儲結構化的學生信息
一、題目描述
定義一個學生結構體,包含有:學號,姓名,成績。輸入N個學生的信息,並進行基本的鏈表操作。(更好的閱讀體驗,請在我的博客網站上閱讀此文:使用單鏈表數據結構存儲結構化的學生信息)
二、分析與解答
這是一道主要考察基礎知識點運用的題目,涉及到結構體、指針、字符數組等相關的概念。因此,要熟練的掌握這些知識點的定義與使用。
2.1學生結構體的定義
typedef struct student_node{
unsigned num; //學號,無符號整型
char name[10]; //姓名,字符數組
float score; //成績,浮點型
struct student_node *next;
}StudentNode, *PStuNode;
其中,struct關鍵字表示定義的是結構體數據類型,student_node是結構體類型名,花括號中依次列舉該類型的每個成員變量的類型及其名稱,每個成員變量的數據類型可以是基本數據類型也可以是自定義數據類型。一旦定義了一個結構體類型,就可以像基本數據類型一樣使用,定義該類型的變量、數組和指針。如下所示:
1)struct student_node stu_node; // 定義了一個student_node型的變量 stu_node;
2)struct student_node s[20]; //定義了一個student_node類型的數組s,用於存放20個該類型的學生數據。
3)struct student_node *p; //定義了一個student_node型的指針變量p;
4)爲指針p初始化賦值的方式,和基本數據類型一致: p = &stu_node。使用指針訪問結構體變量的成員可以使用如下兩種方式:
①(*結構體指針變量名).成員名; (*p).num;
②結構體指針變量名->成員名; p->num;
爲了簡化結構體變量的定義,可以使用C語言提供的類型別名定義語句typedef,爲結構體類型取一個別名。StudentNode, *PStuNode就是struct student_node的別名,然後可以使用student_node的別名StudentNode定義變量stu_node,可以使用PStuNode定義指向student_node類型的指針p。如果不再使用student_node定義變量,則可以在定義結構體類型的時候省去,如果需要使用到,則不可以省略去。
2.2 使用尾插法建立不帶頭結點的學生鏈表
//尾插法創建學生鏈表.
StudentNode * init_list_tail(){
int i=0,n;
StudentNode *head = NULL, *tail = NULL, *p;
//輸入N個學生的信息
scanf("%d",&n);
while(i++<n){
//申請節點所佔用的內存空間
if( (p = (StudentNode *)malloc(sizeof(StudentNode))) == NULL){
printf("memery is not available");
exit(1);
}
//錄入學生節點的基本信息
scanf("%u",&(p->num)); //輸入學號,無符號整形
getchar();//scanf//消除scanf函數不接收的 回車符(\n)
gets(p->name); //name中可以包含空格
scanf("%f",&(*p).score); //注意這裏的幾個括號,'.'的優先級高於'*',因此要打一個括號
p->next = NULL;
//當前申請的是第一個節點
if(head == NULL){
head = tail = p;
}else{
//鏈尾插值
tail->next = p;
tail = p;
}
}
return head;
}
init_list_tail()是返回值爲StudentNode型指針的函數。
gets(p->name)是輸入姓名字符串。在C語言中,輸入字符的方式有getchar()、scanf(“%s”,str)、gets(str),其中①scanf函數不能輸入空格;②getchar()函數是逐個字符輸入;③gets()會將’\n’作爲輸入終止符,因此要注意在這個函數前將回車符消除掉。
2.3 增加鏈表節點
//在鏈表頭部新增學生節點信息
int insert_stu_head(PStuNode *head){
PStuNode p;
//申請節點所佔用的內存空間
if( (p = (PStuNode)malloc(sizeof(StudentNode))) == NULL){ // 標註 p 之鄉的空間類型是 PStuNode。與 StudentNode * 相同的功能
printf("memery is not available");
return 0;
}
//錄入學生節點的基本信息
scanf("%u",&(p->num));
scanf("%s",p->name); // %s,這裏的字符串輸入不能有空格
scanf("%f",&(*p).score);
//頭插法插入一個節點
p->next = *head;
*head = p;
return 1; //只是返回標誌
}
malloc()是申請內存空間的函數,與之對應的是free()函數。p = (PStuNode)malloc(sizeof(StudentNode)))代表申請sizeof(StudentNode)大小的內存空間,並轉換爲PStuNode類型的指針賦給p。
2.3 刪除score<60的學生節點
//刪除score<60的學生節點
void del_stu_by_score(PStuNode *head){
PStuNode pre_p = *head, p = *head;
while(p){
//由於使用的是不帶頭結點的鏈表,因此,頭節點需要區別處理
if((p->score) < 60){
PStuNode temp = p;
if(p == *head){
*head = p->next;
pre_p = p = *head;
}else{
pre_p->next = p->next;
p = pre_p->next;
}
free(temp);
}else{
pre_p = p;
p = pre_p->next;
}
}
}
因爲刪除的節點可能會是首節點,可能需要改變首節點的指向,因此要傳入(PStuNode *head,二級指針)這個參數。由於使用的是不帶頭結點的鏈表,在做判斷的時候,需要區分第一個節點和其餘節點。
2.4 查詢學生姓名爲”xiaoming”的學生信息
StudentNode * search_stu_by_name(PStuNode head, char *str){
PStuNode p = head;
while(p){
if( strcmp(p->name,str) == 0 ){
p->next = NULL; //只返回p節點
return p;
}
p = p->next;
}
return NULL;
}
注意,這裏的傳參,是一個字符指針str。要注意字符指針和字符數組的區別,字符數組名是一個常量,字符指針變量本身是一個變量,用於存放字符串的首地址,字符串本身是存儲在以該首地址開始的一塊連續的內存空間中並以’\0’作爲字符串的結束。
常用的字符串處理函數由<string.h>提供支持。①strlen,字符串長度統計函數;②strcat,連接兩個字符串;③strcpy將一個字符串複製到另一個字符數組中;④strcmp,比較兩個字符串的大小;⑤strlwr,將字符串中大寫字母轉換成小寫字母;⑥strupr,將字符串中小寫字母轉換成大寫字母。
2.5 修改學生信息
//修改學生學號爲 modify_num 的學生姓名爲 'itour'。1:成功,0:未找到該人
int edit_stu_by_num(PStuNode head){
PStuNode p = head;
unsigned modify_num;
printf("請輸入待修改學生信息的學號: ");
scanf("%u",&modify_num);
while(p){
if(p->num == modify_num){
char modify_name[10]; //修改後的姓名
printf("請輸入num=%u的學生的姓名:",modify_num);
getchar(); //消除回車鍵的影響
gets(modify_name);
strcpy(p->name,modify_name);
return 1;
}
p = p->next;
}
return 0;
}
2.6 鏈表的遍歷訪問
void print_list(PStuNode head){
PStuNode p = head;
while(p){
printf("num: %u \t name: %s \t score: %.2f\n",p->num,p->name,p->score);
p = p->next;
}
}
注意輸入輸出的格式控制,unsigned 無符號整形,格式爲%u;float浮點類型,%.2f 代表輸出的浮點數帶有兩位小數;%s代表的是字符串。
三、總結
指針、結構體、字符串、數組,是C語言中最重要的幾個模塊。在編程開發中,這幾個知識點往往會配合使用,比如常見的有:指向字符串的指針、指向數組的指針、指向結構體的指針、字符數組等等。熟練掌握後,使用起來會很靈活,能實現的功能也是異常強大。
使用C語言可以很方便的控制計算機硬件,因此,它會經常的被用在操作系統、嵌入式、物聯網、驅動開發等領域。我們所熟知的操作系統,如Windows、Linux就是使用C語言進行開發的。操作系統的某些模塊開發會經常的涉及到鏈表、結構體,比如常見的內存管理模塊中,一個內存頁就是使用一個結構體進行描述,多個內存頁會使用鏈表進行連接起來,從而達到高效的使用。