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

1.功能介绍

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

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

在这里插入图片描述
在建立通讯录和插入联系人信息时,会对ID进行判断,重复则提示重新输入,在插入联系人时若输入的插入位置错误,会提示是否将新联系人插入到通讯录最后面。

2.具体实现

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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.运行结果

这里测试了部分功能,但是每个功能都是亲测可以用的,图片太长我又太懒就只显示部分截图了。
在这里插入图片描述

4.总结

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

  • 45
    点赞
  • 325
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
### 回答1: 好的,下面是一个简单的单链表通讯录数据结构: ``` struct Contact { char name[50]; char phone[20]; char email[50]; struct Contact* next; }; typedef struct Contact* ContactPtr; ``` 上面的结构体定义了一个通讯录中联系人的信息,包括姓名、电话和电子邮件地址。每个联系人都有一个指向下一个联系人的指针。使用这个结构体,我们可以定义一个单链表通讯录。 下面是一个简单的实现示例,包括添加联系人、查找联系人和删除联系人等基本操作: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> struct Contact { char name[50]; char phone[20]; char email[50]; struct Contact* next; }; typedef struct Contact* ContactPtr; // 初始化通讯录 ContactPtr initContact() { ContactPtr head = (ContactPtr)malloc(sizeof(struct Contact)); head->next = NULL; return head; } // 添加联系人 void addContact(ContactPtr head, char name[], char phone[], char email[]) { ContactPtr newContact = (ContactPtr)malloc(sizeof(struct Contact)); strcpy(newContact->name, name); strcpy(newContact->phone, phone); strcpy(newContact->email, email); newContact->next = NULL; ContactPtr current = head; while (current->next != NULL) { current = current->next; } current->next = newContact; printf("联系人 %s 添加成功!\n", name); } // 查找联系人 void findContact(ContactPtr head, char name[]) { ContactPtr current = head->next; while (current != NULL) { if (strcmp(current->name, name) == 0) { printf("%s 的电话号码是:%s,电子邮件地址是:%s\n", current->name, current->phone, current->email); return; } current = current->next; } printf("未找到联系人 %s!\n", name); } // 删除联系人 void deleteContact(ContactPtr head, char name[]) { ContactPtr current = head; while (current->next != NULL) { if (strcmp(current->next->name, name) == 0) { ContactPtr temp = current->next; current->next = temp->next; free(temp); printf("联系人 %s 删除成功!\n", name); return; } current = current->next; } printf("未找到联系人 %s!\n", name); } // 打印所有联系人 void printContacts(ContactPtr head) { if (head->next == NULL) { printf("通讯录为空!\n"); return; } ContactPtr current = head->next; while (current != NULL) { printf("%s 的电话号码是:%s,电子邮件地址是:%s\n", current->name, current->phone, current->email); current = current->next; } } int main() { ContactPtr head = initContact(); addContact(head, "张三", "123456789", "zhangsan@example.com"); addContact(head, "李四", "987654321", "lisi@example.com"); addContact(head, "王五", "234567890", "wangwu@example.com"); printContacts(head); findContact(head, "张三"); findContact(head, "赵六"); deleteContact(head, "李四"); printContacts(head); return 0; } ``` 上面的代码实现了一个简单的单链表通讯录,包括添加联系人、查找联系人和删除联系人等基本操作。 ### 回答2: 单链表通讯录是一种基于数据结构通讯录实现方式。通讯录中的每个联系人都被封装为一个节点,节点内包含联系人的信息和一个指向下一个节点的指针。该链表的头节点指向第一个联系人,最后一个联系人的节点指针为空。 单链表通讯录的优点是插入和删除联系人的操作高效。当需要插入一个新的联系人时,只需要修改指针的指向即可,无需移动其他节点。同样,删除联系人时也只需修改指针指向,不需要进行大量的数据迁移。这样可以大大提高操作的效率。 然而,单链表通讯录的缺点是查找联系人的效率相对较低。由于只能按顺序查找,当查找的联系人位于链表的末尾时,需要遍历整个链表寻找,时间复杂度较高。为了解决这个问题,可以使用更高效的数据结构,如哈希表来实现通讯录。 除了基本的插入、删除和查找操作,单链表通讯录还可以进行其他一些常见的操作,如修改联系人信息、排序联系人。修改联系人信息时,可以根据给定的姓名或其他关键字,遍历链表找到指定联系人并修改其信息。排序联系人时,可以使用冒泡排序等算法进行排序,将链表中的联系人按照一定的规则排序。 总的来说,单链表通讯录是一种简单而高效的通讯录实现方式。它具有插入、删除操作高效的优点,但查找效率相对较低。可以根据实际需求选择合适的数据结构实现通讯录。 ### 回答3: 单链表通讯录是一种常见的数据结构,用于存储和管理联系人信息。通讯录中的每个联系人被表示为一个节点,节点包含姓名、电话号码等信息,并且有一个指向下一个节点的指针。 单链表通讯录的优点是插入、删除和查找操作都很高效。在插入操作时,只需将新的联系人节点插入到链表中,并更新相应的指针,即可完成插入操作。在删除操作时,只需修改相应节点的指针,即可将该联系人节点从链表中删除。在查找操作时,只需从头节点开始,依次遍历每个节点,直到找到目标联系人节点为止。 另一个优点是单链表通讯录能够动态地存储联系人信息。链表的长度可以根据需要进行动态调整,不需要预先分配固定的内存空间。这使得单链表通讯录更加灵活和节省内存空间。 然而,单链表通讯录也有一些缺点。首先,查询某个特定联系人节点的效率较低,需要遍历整个链表,尤其在链表较长时,效率较低。其次,在内存分配上可能存在空间浪费的问题,因为每个节点的指针需要占用额外的内存空间。 综上所述,单链表通讯录是一种高效、灵活的数据结构,适合用于存储和管理通讯录信息。但在应用中需要根据具体需求权衡其优缺点,选择合适的数据结构实现通讯录功能。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值