数据结构单链表实现通讯录

1.功能介绍

基于上一篇对单链表的基本操作的了解,为了巩固相关知识点,于是用单链表写了一个通讯录作为巩固和加深对相关知识点的理解。基本的原理都是对单链表基本操作的应用。主要实现了一下几个简单的功能。

  1. 初始化通讯录
  2. 建立通讯录(头插法和尾插法)
  3. 删除联系人
  4. 修改联系人
  5. 查找联系人
  6. 插入联系人信息(前插法和后插法)
  7. 遍历通讯录
  8. 清空通讯录

数据结构单链表实现通讯录_第1张图片
在建立通讯录和插入联系人信息时,会对ID进行判断,重复则提示重新输入,在插入联系人时若输入的插入位置错误,会提示是否将新联系人插入到通讯录最后面。

2.具体实现

功能就不一一介绍了,直接上代码,都有注释。

#include 
#include 
#include 
#pragma warning(disable:4996)//用于屏蔽序号4996的错误
typedef struct phone
{
	char ID[10]; //编号
	char name[20]; //姓名
	char tel[15]; //电话
	char group[20]; //分组
}Ipa;//给存放通讯录属性的结构体取个别名

typedef struct List
{
	Ipa data;
	struct List *next;

}List;

List *L = NULL;
//1.初始化通讯录,建立头节点
void InitList()
{
	L = (List *)malloc(sizeof(List));
	L->next = NULL;
	printf("初始化成功!\n");
}

//2.插入数据,建立通讯录(头插法)
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("分组:");
		scanf("%s", &(node->data.group));
		node->next = L->next;
		L->next = node;
		printf("是否继续录入(1.继续 0.完成录入):");
		scanf("%d", &flag);
		if (flag == 0)
			break;
	}
}

//3.插入数据,建立通讯录(尾插法)
void CreateList_Tail()
{
	List *node, *tail; //插入节点和尾节点
	tail = L;
	int count = 0;
	int flag = 1;
	while (flag)
	{
		count++;
		node = (List *)malloc(sizeof(List)); //给节点分配空间
		printf("录入第%d条数据:", 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("分组:");
		scanf("%s", node->data.group);
		tail->next = node;
		node->next = NULL;
		tail =  node;
		printf("是否继续录入(1.继续 0:结束):");
		scanf("%d", &flag);
		if (flag == 0)
			break;
	}
}

//4.删除数据(根据姓名删除)
void DelList()
{
	List *p = L->next;
	List *q = L;
	char ID[10];
	printf("请输入要删除的ID:");
	scanf("%s", &ID);
	while (p && strcmp(p->data.ID, ID) != 0)
	{
		p = p->next;
		q = q->next;
	}
	if (p)
	{
		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("分组:%s\n", p->data.group);
		free(p);
	}
	else
	{
		printf("通讯录中不存在此人信息!\n");
	}
}

//5.插入数据(前插法)
void InsertList_Pre()
{
	List *p, *q, *r, *node;
	p = L->next;
	q = L;
	char ID[10];
	printf("请输入要插入的位置(输入该位置的ID):");
	scanf("%s", &ID);
	while (p && strcmp(p->data.ID, ID) != 0)
	{
		p = p->next;
		q = q->next;
	}
	if (p != NULL)
	{

		//给新节点分配空间
		node = (List*)malloc(sizeof(List));
		//录入新节点数据
		printf("请输入新节点信息:\n");
	   judge:	
			printf("ID:");
			scanf("%s", &(node->data.ID));
			//判断ID是否重复
		    r = L->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("分组:");
	  scanf("%s", &(node->data.group));
	}
	else
	{
		//如果没有该ID
		int choice1;
		printf("通讯录中未找到该ID,是否使用默认方式将新节点插入到最后(1.是 0.否):");
		scanf("%d", &choice1);
		if (choice1 == 1)
		{
			//给新节点分配空间
			node = (List *)malloc(sizeof(List));
			//给新节点输入数据
			printf("请输入新节点信息:\n");
		judge1:
			printf("ID:");
			scanf("%s", &(node->data.ID));
			//判断ID是否重复
			r = L->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("分组:");
			scanf("%s", &(node->data.group));
		}
		else
			return;
	}
	node->next = q->next;
	q->next = node;
	printf("插入成功!\n");
}

//6.插入数据(后插法)
void InsertList_Back()
{
	List *p, *r, *q, *node;
	p = L->next;
	q = L;
	char ID[10];
	printf("请输入要插入的位置(输入该位置的ID):");
	scanf("%s", &ID);
	while (p && strcmp(p->data.ID, ID) != 0)
	{
		p = p->next;
		q = q->next;
	}
	if (p != NULL)
	{
		//为新节点分配空间
		node = (List*)malloc(sizeof(List));
		//为新节点输入数据
		printf("请输入新节点信息:\n");
	judge:
		printf("ID:");
		scanf("%s", &(node->data.ID));
		//判断ID是否重复
		r = L->next;
		while (r && strcmp(r->data.ID, node->data.ID) != 0)
		{
			r = r->next;
		}
		if (r != NULL)
		{
			printf("ID重复,请重新输入!\n");
			goto judge;
		}
		printf("姓名:");
		scanf("%s", &(node->data.name));
		printf("电话:");
		scanf("%s", &(node->data.tel));
		printf("分组:");
		scanf("%s", &(node->data.group));
		node->next = p->next;
		p->next = node;
		printf("插入成功!\n");
	}
	else
	{
		//通讯录中未找到该ID
		int choice2;
		printf("通讯录中未找到该ID,是否使用默认方式将该节点插入到最后(1.是 0.否):");
		scanf("%d", &choice2);
		if (choice2 == 1)
		{
			//为新节点分配空间
			node = (List *)malloc(sizeof(List));
			//为新节点录入信息
			printf("请输入新节点信息:\n");
		judge1:
			printf("ID:");
			scanf("%s", &(node->data.ID));
			//判断ID是否重复
			r = L->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("分组:");
			scanf("%s", &(node->data.group));
			node->next = q->next;
			q->next = node;
		}
		if (choice2 == 0)
			return;
	}
	
}

//7.修改通讯录数据
void UpdateList()
{
	List *p;
	p = L->next;
	char ID[10];
	printf("请输入要修改的ID:");
	scanf("%s", &ID);
	while (p && strcmp(p->data.ID, ID) != 0)
	{
		p = p->next;
	}
	if (p != NULL)
	{
		int choice;
		printf("请输入需要修改的属性(1.姓名 2.电话 3.分组):");
		scanf("%d", &choice);
		if (choice == 1)
		{
			printf("请输入新的姓名:");
			scanf("%s", &(p->data.name));
		}
		if (choice == 2)
		{
			printf("请输入新的电话:");
			scanf("%s", &(p->data.tel));
		}
		if (choice == 3)
		{
			printf("请输入新的分组:");
			scanf("%s", &(p->data.group));
		}
		printf("更新成功!\n");
	}
	else
	{
		printf("通讯录中不存在该ID!\n");
		return;
	}
}

//8.查找通讯录中的数据(通过ID查找)
void SearchList()
{
	List *p;
	char ID[10];
	p = L->next;
	printf("请输入要查找的ID:");
	scanf("%s", &ID);
	while (p && strcmp(p->data.ID, ID) != 0)
	{
		p = p->next;
	}
	if (p == NULL)
	{
		printf("通讯录中不存在此人!\n");
		return;
	}
	else
	{
		printf("ID:%s\t", p->data.ID);
		printf("姓名:%s\t", p->data.name);
		printf("电话:%s\t", p->data.tel);
		printf("分组:%s\n", p->data.group);
	}
	
}

//9.遍历通讯录
void TraverseList()
{
	List *p;
	if (L == NULL)
	{
		printf("通讯录为空!\n");
		return;
	}
	else
	{
		p = L->next;
		printf("通讯录中全部信息如下:\n");
		while (p)
		{
			printf("ID:%s\t", p->data.ID);
			printf("姓名:%s\t", p->data.name);
			printf("电话:%s\t", p->data.tel);
			printf("分组:%s\n", p->data.group);
			p = p->next;
		}
	}
}

//10.释放链表
void DestroyList()
{
	List *p, *q;
	p = L->next;
	while (p)
	{
		q = p;//用q暂时存放该节点
		p = p->next; //p指向下一个节点
		free(q);// 释放当前节点
		q = NULL;
	}
	free(p);
	free(L);
	L = NULL;
	printf("释放成功!\n");
}

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");
}
void main()
{
	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:
			break;
		}
	}
}

3.运行结果

这里测试了部分功能,但是每个功能都是亲测可以用的,图片太长我又太懒就只显示部分截图了。
数据结构单链表实现通讯录_第2张图片

4.总结

在删除节点的一个坑,在获取到要删除节点的前一个结点和要删除的节点时,一定要先让前一个结点指向要删除节点的下一个节点,之后再free要删除的节点,如果先操作要删除的节点,则会丢失下一个节点的位置。插入的时候也是同理。

你可能感兴趣的:(c语言,c++,其他,数据结构)