【算法基础四】C语言小项目实战---通讯录管理系统(单链表)

✅作者简介:大家好,我是新小白2022,让我们一起学习,共同进步吧!
个人主页:新小白2022的CSDN博客
系列专栏:算法基础入门
如果觉得博主的文章还不错的话,请点赞+收藏⭐️+留言支持一下博主哦

系列文章目录

算法学习打卡

第一章  字符编码分类(算法基础一)

第二章   双飞本科学习动力,方向(算法基础二)

第三章   零基础入门算法应该学些什么(算法基础三)

第四章  小项目实战(通讯录管理系统) 

第五章 项目实战自我总结

第六章  快捷键操作(单行/多行注释) 


文章目录

目录

系列文章目录

文章目录

题目要求

一、程序设计思路(可以使用程序流程图)

1、思路:单链表建立通讯录

 2、测试用例

二、具体代码实现

1、确定头文件(依据所用函数确定)

2、各个功能函数的声明

3、主控函数的完善

4、把功能函数写好并进行调试和整合

4.1      显示界面

4.2      通讯录的初始化

4.3     建立通讯录(单链表的建立)(头插法建立)

4.4   建立通讯录(单链表的建立)(尾插法创建)

4.5    删除数据(根据姓名删除) 

4.6   修改信息

4.7    查找指定ID信息

4.8   仿头插法-->前插法(添加信息到指定位置)

4.9 仿尾插法-->后插法(添加信息到指定位置)

4.10   遍历通讯录

4.11    释放通讯录

4.12  用于将数组中的信息保存到文件中 (n个同学)

三、整合代码(大家一起学习)

四、程序运行结果及分析(以图片形式表现运行结果)

1、单链表的初始化

2.通讯录的建立(头插法和尾插法)

 3.删除联系人

4、修改联系人

5、查找联系人信息并输出

6、添加联系人(可以在指定ID的前边或后边添加)

 (1)在ID:002前插入新联系人

(2)在ID为002后添加新信息

7、输出联系人信息

8、清空联系人信息并退出

五、遇到的问题及创新之处

六、自我评价与总结


题目要求

每一个联系人包含编号、姓名、手机、qq等信息,通讯录管理系统可以方便实现对通讯录的管理,如增加联系人,查找某人的联系方式,删除联系人,修改某个人的联系信息等功能。要求用文件保存通讯录中联系人的信息,上交时要包括生成的通讯录文件(大家帮博主找找漏洞呀)

功能要求:1.添加 2.查找 3.删除 4.修改

包含信息每一个联系人包含编号、姓名、手机、qq等信息


一、程序设计思路(可以使用程序流程图)

1、思路:单链表建立通讯录

首先需要确定头文件(依据所用函数确定);

要用单链表进行数据的存储和指针域将通讯录连接起来;

接着对于各个功能函数的声明,

然后先把主控函数写完善,

最后要把功能函数写好并进行调试和整合。

设计思路图如下:(这张图不太清晰,请见谅呀!)

 2、测试用例

/*单链表----通讯录管理系统 */ 
//ID:001     姓名:小辉              电话:18736509288       QQ:2276876534
//ID: 002     姓名:小混              电话:17823487789       QQ:3399008875
//ID:003     姓名:小红              电话:18726539009       QQ:2267873465
//ID:004     姓名:小黄              电话:18767236554       QQ:2278765439
//ID:005     姓名:小红帽            电话:18726539009       QQ:2267873465
//ID:006     姓名:小安              电话:19872346547       QQ:2213987653
//ID:007     姓名:小黄毛            电话:18767236554       QQ:2278765439
//ID:008     姓名:小灰灰            电话:19823983392       QQ:2209183762

二、具体代码实现

1、确定头文件(依据所用函数确定)

#include 
#include 
#include 

typedef struct TongXun{
	char ID[15];
	char name[12];
	char tel[15];
	char QQ[15];
}datatype;

typedef struct node{
	datatype data;
	struct node *next;
}Node, *LinkNode;

LinkNode head =NULL; //先确定头结点 

2、各个功能函数的声明

/*功能函数*/
void Menu();				//0.显示界面 
void InitList();			//1.通讯录的初始化*** 

 							//2.建立通讯录(单链表的建立)*******
void CreateList_Head();  	//2.1头插法创建 
int JudgeID(LinkNode ptr1,LinkNode ptr2);//2.11判断ID是否重复 
void CreateList_Tail();  	//2.2尾插法创建 
 
void DelList();			//3.删除数据(根据姓名删除) ********

void UpdateList();  	//4.修改信息******
  
void SearchList();		//5.查找信息

 							//6.插入信息 ******
void InsertList_Pre(); 		//6.1前插法
void InputList(LinkNode ptr);			//6.11输入指定结点指针相关信息 
void InsertList_Back(); 	//6.2后插法


void TraverseList(); //7.遍历通讯录
void PrintList(LinkNode ptr); //7.1输出指定信息

void DestroyList();  //8.释放通讯录*** 

void save(LinkNode stu);	//9.用于将数组中的信息保存到文件中 (n个同学)
int NumList(LinkNode head);	//9.1 通过头指针,遍历单链表进行人数统计并返回 

3、主控函数的完善

/*主控函数*/
int  main(void)
{
	int choice;
	
	while (1)
	{
		Menu();
		printf("请选择菜单:");
		scanf("%d", &choice);
		switch (choice)
		{
		    case 1:		//初始化通讯录
					InitList(); break;
			case 2:		//建立通讯录
				{	   
						int num;
						printf("请选择建立方式(1.头插法 2.尾插法):");
						scanf("%d", &num);   /****下边的大括号不可以省略 ****/
						if (num == 1)	//头插法
						{
						   CreateList_Head();  break;
						}
							
						if (num == 2)	//尾插法
						{
							CreateList_Tail();  break;
						}
					
							
				}
			
			case 3:		//删除信息
					DelList(); break;
			
			case 4:		//修改信息
					UpdateList(); break;
				
		  	case 5:		//查找信息
					SearchList(); break;
		
		  	case 6:		//添加信息
		       {
			
					int num;
					printf("请选择插入方式(1.前插法 2.尾插法):");
					scanf("%d", &num);
					if (num == 1) //头插法
					{	
						InsertList_Pre(); break;
					} 
					if (num == 2) //尾插法
					{
					    InsertList_Back(); break;
					}
				
							
		      }
		
		   
			case 7:		//遍历通讯录
					TraverseList(); break;
		
			case 8:	//释放通讯录
					DestroyList(); break;
		
			
		    default: return 0;
		}
	}
	return 0;
} 

4、把功能函数写好并进行调试和整合

4.1      显示界面

void Menu()
{
	printf("\t\t\t****************************************************\n");
	printf("\t\t\t1.初始化通讯录                          2.建立通讯录\n");
	printf("\t\t\t3.删除联系人                            4.修改联系人\n");
	printf("\t\t\t5.查找联系人                            6.添加联系人\n");
	printf("\t\t\t7.输出通讯录                            8.释放通讯录\n");
	printf("\t\t\t***************其他按键视为退出**********************\n");
}

4.2      通讯录的初始化

//(建立头结点)
void InitList()
{
     head= (LinkNode)malloc(sizeof(Node));
     head->next = NULL;
     printf("初始化成功!\n");
     save(head);
}

4.3     建立通讯录(单链表的建立)(头插法建立)

2.1插入数据,建立通讯录(头插法)
首先建立新结点,为其动态开辟内存空间
接着为该结点的data域存入数据
然后经典头插代码(为让新结点插入头结点和首元结点之间) 
先让新结点的指针域指向原首元结点;然后让头结点的指针域指向该新结点
就是通过结点间的指针进行连接/相关联 

//2.1插入数据,建立通讯录(头插法)
//首先建立新结点,为其动态开辟内存空间
//接着为该结点的data域存入数据
//然后经典头插代码(为让新结点插入头结点和首元结点之间) 
//先让新结点的指针域指向原首元结点;然后让头结点的指针域指向该新结点
//就是通过结点间的指针进行连接/相关联 
void CreateList_Head()
{
    LinkNode ptr;
    LinkNode ptr2; 
    int item=1;		//用来控制循环(决定是否继续录入信息) 
    int n;
		 
    //需要实现:循环直至自行决定,不继续建立新的联系人
    //另写函数2.11,实现排除输入ID重复 
    while(item!=0)
    {
    	ptr = (LinkNode)malloc(sizeof(Node));//动态分配内存(新结点)
		
    
    	printf("请输入通讯信息:\n"); 
    	printf("请输入ID:");
    	scanf("%s",&(ptr->data.ID)); 
    		ptr2 = head->next;
    	n = JudgeID(ptr,ptr2); //ID有重复n=1;
    	//调用函数2.11进行判断ID是否重复 
    
    	while(n==1)
    	{
			printf("ID已存在请重新输入:\n");
			scanf("%s",&(ptr->data.ID)); 
			n = JudgeID(ptr,ptr2); //ID有重复n=1;	
		}
				
		    printf("请输入姓名:\n");
		    scanf("%s",&(ptr->data.name));
		    printf("请输入手机号:\n");
		    scanf("%s",&(ptr->data.tel));
		    printf("请输入QQ:\n"); 
		    scanf("%s",&(ptr->data.QQ));
		    	
		    //头插法的经典代码 
		    ptr->next = head->next;
		    head->next = ptr;
		    printf("是否继续录入,继续:1,退出:0\n");
		    scanf("%d",&item);
    	
	}
	save(head);
   return ;
} 
//2.11实现排除输入ID重复
//传入需要比对的结点,遍历单链表逐个比对ID
//要和首元结点的指针域指向的结点比较(次首元结点),就是新结点的指针域所指结点 
//如果有重复返回 1,否则返回0; 
int JudgeID(LinkNode ptr1,LinkNode ptr2)
{
	while(ptr2!=NULL)
	{
	  if(strcmp(ptr1->data.ID,ptr2->data.ID)==0) 
	     return 1;
	  else
	    ptr2 = ptr2->next; 
	}
	return 0;	
}

2.11实现排除输入ID重复
传入需要比对的结点,遍历单链表逐个比对ID
要和首元结点的指针域指向的结点比较(次首元结点),就是新结点的指针域所指结点 
如果有重复返回 1,否则返回0; 
 

int JudgeID(LinkNode ptr1,LinkNode ptr2)
{
    while(ptr2!=NULL)
    {
      if(strcmp(ptr1->data.ID,ptr2->data.ID)==0) 
         return 1;
      else
        ptr2 = ptr2->next; 
    }
    return 0;    
}

4.4   建立通讯录(单链表的建立)(尾插法创建)

首先先建立一个新结点和一个尾结点rear(一直指向尾结点)
依旧需要为新结点的data域赋值 
依旧让rear指向头结点
但是尾插法核心仍然是:
先让新结点作为rear的后继结点,然后让rear还指向尾结点; 

/*2.2尾插法创建 */

void CreateList_Tail()
{
	LinkNode  ptr, rear=head;	//定义新结点和尾节点 
	LinkNode  ptr2;
    int item = 1; //用来判断循环是否继续 
    int n;
    while(item ==1 )
	{
		ptr = (LinkNode)malloc(sizeof(Node));//动态分配内存(新结点)
		printf("请输入通讯信息:\n"); 
    	printf("请输入ID:");
    	scanf("%s",&(ptr->data.ID));
		  ptr2 = head->next;
	    n = JudgeID(ptr,ptr2); //ID有重复n=1;
    	//调用函数2.11进行判断ID是否重复 
    
    	while(n==1)		//如果加入的ID与之前重复,便一直循环重新输入 
    	{
			printf("ID已存在请重新输入:\n");
			scanf("%s",&(ptr->data.ID)); 
			n = JudgeID(ptr,ptr2); //ID有重复n=1;	
		}
    	printf("请输入姓名:\n");
		scanf("%s",&(ptr->data.name));
		printf("请输入手机号:\n");
		scanf("%s",&(ptr->data.tel));
		printf("请输入QQ:\n"); 
		scanf("%s",&(ptr->data.QQ));
    	//尾插法的核心
		  rear->next =ptr;
		  ptr->next = NULL;
		  rear = ptr;
		
		printf("是否继续录入,继续:1,退出:0\n");
		scanf("%d",&item);
	} 
	return ;
} 	

4.5    删除数据(根据姓名删除) 


关键点:单链表所有结点间通过结点指针的指向来联系
首先明确两个结点指针:p,q
分别用来 使整个单链表联系;找到需要删除信息的结点
然后需要保证-->单链表的完整连接性,
最后释放-->需要删除的结点信息 

void DelList()
{
   LinkNode p, q;
   p = head;
   q = head->next;
   char str[20];
   printf("请输入需要删除ID:\n");
   scanf("%s",&str);
   //可能一开始就是空链表,或者遍历到最后(通过指针q来判断) 
   while(q!=NULL && strcmp(q->data.ID,str)!=0) //遍历单链表,让结点指针q指向需要删除的结点 
   {
   	  q = q->next;
	  p = p->next; 
   }
   
   if(q==NULL)//遍历单链表找不到该信息 
   {
   	 	printf("该信息不存在\n");
   } 
   else
   {
   	   p->next = q->next;	//让p在单链表中保证其完整性 
   	   printf("该信息如下:\n");
   	   printf("ID为:%s\n",q->data.ID);
   	   printf("姓名为:%s\n",q->data.name);
   	   printf("手机号为:%s\n",q->data.tel);
   	   printf("QQ号为:%s\n",q->data.QQ);
   	   
	   printf("通讯信息删除成功\n"); 
	     free(q);
   } 
   save(head);
   
}

4.6   修改信息


先定义一个结点指针,让它指向首元结点
进行遍历,与输入的ID进行比较,找到该信息所在结点
然后进行(循环)修改信息:1.姓名 2.手机号 3.QQ号    

void UpdateList()
{
	LinkNode ptr = head->next;
	int item=1,n=0;  //item控制是否继续修改信息 
	  				//n用来判断是否修改该信息 
	char str[20];
	printf("请输入需要修改的ID:\n");
	scanf("%s",&str); 
	while(ptr!=NULL&&strcmp(ptr->data.ID,str)!=0)//遍历单链表,找和所输入ID一致的结点 
	{
		ptr = ptr->next;
	}
	if(ptr==NULL)
	{
		 printf("该信息不存在\n");
		 return ; 
	}
	  //能够找到该信息,就会进行修改 
	while(item==1) //item为1表示继续修改信息 
	{
		printf("是否要修改姓名:(是:1 否:0)\n");
		   if(scanf("%d",&n),n==1) 
		     scanf("%s",&ptr->data.name);
		printf("是否要修改手机号:(是:1 否:0)\n");
			if(scanf("%d",&n),n==1) 
		      scanf("%s",&ptr->data.tel);
		printf("是否要修改QQ号:(是:1 否:0)\n");
			if(scanf("%d",&n),n==1) 
		      scanf("%s",&ptr->data.QQ);
		printf("是否需要重新修改信息:(是:1 否:0)\n"); 
		scanf("%d",&item);
	}	
	save(head);
}

4.7    查找指定ID信息


先定义一个结点指针,指向首元结点
接着进行遍历单链表,找该信息所在结点
然后输出该信息 
 

void SearchList()
{
	LinkNode ptr = head->next;
	char str[20];
	printf("请输入需要查找的ID:\n");
	scanf("%s",&str); 
	while(ptr!=NULL&&strcmp(ptr->data.ID,str)!=0)//遍历单链表,找和所输入ID一致的结点 
	{
		ptr = ptr->next;
	}
	if(ptr==NULL)
	{
		 printf("该信息不存在\n");
		 return ; 
	}
	
	//如果找到该信息,就会输出
	
	printf("该通讯信息如下:\n"); 
	PrintList(ptr);
	
}

4.8   仿头插法-->前插法(添加信息到指定位置)


首先输入ID,确定新结点插入位置 (让结点指针p指向该位置)
接着为新结点 ptr 开辟内存并赋值
然后就是模仿-->经典的头插法
建立链表时,(头插法建立)已经写好的函数进行封装,接着在该函数进行调用; 

void InsertList_Pre() 		
{
	 LinkNode p,L,ptr;	  //结点指针L伴随着p一起移动
	 char str[20]; 
	 L = head;
	 p = head->next; 
	 
	 //需要输入ID,遍历单链表,将p指向该结点
	  
	 //情况1:如果p不是空指针,最后进行仿头指针的经典代码(将指针L假想成头指针) 
	 //情况2:如果p是空指针,此时便是头结点 
	 //可以为新结点 ptr 依次输入信息(记得查重复ID)
	 //因此两种情可以用一个经典代码(将结点指针L,假想为头结点)进行插入添加
	 
	printf("请输入ID(新信息会插入到该信息前):\n");
	 scanf("%s",&str);
	 while(p!=NULL && strcmp(p->data.ID,str)!=0)	//逻辑符号的使用 
	 {
	 	p = p->next;
	 	L = L->next; 	
	 }
	 	 
	 if(p==NULL) 
	 {
	 	printf("所输入的ID不存在\n");
	 	return;
	 } 
	 
	 
	//为新结点开辟内存并赋值
	  ptr =(LinkNode)malloc(sizeof(Node)); 
	  //函数InputList()   为指定结点指针-->输入相关信息 
	   InputList(ptr); 
   	   
   	//经典头插法核心代码
	   //仿头插法(将 L的指向看作头结点,p的指向看作首元结点)
	   ptr->next = L->next;
	   L->next =ptr;
	   printf("信息插入成功\n");
	   save(head);	    
}

/*6.11输入指定结点指针相关信息*/
功能中实现防输入重复ID 

void InputList(LinkNode ptr)
{
    int n=0;
    printf("请输入通讯信息:\n"); 
    printf("请输入ID:");
    scanf("%s",&(ptr->data.ID)); 
    LinkNode ptr2 = head->next;
      n = JudgeID(ptr,ptr2); //ID有重复n=1;
        //调用函数2.11进行判断ID是否重复 
    
    while(n==1)        //如果加入的ID与之前重复,便一直循环重新输入 
    { 
        printf("ID已存在请重新输入:\n");
        scanf("%s",&(ptr->data.ID)); 
        n = JudgeID(ptr,ptr2); //ID有重复n=1;    
    }
                
            printf("请输入姓名:\n");
            scanf("%s",&(ptr->data.name));
            printf("请输入手机号:\n");
            scanf("%s",&(ptr->data.tel));
            printf("请输入QQ:\n"); 
            scanf("%s",&(ptr->data.QQ));
            
}         

4.9 仿尾插法-->后插法(添加信息到指定位置)

首先让指针结点遍历单链表,来找指定ID的位置
然后创建新结点,为其开辟动态内存并赋值
最后仿尾插法运用经典代码 

结点指针 p,L;分别用来作为当前遍历的首元结点,总是指向当前的尾结点
就是用指针p来遍历单链表
当p为NULL时,即没有找到指定ID,将此时L作为尾结点进行处理 
当p不是NULL,即找到指定ID,将此时p作为尾结点进行处理 

void InsertList_Back()
{
	LinkNode p,L,ptr;	  //结点指针L伴随着p一起移动
	LinkNode node; 	//暂存所指结点后的结点(以防出现单链表中途插入新信息) 
	char str[20]; 
	L = head;
	p = head->next; 
	
	printf("请输入ID(新信息会插入到该信息前):\n");
	scanf("%s",&str);
	while(p!=NULL && strcmp(p->data.ID,str)!=0)	//逻辑符号的使用 
	{
	 	p = p->next;
	 	L = L->next; 	
	}
	
	 
	if(p == NULL) 
	{
	  printf("所输入的ID不存在\n");
	   return;
	 /* 
	  L->next = ptr;
	  L = ptr;
	  ptr = NULL;
	*/		
	} 

		//为新结点开辟内存并赋值
	  ptr =(LinkNode)malloc(sizeof(Node)); 
     	//函数InputList()   为指定结点指针-->输入相关信息 
	   InputList(ptr);
	   
	   
	   //暂存 所指结点后的结点(以防出现单链表中途插入新信息)
	    node = p->next; 
	    
	   //经典尾插法的核心代码
	   p->next = ptr;
	   p = ptr;
	   
	   //保证插入后单链表的完整性 
	   ptr->next = node;
	  // ptr = NULL;   //仿尾插法,可能所要插入的ID不在单链表的最后	
	
	save(head);
}	

4.10   遍历通讯录

void TraverseList()
{
	LinkNode p;
	if (head == NULL)
	{
		printf("通讯录为空!\n");
		return;
	}
	else
	{
		p = head->next;
		printf("通讯录中全部信息如下:\n");
		while (p)
		{
			PrintList(p);
			p = p->next;
		}
	}
}
//7.1输出指定信息
void PrintList(LinkNode ptr)
{
    printf("ID:%-15s\t", ptr->data.ID);
	printf("姓名:%-10s\t", ptr->data.name);
	printf("电话:%-15s\t", ptr->data.tel);
	printf("QQ:%-15s\n", ptr->data.QQ);

} 

4.11    释放通讯录


需要释放内存后,接着把指针指向NULL
释放头结点,首元结点和遍历单链表所指向的结点 
先定义两个结点指针 p,t;
分别用来遍历单链表,暂时指向所遍历之处的结点

void DestroyList()
{
	LinkNode p, t;
	p = head->next; 
	while(p!= NULL) //遍历单链表,进行内存释放,数据置空 
	{
		t = p;
		free(t);
		t = NULL; 
		p = p->next ;
	}
	//遍历完单链表,此时p为空指针
	free(p);
	free(head);
	head = NULL;
	
	printf("单链表清空成功\n");	
	 save(head);
} 

4.12  用于将数组中的信息保存到文件中 (n个同学)


调用fwrite()函数 ,将数组中的信息存入文件中 
了解exit()函数 ,用来终止正在执行的程序

void save(LinkNode stu)
{
	FILE *fp;
	int n;
	n = NumList(stu);
	if((fp = fopen("D:\\student1.txt","w")) == NULL) 
	{
		printf("打开文件失败\n");
		exit(0);
	}
	
	fwrite(stu , n*sizeof(Node) , 1,fp); //一个学生信息,用一个结构体数组存储 
	fclose(fp); 
}
//9.1 通过头指针,遍历单链表进行人数统计并返回 
int NumList(LinkNode head)
{
	int num=0;
	LinkNode L;
	L = head->next;
	while(L!=NULL)
	{
	   num++;
	   L =L->next;
	}
	
	return num;
}

三、整合代码(大家一起学习)

/*单链表----通讯录管理系统 */ 
//ID:001     姓名:小辉              电话:18736509288       QQ:2276876534
//ID: 002     姓名:小混              电话:17823487789       QQ:3399008875
//ID:003     姓名:小红              电话:18726539009       QQ:2267873465
//ID:004     姓名:小黄              电话:18767236554       QQ:2278765439
//ID:005     姓名:小红帽            电话:18726539009       QQ:2267873465
//ID:006     姓名:小安              电话:19872346547       QQ:2213987653
//ID:007     姓名:小黄毛            电话:18767236554       QQ:2278765439
//ID:008     姓名:小灰灰            电话:19823983392       QQ:2209183762
/*一、功能要求:
 1.添加 2.查找 3.删除 4.修改 */ 
/*二、包含信息 
 每一个联系人包含编号、姓名、手机、qq等信息*/ 
#include 
#include 
#include 

typedef struct TongXun{
	char ID[15];
	char name[12];
	char tel[15];
	char QQ[15];
}datatype;

typedef struct node{
	datatype data;
	struct node *next;
}Node, *LinkNode;

LinkNode head =NULL; //先确定头结点 

/*功能函数*/
void Menu();				//0.显示界面 
void InitList();			//1.通讯录的初始化*** 

 							//2.建立通讯录(单链表的建立)*******
void CreateList_Head();  	//2.1头插法创建 
int JudgeID(LinkNode ptr1,LinkNode ptr2);//2.11判断ID是否重复 
void CreateList_Tail();  	//2.2尾插法创建 
 
void DelList();			//3.删除数据(根据姓名删除) ********

void UpdateList();  	//4.修改信息******
  
void SearchList();		//5.查找信息

 							//6.插入信息 ******
void InsertList_Pre(); 		//6.1前插法
void InputList(LinkNode ptr);			//6.11输入指定结点指针相关信息 
void InsertList_Back(); 	//6.2后插法


void TraverseList(); //7.遍历通讯录
void PrintList(LinkNode ptr); //7.1输出指定信息

void DestroyList();  //8.释放通讯录*** 

void save(LinkNode stu);	//9.用于将数组中的信息保存到文件中 (n个同学)
int NumList(LinkNode head);	//9.1 通过头指针,遍历单链表进行人数统计并返回 

/*主控函数*/
int  main(void)
{
	int choice;
	
	while (1)
	{
		Menu();
		printf("请选择菜单:");
		scanf("%d", &choice);
		switch (choice)
		{
		    case 1:		//初始化通讯录
					InitList(); break;
			case 2:		//建立通讯录
				{	   
						int num;
						printf("请选择建立方式(1.头插法 2.尾插法):");
						scanf("%d", &num);   /****下边的大括号不可以省略 ****/
						if (num == 1)	//头插法
						{
						   CreateList_Head();  break;
						}
							
						if (num == 2)	//尾插法
						{
							CreateList_Tail();  break;
						}
					
							
				}
			
			case 3:		//删除信息
					DelList(); break;
			
			case 4:		//修改信息
					UpdateList(); break;
				
		  	case 5:		//查找信息
					SearchList(); break;
		
		  	case 6:		//添加信息
		       {
			
					int num;
					printf("请选择插入方式(1.前插法 2.尾插法):");
					scanf("%d", &num);
					if (num == 1) //头插法
					{	
						InsertList_Pre(); break;
					} 
					if (num == 2) //尾插法
					{
					    InsertList_Back(); break;
					}
				
							
		      }
		
		   
			case 7:		//遍历通讯录
					TraverseList(); break;
		
			case 8:	//释放通讯录
					DestroyList(); break;
		
			
		    default: return 0;
		}
	}
	return 0;
} 

/*0.显示界面*/ 
void Menu()
{
	printf("\t\t\t****************************************************\n");
	printf("\t\t\t1.初始化通讯录                          2.建立通讯录\n");
	printf("\t\t\t3.删除联系人                            4.修改联系人\n");
	printf("\t\t\t5.查找联系人                            6.添加联系人\n");
	printf("\t\t\t7.输出通讯录                            8.释放通讯录\n");
	printf("\t\t\t***************其他按键视为退出**********************\n");
}
/*1.通讯录的初始化*/
//(建立头结点)
void InitList()
{
     head= (LinkNode)malloc(sizeof(Node));
     head->next = NULL;
     printf("初始化成功!\n");
     save(head);
}



/*2.建立通讯录(单链表的建立)*/
//2.1插入数据,建立通讯录(头插法)
//首先建立新结点,为其动态开辟内存空间
//接着为该结点的data域存入数据
//然后经典头插代码(为让新结点插入头结点和首元结点之间) 
//先让新结点的指针域指向原首元结点;然后让头结点的指针域指向该新结点
//就是通过结点间的指针进行连接/相关联 
void CreateList_Head()
{
    LinkNode ptr;
    LinkNode ptr2; 
    int item=1;		//用来控制循环(决定是否继续录入信息) 
    int n;
		 
    //需要实现:循环直至自行决定,不继续建立新的联系人
    //另写函数2.11,实现排除输入ID重复 
    while(item!=0)
    {
    	ptr = (LinkNode)malloc(sizeof(Node));//动态分配内存(新结点)
		
    
    	printf("请输入通讯信息:\n"); 
    	printf("请输入ID:");
    	scanf("%s",&(ptr->data.ID)); 
    		ptr2 = head->next;
    	n = JudgeID(ptr,ptr2); //ID有重复n=1;
    	//调用函数2.11进行判断ID是否重复 
    
    	while(n==1)
    	{
			printf("ID已存在请重新输入:\n");
			scanf("%s",&(ptr->data.ID)); 
			n = JudgeID(ptr,ptr2); //ID有重复n=1;	
		}
				
		    printf("请输入姓名:\n");
		    scanf("%s",&(ptr->data.name));
		    printf("请输入手机号:\n");
		    scanf("%s",&(ptr->data.tel));
		    printf("请输入QQ:\n"); 
		    scanf("%s",&(ptr->data.QQ));
		    	
		    //头插法的经典代码 
		    ptr->next = head->next;
		    head->next = ptr;
		    printf("是否继续录入,继续:1,退出:0\n");
		    scanf("%d",&item);
    	
	}
	save(head);
   return ;
} 
//2.11实现排除输入ID重复
//传入需要比对的结点,遍历单链表逐个比对ID
//要和首元结点的指针域指向的结点比较(次首元结点),就是新结点的指针域所指结点 
//如果有重复返回 1,否则返回0; 
int JudgeID(LinkNode ptr1,LinkNode ptr2)
{
	while(ptr2!=NULL)
	{
	  if(strcmp(ptr1->data.ID,ptr2->data.ID)==0) 
	     return 1;
	  else
	    ptr2 = ptr2->next; 
	}
	return 0;	
}
			
/*
   2.建立通讯录(单链表的建立)
//2.1插入数据,建立通讯录(头插法)
void CreateList_Head()
{
	List *node; //插入节点
	int count = 0;
	int flag = 1;//用于判断是否继续输入下一条记录
	while (flag)
	{
		count++;
		node= (List *)malloc(sizeof(List));  //为新建的结点开辟内存 
		printf("插入第%d条记录:\n", count);
		printf("ID:");
		scanf("%s", &(node->data.ID));
		
		//判断ID是否重复
		List *p = L->next;
		while (p)
		{
			if (strcmp(p->data.ID, node->data.ID) == 0)
			{
				printf("ID重复,请重新输入!\n");
				printf("ID:");
				scanf("%s", &(node->data.ID));
			}
			else
				p = p->next;
		}
		printf("姓名:");
		scanf("%s", &(node->data.name));
		printf("电话:");
		scanf("%s", &(node->data.tel));
		printf("QQ:");
		scanf("%s", &(node->data.QQ));
		
		//头插法的核心 代码 
		node->next = L->next; //对于新建立的结点,先确定其指针域,再确定头指针的指向 
		L->next = node;
		printf("是否继续录入(1.继续 0.完成录入):");
		scanf("%d", &flag);
		if (flag == 0)
			break;
	}
}
*/
/*2.2尾插法创建 */
//首先先建立一个新结点和一个尾结点rear(一直指向尾结点)
//依旧需要为新结点的data域赋值 
//依旧让rear指向头结点
//但是尾插法核心仍然是:
//先让新结点作为rear的后继结点,然后让rear还指向尾结点; 
void CreateList_Tail()
{
	LinkNode  ptr, rear=head;	//定义新结点和尾节点 
	LinkNode  ptr2;
    int item = 1; //用来判断循环是否继续 
    int n;
    while(item ==1 )
	{
		ptr = (LinkNode)malloc(sizeof(Node));//动态分配内存(新结点)
		printf("请输入通讯信息:\n"); 
    	printf("请输入ID:");
    	scanf("%s",&(ptr->data.ID));
		  ptr2 = head->next;
	    n = JudgeID(ptr,ptr2); //ID有重复n=1;
    	//调用函数2.11进行判断ID是否重复 
    
    	while(n==1)		//如果加入的ID与之前重复,便一直循环重新输入 
    	{
			printf("ID已存在请重新输入:\n");
			scanf("%s",&(ptr->data.ID)); 
			n = JudgeID(ptr,ptr2); //ID有重复n=1;	
		}
    	printf("请输入姓名:\n");
		scanf("%s",&(ptr->data.name));
		printf("请输入手机号:\n");
		scanf("%s",&(ptr->data.tel));
		printf("请输入QQ:\n"); 
		scanf("%s",&(ptr->data.QQ));
    	//尾插法的核心
		  rear->next =ptr;
		  ptr->next = NULL;
		  rear = ptr;
		
		printf("是否继续录入,继续:1,退出:0\n");
		scanf("%d",&item);
	} 
	return ;
} 	

/*3.删除数据(根据姓名删除)
void DelList()
{
	LinkNode p = head->next;
	LinkNode q = head;
	char ID[10];
	printf("请输入要删除的ID:");
	scanf("%s", &ID);
	while (p && strcmp(p->data.ID, ID) != 0)//找到ID一样的,让P指向该结点 
	{
		p = p->next;
		q = q->next;
	}
	if (p!= NULL)
	{
		q->next = p->next;
		printf("删除成功!\n");
		printf("被删除数据的信息为:\n");
		printf("ID:%s\n", p->data.ID);
		printf("姓名:%s\n", p->data.name);
		printf("电话:%s\n", p->data.tel);
		printf("QQ:%s\n", p->data.QQ);
		free(p);
	}
	else
	{
		printf("通讯录中不存在此人信息!\n");
	}
}
*/
/*3.删除数据(根据姓名删除)*/ 
//关键点:单链表所有结点间通过结点指针的指向来联系
//首先明确两个结点指针:p,q
//分别用来 使整个单链表联系;找到需要删除信息的结点
//然后需要保证-->单链表的完整连接性,
//最后释放-->需要删除的结点信息 
void DelList()
{
   LinkNode p, q;
   p = head;
   q = head->next;
   char str[20];
   printf("请输入需要删除ID:\n");
   scanf("%s",&str);
   //可能一开始就是空链表,或者遍历到最后(通过指针q来判断) 
   while(q!=NULL && strcmp(q->data.ID,str)!=0) //遍历单链表,让结点指针q指向需要删除的结点 
   {
   	  q = q->next;
	  p = p->next; 
   }
   
   if(q==NULL)//遍历单链表找不到该信息 
   {
   	 	printf("该信息不存在\n");
   } 
   else
   {
   	   p->next = q->next;	//让p在单链表中保证其完整性 
   	   printf("该信息如下:\n");
   	   printf("ID为:%s\n",q->data.ID);
   	   printf("姓名为:%s\n",q->data.name);
   	   printf("手机号为:%s\n",q->data.tel);
   	   printf("QQ号为:%s\n",q->data.QQ);
   	   
	   printf("通讯信息删除成功\n"); 
	     free(q);
   } 
   save(head);
   
}

/*4.修改信息*/
//先定义一个结点指针,让它指向首元结点
//进行遍历,与输入的ID进行比较,找到该信息所在结点
//然后进行(循环)修改信息:1.姓名 2.手机号 3.QQ号		
void UpdateList()
{
	LinkNode ptr = head->next;
	int item=1,n=0;  //item控制是否继续修改信息 
	  				//n用来判断是否修改该信息 
	char str[20];
	printf("请输入需要修改的ID:\n");
	scanf("%s",&str); 
	while(ptr!=NULL&&strcmp(ptr->data.ID,str)!=0)//遍历单链表,找和所输入ID一致的结点 
	{
		ptr = ptr->next;
	}
	if(ptr==NULL)
	{
		 printf("该信息不存在\n");
		 return ; 
	}
	  //能够找到该信息,就会进行修改 
	while(item==1) //item为1表示继续修改信息 
	{
		printf("是否要修改姓名:(是:1 否:0)\n");
		   if(scanf("%d",&n),n==1) 
		     scanf("%s",&ptr->data.name);
		printf("是否要修改手机号:(是:1 否:0)\n");
			if(scanf("%d",&n),n==1) 
		      scanf("%s",&ptr->data.tel);
		printf("是否要修改QQ号:(是:1 否:0)\n");
			if(scanf("%d",&n),n==1) 
		      scanf("%s",&ptr->data.QQ);
		printf("是否需要重新修改信息:(是:1 否:0)\n"); 
		scanf("%d",&item);
	}	
	save(head);
}

/*5.查找指定ID信息*/
//先定义一个结点指针,指向首元结点
//接着进行遍历单链表,找该信息所在结点
//然后输出该信息 
void SearchList()
{
	LinkNode ptr = head->next;
	char str[20];
	printf("请输入需要查找的ID:\n");
	scanf("%s",&str); 
	while(ptr!=NULL&&strcmp(ptr->data.ID,str)!=0)//遍历单链表,找和所输入ID一致的结点 
	{
		ptr = ptr->next;
	}
	if(ptr==NULL)
	{
		 printf("该信息不存在\n");
		 return ; 
	}
	
	//如果找到该信息,就会输出
	
	printf("该通讯信息如下:\n"); 
	PrintList(ptr);
	
}


/*5.插入数据(前插法)
void InsertList_Pre()
{
   LinkNode p, q, r, node;
	p = head->next;
	q = head;
	char ID[10];
	printf("请输入要插入的位置(输入该位置的ID):");
	scanf("%s", &ID);//结点指针p用来遍历指向 所要添加的位置 
	while (p && strcmp(p->data.ID, ID) != 0)
	{
		p = p->next;
		q = q->next;
	}
	
	if (p != NULL)
	{

		//给新节点分配空间
		node = (LinkNode)malloc(sizeof(Node));
		//录入新节点数据
		printf("请输入新节点信息:\n");
	   judge:	
			printf("ID:");
			scanf("%s", &(node->data.ID));
			//判断ID是否重复
		    r = head->next;
		    while (r && strcmp(r->data.ID, node->data.ID) != 0)
			{
				r = r->next;
		    }
	  if (r != NULL)
	  {
		  printf("ID重复,请重新输入!\n");
		  //free(r); 这里不能free(r),如果释放r,会失去r的下一个节点的信息,链表会被破坏
		  goto judge;
	  }
	  printf("姓名:");
	  scanf("%s", &(node->data.name));
	  printf("电话:");
	  scanf("%s", &(node->data.tel));
	  printf("QQ:");
	  scanf("%s", &(node->data.QQ));
	}
	
	
	
	else
	{
		//如果没有该ID
		int choice1;
		printf("通讯录中未找到该ID,是否使用默认方式将新节点插入到最后(1.是 0.否):");
		scanf("%d", &choice1);
		if (choice1 == 1)
		{
			//给新节点分配空间
			node = (LinkNode)malloc(sizeof(Node));
			//给新节点输入数据
			printf("请输入新节点信息:\n");
		judge1:
			printf("ID:");
			scanf("%s", &(node->data.ID));
			//判断ID是否重复
			r = head->next;
			while (r && strcmp(r->data.ID, node->data.ID) != 0)
			{
				r = r->next;
			}
			if (r != NULL) //ID重复
			{
				printf("ID重复,请重新输入!\n");
				goto judge1;
			}
			printf("姓名:");
			scanf("%s", &(node->data.name));
			printf("电话:");
			scanf("%s", &(node->data.tel));
			printf("QQ:");
			scanf("%s", &(node->data.QQ));
		}
		else
			return;
	}
	node->next = q->next;
	q->next = node;
	printf("插入成功!\n");
}
*/ 
/*6.1仿头插法-->前插法(添加信息到指定位置)*/
//首先输入ID,确定新结点插入位置 (让结点指针p指向该位置)
//接着为新结点 ptr 开辟内存并赋值
//然后就是模仿-->经典的头插法
//建立链表时,(头插法建立)已经写好的函数进行封装,接着在该函数进行调用; 
void InsertList_Pre() 		
{
	 LinkNode p,L,ptr;	  //结点指针L伴随着p一起移动
	 char str[20]; 
	 L = head;
	 p = head->next; 
	 
	 //需要输入ID,遍历单链表,将p指向该结点
	  
	 //情况1:如果p不是空指针,最后进行仿头指针的经典代码(将指针L假想成头指针) 
	 //情况2:如果p是空指针,此时便是头结点 
	 //可以为新结点 ptr 依次输入信息(记得查重复ID)
	 //因此两种情可以用一个经典代码(将结点指针L,假想为头结点)进行插入添加
	 
	printf("请输入ID(新信息会插入到该信息前):\n");
	 scanf("%s",&str);
	 while(p!=NULL && strcmp(p->data.ID,str)!=0)	//逻辑符号的使用 
	 {
	 	p = p->next;
	 	L = L->next; 	
	 }
	 	 
	 if(p==NULL) 
	 {
	 	printf("所输入的ID不存在\n");
	 	return;
	 } 
	 
	 
	//为新结点开辟内存并赋值
	  ptr =(LinkNode)malloc(sizeof(Node)); 
	  //函数InputList()   为指定结点指针-->输入相关信息 
	   InputList(ptr); 
   	   
   	//经典头插法核心代码
	   //仿头插法(将 L的指向看作头结点,p的指向看作首元结点)
	   ptr->next = L->next;
	   L->next =ptr;
	   printf("信息插入成功\n");
	   save(head);	    
} 
/*6.11输入指定结点指针相关信息*/
//功能中实现防输入重复ID 
void InputList(LinkNode ptr)
{
	int n=0;
	printf("请输入通讯信息:\n"); 
    printf("请输入ID:");
    scanf("%s",&(ptr->data.ID)); 
    LinkNode ptr2 = head->next;
      n = JudgeID(ptr,ptr2); //ID有重复n=1;
    	//调用函数2.11进行判断ID是否重复 
    
    while(n==1)		//如果加入的ID与之前重复,便一直循环重新输入 
    { 
		printf("ID已存在请重新输入:\n");
		scanf("%s",&(ptr->data.ID)); 
		n = JudgeID(ptr,ptr2); //ID有重复n=1;	
	}
				
		    printf("请输入姓名:\n");
		    scanf("%s",&(ptr->data.name));
		    printf("请输入手机号:\n");
		    scanf("%s",&(ptr->data.tel));
		    printf("请输入QQ:\n"); 
		    scanf("%s",&(ptr->data.QQ));
			
}		 
/*6.2仿尾插法-->后插法(添加信息到指定位置)*/
//首先让指针结点遍历单链表,来找指定ID的位置
//然后创建新结点,为其开辟动态内存并赋值
//最后仿尾插法运用经典代码 

//结点指针 p,L;分别用来作为当前遍历的首元结点,总是指向当前的尾结点
//就是用指针p来遍历单链表
//当p为NULL时,即没有找到指定ID,将此时L作为尾结点进行处理 
//当p不是NULL,即找到指定ID,将此时p作为尾结点进行处理 
void InsertList_Back()
{
	LinkNode p,L,ptr;	  //结点指针L伴随着p一起移动
	LinkNode node; 	//暂存所指结点后的结点(以防出现单链表中途插入新信息) 
	char str[20]; 
	L = head;
	p = head->next; 
	
	printf("请输入ID(新信息会插入到该信息前):\n");
	scanf("%s",&str);
	while(p!=NULL && strcmp(p->data.ID,str)!=0)	//逻辑符号的使用 
	{
	 	p = p->next;
	 	L = L->next; 	
	}
	
	 
	if(p == NULL) 
	{
	  printf("所输入的ID不存在\n");
	   return;
	 /* 
	  L->next = ptr;
	  L = ptr;
	  ptr = NULL;
	*/		
	} 

		//为新结点开辟内存并赋值
	  ptr =(LinkNode)malloc(sizeof(Node)); 
     	//函数InputList()   为指定结点指针-->输入相关信息 
	   InputList(ptr);
	   
	   
	   //暂存 所指结点后的结点(以防出现单链表中途插入新信息)
	    node = p->next; 
	    
	   //经典尾插法的核心代码
	   p->next = ptr;
	   p = ptr;
	   
	   //保证插入后单链表的完整性 
	   ptr->next = node;
	  // ptr = NULL;   //仿尾插法,可能所要插入的ID不在单链表的最后	
	
	save(head);
}	


/*7.遍历通讯录*/
void TraverseList()
{
	LinkNode p;
	if (head == NULL)
	{
		printf("通讯录为空!\n");
		return;
	}
	else
	{
		p = head->next;
		printf("通讯录中全部信息如下:\n");
		while (p)
		{
			PrintList(p);
			p = p->next;
		}
	}
}

//7.1输出指定信息
void PrintList(LinkNode ptr)
{
    printf("ID:%-15s\t", ptr->data.ID);
	printf("姓名:%-10s\t", ptr->data.name);
	printf("电话:%-15s\t", ptr->data.tel);
	printf("QQ:%-15s\n", ptr->data.QQ);

} 
/*8.释放通讯录*/ 
//需要释放内存后,接着把指针指向NULL
//释放头结点,首元结点和遍历单链表所指向的结点 
//先定义两个结点指针 p,t;
//分别用来遍历单链表,暂时指向所遍历之处的结点 
void DestroyList()
{
	LinkNode p, t;
	p = head->next; 
	while(p!= NULL) //遍历单链表,进行内存释放,数据置空 
	{
		t = p;
		free(t);
		t = NULL; 
		p = p->next ;
	}
	//遍历完单链表,此时p为空指针
	free(p);
	free(head);
	head = NULL;
	
	printf("单链表清空成功\n");	
	 save(head);
} 

/* 9.用于将数组中的信息保存到文件中 (n个同学)*/ 
//调用fwrite()函数 ,将数组中的信息存入文件中 
//了解exit()函数 ,用来终止正在执行的程序 
void save(LinkNode stu)
{
	FILE *fp;
	int n;
	n = NumList(stu);
	if((fp = fopen("D:\\student1.txt","w")) == NULL) 
	{
		printf("打开文件失败\n");
		exit(0);
	}
	
	fwrite(stu , n*sizeof(Node) , 1,fp); //一个学生信息,用一个结构体数组存储 
	fclose(fp); 
}
//9.1 通过头指针,遍历单链表进行人数统计并返回 
int NumList(LinkNode head)
{
	int num=0;
	LinkNode L;
	L = head->next;
	while(L!=NULL)
	{
	   num++;
	   L =L->next;
	}
	
	return num;
}

、程序运行结果及分析(以图片形式表现运行结果

1、单链表的初始化

【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第1张图片

2.通讯录的建立(头插法和尾插法)

 3.删除联系人

【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第2张图片

【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第3张图片

4、修改联系人

【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第4张图片

5、查找联系人信息并输出

 【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第5张图片

6、添加联系人(可以在指定ID的前边或后边添加)

 (1)在ID:002前插入新联系人

【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第6张图片

【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第7张图片

(2)在ID为002后添加新信息

【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第8张图片

 【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第9张图片

7、输出联系人信息

【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第10张图片

8、清空联系人信息并退出

【算法基础四】C语言小项目实战---通讯录管理系统(单链表)_第11张图片

、遇到的问题及创新之处

1、显示界面可以进行创新。页面的前景颜色和背景颜色的调整,按照#include库函数中颜色函数system(color XY)的使用

2、通讯录的初始化的注意事项:由于采用单链表的方法进行通讯录的写法,和结构体数组的差异就显现出来了。此时初始化单链表实质就是创建一个头结点,记得把该结点的指针域置为NULL

3、建立通讯录(单链表的建立)创新之处及注意事项。

(1)我采用的建立单链表可以用头插法建立也可以用尾插法建立。在这里我用一个函数进行选项的选择(1或0),决定使用头插还是尾插法,接着进行输入信息就可以了,此时我加入ID重复函数进行多次循环,直至输入不重复为止。

(2再一次创建单链表中,不仅仅可以进行一个新联系人信息的建立,可以进行一个新联系人建立后紧接着确定是否继续建立,接着进行新联系人的信息输入,这样就是一个循环判断就可以完成

(3)浅析头插法与尾插法的核心代码:头插法,主要盯准头结点(头指针head);(新结点插在头结点与原首元结点之间)先确定新结点的指针域,接着就是连接-->头结点 尾插法,主要盯准尾结点(指针rear);(新结点接在尾结点后边,作为现在的尾结点)先将新结点作为rear的后继结点,接着rear总是指向尾结点(尾结点的指针域总是NULL)

(4)难点突破:实现排除输入ID重复。首先自己写一个防止输入ID重复,然后想让再次输入ID重复时,循环判断直至建立新联系人时与该通讯录中的ID没有重复才允许继续输入信息。

(5)函数的封装及循环判断是否继续建立新信息。

 4、删除数据(根据姓名删除)

关键点:单链表所有结点间通过结点指针的指向来联系接着进行循环遍历单链表找信息,看是否可以找到需要删除ID所在的结点,接着进行删除或输出未找到该信息和再次输入需要删除的ID 。

5、修改信息

先定义一个结点指针,让它指向首元结点。进行遍历,与输入的ID进行比较,找到该信息所在结点。然后进行(循环)修改信息:1.姓名 2.手机号 3.QQ号。

(创新)进行循环判断是都继续修改信息

6、查找指定ID信息

先定义一个结点指针,指向首元结点;接着进行遍历单链表,找该信息所在结点;然后输出该信息

7、添加信息

(1)仿头插法-->前插法(添加信息到指定位置)

首先输入ID,确定新结点插入位置 (让结点指针p指向该位置)接着为新结点 ptr 开辟内存并赋值然后就是模仿-->经典的头插法建立链表时,(头插法建立)已经写好的函数进行封装,接着在该函数进行调用;

(遇到的问题及解决)需要输入ID,遍历单链表,将p指向该结点

情况1:如果p不是空指针,最后进行仿头指针的经典代码(将指针L假想成头指针) 情况2:如果p是空指针,此时便是头结点 ;可以为新结点 ptr 依次输入信息(记得查重复ID)因此两种情可以用一个经典代码(将结点指针L,假想为头结点)进行插入添加

(2)仿尾插法-->后插法(添加信息到指定位置)

首先让指针结点遍历单链表,来找指定ID的位置;然后创建新结点,为其开辟动态内存并赋值;最后仿尾插法运用经典代码

结点指针 p,L;分别用来作为当前遍历的首元结点,总是指向当前的尾结点

就是用指针p来遍历单链表当p为NULL时,即没有找到指定ID,将此时L作为尾结点进行处理 ;当p不是NULL,即找到指定ID,将此时p作为尾结点进行处理

8、释放通讯录(问题的解决)

需要释放内存后,接着把指针指向NULL;释放头结点,首元结点和遍历单链表所指向的结点 先定义两个结点指针 p,t;分别用来遍历单链表,暂时指向所遍历之处的结点


六、自我评价与总结

一起学习进步,还不点赞收藏?在评论区一起进步,改正呀!

项目总结与自我评价https://blog.csdn.net/qq_63943626/article/details/126237207

你可能感兴趣的:(算法与数据结构,c++,c语言,经验分享)