主程序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//结构体定义
struct stud_node {
int num;
char name[20];
int score;
struct stud_node * next;
};
//函数声明
struct stud_node * Create_Stu_Doc (void);
struct stud_node * InsertDoc (struct stud_node * head, struct stud_node * stud,int target);
struct stud_node * DeleteDoc (struct stud_node * head, int num);
void Print_Stu_Doc (struct stud_node * head);
int main (void){
struct stud_node *head, *p;
int choice, num, score,target;
char name[20];
int size = sizeof(struct stud_node);
do{
printf("1: Create 2: Insert 3: Delete 4: Print 0: Exit\n");
scanf("%d",&choice);
switch(choice){
case 1:
head = Create_Stu_Doc ();
break;
case 2:
if(head == NULL) {
printf("No Data Yet! Please Create A Node First\n");
break;
}
target = -1;
printf("Insert A Student's Data After Num ");
scanf("%d",&target);
putchar('\n');
printf("Input num, name and score: \n");
scanf("%d%s%d",&num,name,&score);
p = (struct stud_node*)malloc(size);
p->num = num;
strcpy(p->name,name);
p->score = score;
head = InsertDoc(head,p,target);
break;
case 3:
printf("Input num: \n");
scanf("%d",&num);
head = DeleteDoc (head,num);
break;
case 4:
Print_Stu_Doc (head);
break;
case 0:
break;
}
}while (choice != 0);
return 0;
}
链表的建立
函数的输入为空,返回值为strcut stud_node*
//按照输入顺序建立单向链表
struct stud_node * Create_Stu_Doc (void){
int num,score;
char name[20];
int size = sizeof (struct stud_node) ;
struct stud_node *head, *tail, *p;
head = tail = p =NULL;
printf("Input num, name and score: \n");
scanf("%d%s%d",&num,name,&score);
while (num != 0){
p = (struct stud_node*)malloc(size);
p->num = num;
strcpy(p->name,name);
p->score = score;
p->next = NULL;
if(head == NULL)
head = p;
else
tail->next = p;
tail = p;
scanf("%d%s%d",&num,name,&score);
}
return head;
}
由于需要新建一个链表,所以不需要输入。在定义的时候按照结构体里面的内容,定义int num,score;以及char name[20];等内容。通过sizeof来测量出链表的空间,以便动态内存申请。对于一个链表,需要有头(head)和尾(tail)以及指向结点的指针(p)。为了安全,让这些strcut stud_node *型的指针全部为NULL。#读入相应的数据。当编号不为0的时候,申请内存空间,然后让对应赋值(name需要用字符串处理函数进行处理)。对于指针head来说,如果是NULL的话,让他等于第一次申请时p的地址;否则由于已经有了链表而且tail指向上一个结点,通过tail->next = p;将上一个节点和新建的节点连接起来。再次读入数据判断是否结束,然后返回这个链表的头指针。
注意:链表的头一旦确定,不在改动,但是链表的尾会随着节点的变动而变动,指针p的作用是用来指向动态内存分配后的那个内存空间。
如果要反向建立结点,就使用p->next = head; head = p;
最后的结果就是头变成尾,不需要tail了(因为head其实承担了tail的作用)
链表的遍历
void Print_Stu_Doc (struct stud_node * head){
struct stud_node * ptr = head;
if(ptr == NULL) {
printf("\nNo Records!\n");
return ;
}
printf("\nThe Students' Records Are: \n");
printf("Num \t Name \t Score \n");
while( ptr != NULL ){
printf("%d \t %s \t %d \n",ptr->num,ptr->name,ptr->score);
ptr = ptr->next;
}
return ;
}
链表在遍历的时候,需要传入头的指针,没有返回值。原理就是头指针的值给ptr,如果ptr为NULL,那么就说明没有数据,直接return ;即可。如果不是NULL,那么就需要通过while循环遍历链表,每次遍历包括打印内容和让ptr指向下个结点,最终指向NULL的时候说明链表结束。
添加结点
struct stud_node * InsertDoc (struct stud_node * head, struct stud_node * stud,int target){
struct stud_node *ptr = head;
while ( target != ptr->num && ptr != NULL){
ptr = ptr->next;
}
if(ptr == NULL){
printf("Not Found\n");
return head;
}
stud->next = ptr->next;
ptr->next = stud;
return head;
}
首先传入的参数是两个指针head和stud,以及整型target。在主函数可以知道调用这个函数之前,对head是否为NULL做了一次判断,如果是NULL的话就要先创建一个链表,再执行插入的语句。传入的head是原来链表的头,为了遍历用。stud是新创建结点的地址,target代表插入到这个编号的后面。对链表进行遍历,如果找到target,ptr指向那个编号在的结点;如果没找到,输出Not Found。找到了以后,先将新增结点和后面的结点连接,在断开原来的连接,最终实现插入一个结点的目的。
删除结点
struct stud_node * DeleteDoc (struct stud_node * head, int num){
struct stud_node *ptr1, *ptr2;
while(head !=NULL && head->num==num){
ptr2=head;
head=head->next;
free(ptr2);
ptr2=NULL;
}
if (head == NULL){
printf("No Data\n");
return NULL;
}
ptr1=head;
ptr2=ptr1->next;
while (ptr2 != NULL){
if(ptr2->num==num){
ptr1->next=ptr2->next;
free(ptr2);
ptr2=NULL;
}else{
ptr1 = ptr2;
ptr2 = ptr1->next;
}
}
return head;
}
函数传入了头指针和删除结点的num。如果要删除一个头结点的话,只需要将头向后移动一个结点,然后free之前的头即可。如果要删除一个非头结点,那么需要让ptr1指向ptr2所指的结点的前一个结点。当ptr2指向要删除的结点时,让ptr1->next(注意不是ptr1)指向删除结点的下个结点即ptr2->next,然后free(ptr2)即可。
总结
以上便是对链表的基本操作,各个函数的功能可以随着具体的问题需求改变,传入的参数和返回的参数都可以做出改变。比如插入和删除的时候可以将函数定义成void类型。但是定义成struct stud_node * 类型可以后期修改时返回插入结点的指针,进行一些操作。
程序参考何钦铭与颜晖主编的C语言程序设计第三版11.3,并进行了一定的修改。如有不对,不合理之处,还望指出。