链表的相关操作及简单通讯录系统的实现

链表基础:

对于需要经常插入数据的线性表,一般采用连式存储的链表,链表是采用动态存储分配的一种结构,可以根据需要申请内存单元。


链表的每一个结点都应该包括两个部分,一部分是实际数据,一部分是下一个结点的地址,操作链表时,需要定义一个“头指针“变量(head),该指针指向第一个结点,第一个结点又指向第二个结点。。。一直到最后一个结点,最后一个结点不再指向其他结点,称为”表尾“,在表尾地址放入”NULL“,表示空地址,链表至此结束。

如果链表每个结点只包含一个指针,称为单链表,如果包含两个指针,一个指向下一个结点,一个指向上一个结点,称为双向链表。

在链表中,逻辑上相邻的结点在内存中不一定相邻,逻辑相邻关系通过指针实现,因此对于链表的访问只能从表头逐个查找,一直找到需要的结点为止,而不是像顺序表那样随机访问。

可以用malloc函数动态分配结点的存储空间,删除某个结点时,再用free函数释放结点所占用的内存空间。


链表的基本操作:

链表的基本操作有:添加结点(加到表头,加到表尾),查找,插入,删除,获取链表结点数量等。

首先定义操作链表的头文件 ”ChainList.h“

#include<stdio.h>
#include<stdlib.h>
typedef struct Node     //定义结构体作为链表的数据类型
{
	DATA data;       //保存呢结点的具体数据
	struct Node *next;        //指针next用来指向下一个结点
} ChainListType;

ChainListType *ChainListAddEnd(ChainListType *head,DATA data);

ChainListType *ChainListAddFirst(ChainListType *head,DATA data);

ChainListType *ChainListFind(ChainListType *head,char *key);

ChainListType *ChainListInsert(ChainListType *head,char *findkey,DATA data);

int  ChainListDelete(ChainListType *head, char *key);

int ChainListLength(ChainListType *head);

因为需要动态分配内存的函数malloc,需要头文件stdlib.h。。如果需要做双向链表,需要在结构体中再增加一个指针,指向上一个结点。

链表操作函数保存在文件"ChainList.c"中:

1.添加结点到尾部:

若要在尾部添加结点,需要从头(head)开始逐个往后,直到找到最后一个结点,即表尾,然后将表尾结点的地址部分由NULL更改为新增的结点地址(原来表尾的结点指向新增的结点),然后将新增结点地址部分设置为NULL。

#include<string.h> 

//添加结点到尾部 
ChainListType *ChainListAddEnd(ChainListType *head,DATA data)  //添加节点到链表尾部
{
	ChainListType *node,*h;      //node为新增结点
	if(!(node=(ChainListType *)malloc(sizeof(ChainListType))))   //分配内存失败 
	{
		printf("为保存节点数据申请内存失败\n");
		return NULL; 
	}
	node->data=data;    //保存数据 
	node->next=NULL;  //设置结点指针为空 ,即表尾
	if(head==NULL)         //判断头指针是否为空,若为空则指向当前节点 
	{
		head=node;  //链表的头指针指向当前结点 
		return head;   //返回头指针 
	} 
	//若头指针不为 空 
	h=head;       //保存头指针
	while(h->next!=NULL)         //循环,使指针一直指到最后,即NULL
	      h=h->next;    //往后一直查找 
	h->next=node;  //原来表尾结点地址指向新增结点地址 
	return head;
} 

对于添加结点到尾部时,如果只知道头指针head,则要从头部一直查找到链表的尾部,如果经常需要这样做,可以定义一个尾指针,指向链表尾部,这样在多次给尾部添加结点时,可省去查找过程。


2.添加结点到首部

在首部添加结点,使新增结点指向头指针所指的结点,head指向新增结点即可。

//添加结点到首部
ChainListType *ChainListAddFirst(ChainListType *head,DATA data) //head为链表头指针,data为结点保存数据 
{
	ChainListType *node,*h;
	 if(!(node=(ChainListType *)malloc(sizeof(ChainListType))))   //分配内存失败 
	{
		printf("为保存节点数据申请内存失败\n");
		return NULL; 
	}
	node->data=data;
	node->next=head;      //指向头指针 所指向的结点 
	head=node;     //头指针指向新增结点
	return head; 
}

3.查找结点

对于链表中保存的数据,可通过关键字查找

//查找结点
ChainListType *ChainListFind(ChainListType *head,char *key)
{
ChainListType *h;
h=head;        //保存链表头指针
while(h)   //结点有效,进行查找  h的值不为NULL,即未到表尾 
{
if(strcmp(h->data.key,key)==0)  //结点关键字与传入关键字相同 
    return h;     //找到目标节点,并返回该结点的指针 
h=h->next;        //没有找到,处理下一个结点
}
return NULL;  

从头指针开始,如果指针h的值不为NULL,就对结点关键字和查找关键字进行比较,是否相等,一直到表尾,若还没周到返回空指针。


4.插入结点

插入结点即在链表中间部分新增结点,首先找到插入的位置A(逻辑位置),使插入位置A原来指向位置B的结点指向新增结点,而新增结点指向位置B。

//插入结点
ChainListType *ChainListInsert(ChainListType *head,char *findkey,DATA data) 
{
	ChainListType *node,*node1;
	if(!(node=(ChainListType *)malloc(sizeof(ChainListType))))   //分配内存失败 
	{
		printf("为保存节点数据申请内存失败\n");
		return 0; 
	}
	node->data=data;      
	node1=ChainListFind(head,findkey);      //findkey为在链表中查找的结点(A)关键字,找到A后在该结点后面添加新增结点
	if(node1)    //找到要插入的节点 
	{
		node->next=node1->next;   //新插入结点指向关键节点A的下一个结点B
		node1->next=node;   //关键节点A指向新插入的结点 
	} else{
		free(node);    //释放内存
		printf("没有找到插入的位置"); 
		
	}
	return head;   
}

6.删除结点

首先查找到需要删除的结点(delete),然后还需要定义一个指向需要删除结点的前一个结点的指针(A),修改A,使A原来指向需要删除结点(delete)修改为指向删除结点后的那个结点(B),这样完成了逻辑上的删除,然后再释放掉需要删除结点(delete)的内存,这样才删除成功。

删除前
AdeleteB
删除后
AB
  

注:若是查找到结点后,直接释放内存,而没有完成逻辑上的删除,则A结点以后无法完成后面链接,B结点也无法被前面的指针链接到,这样的删除是不行的。

//删除节点
int ChainListDelete(ChainListType *head,char *key)  //key为需要删除的关键字 
{
	ChainListType *node,*h;      //node保存删除节点的前一个结点
	node=h=head;     //从头部开始
	while(h)
	{
		if(strcmp(h->data.key,key)==0)
		{
			node->next=h->next;     //使前一个结点指向当前结点的下一个结点,逻辑删除
			free(h);       //释放内存
			return 1;
		}else{
			node=h;   //指向当前节点
			h=h->next;         //指向下一个结点 
			
		}
	}
	return 0; //未删除 

} 

7.链表长度

//链表的长度
int ChainListLength(ChainListType *head)
{
	ChainListType *h;
	int i=0;
	h=head;
	while(h)        //遍历链表 
	{
		i++;    //累加结点数量 
		h=h->next;      //处理下一个节点 
	}
	return i;  
} 

以上是链表的基本操作,下面用链表制作一个简单的通讯录

最简单的通信录包括:添加联系人,查找联系人,删除联系人,显示联系人信息。下面用上面介绍的链表来实现这些功能。

通信录的头文件:

typedef struct
{
	char key[15];    //设置名字关键字 
	char addr[20];  
	char tele[15];
}DATA;   //数据结点类型 
#include <stdio.h>
#include <stdlib.h>
#include <conio.h> 
#include  "ChainList.h"
#include "chainlist.cpp"

结构体中定义 姓名(并设置姓名为关键字),联系人地址和电话号。引用上面操作过的函数文件

1.显示联系人模块:

//显示联系人
void  ChainListAll(ChainListType *head)  //遍历链表
{
ChainListType *h;
DATA data;
    h=head;
    printf("数据如下:\n");
    printf("\n");
    while(h)          //循环处理链表每个结点
    {
    data=h->data;         //获取结点数据
printf("姓名:%s\n",data.key);
printf("地址:%s\n",data.addr);
printf("电话号:%s\n",data.tele);
h=h->next;        //处理下一个结点 
    }
     printf("\n");
    return;
   
}

2.添加联系人模块

//添加联系人
 ChainListType *input(ChainListType *head)   //向通讯录输入信息
 {
 	DATA data;
 	printf("输入联系人信息\n");
 	printf("姓名:");
 	scanf("%s",data.key);
 	printf("地址:");
 	scanf("%s",data.addr);
 	printf("电话:");
 	scanf("%s",data.tele);
 	printf("\n");
 	return ChainListAddEnd(head,data);    //调用添加函数
    
 } 

3.查找模块

void find(ChainListType *head)
 {
 	ChainListType *h;
 	DATA data;
 	char name[15];
 	printf("输入查找的姓名:");
 	scanf("%s",name);
 	h=ChainListFind(head,name);
 	if(h)
 	{
 		data=h->data;       //获取结点数据
		 printf("姓名:%s\n",data.key);
		 printf("地址:%s\n",data.addr);
		 printf("手机:%s\n",data.tele);
		  
 	}else 
 	printf("没有找到相应的联系人,请重新查找\n");
 	printf("\n");
 }

4.删除模块

//删除联系人
 void Delete(ChainListType *head)
 {
 	ChainListType *h=head;
 	DATA data;
 	char name[15];
 	char ch;
 	printf("输入要删除的姓名:");
 	scanf("%s",name);
 	printf("删除人信息:\n");
 	data=h->data;    //获取结点信息
 	printf("姓名:%s\n",data.key);   //显示删除信息
	printf("地址:%s\n",data.addr);
	printf("手机:%s\n",data.tele);
	printf("已经删除!");	//提示删除成功   
 }
 

5.主函数

//主模块
 int main()
 {
 	ChainListType *node,*head=NULL;
 	int select;     //选择菜单
	 do{
	 	printf("1.添加联系人\n");
	 	printf("2.查找联系人\n");
	 	printf("3.删除联系人\n");
	 	printf("4.显示联系人\n");
	 	printf("0.退出\n");
	 	select=getch();
	 	switch(select)
	 	{
	 		case '1':
	 			printf("\n添加联系人\n");
	 			head=input(head);
	 			break;
	 		case '2':
	 			printf("\n查找联系人\n");
	 			find(head);
	 			break;
	 		case '3':
	 			printf("\n删除联系人\n");
	 			Delete(head);
	 			break;
	 		case '4':
	 			printf("\n显示联系人\n");
	 			ChainListAll(head);
	 			break;
			case '0':
				return 0;
	
	 	}
	 	
	 } while(select!=0);
 } 
 






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值