文章简介:基本数据结构系列讲解之单链表(附源码)
1.介绍链表结构
链表中的每个结点都应包括以下两个部分。
(1)数据部分:保存结点的数据
(2)地址部分:保存下一结点地址
链表的头指针指向链表结构的第一个结点,依次直到最后一个结点,最后一个结点的地址部分一般放一个空指针NULL。
在链表结构中,通过指针实现结点的逻辑相邻,而逻辑相邻的结点在内存中不一定相邻,所以使用链表时不需要事先分配一块存储空间,程序员通过malloc函数动态分配结点的存储空间,当删除某结点时,再用free函数释放空间。
链表结构的缺点是浪费存储空间,因为每一个结点都需要额外存储一个指针变量。
链表有单链表、双向链表、循环链表等几类,本文主要讲解单链表的相关操作。
2.定义链表及数据元素类型
本程序的功能是通过单链表存储学生信息,学生信息包括:学号key,姓名name,年龄age。学生信息作为数据元素类型Data,链表数据结构CLType,在CLType内用Data类型变量表示数据部分,用链表结构的指针指向下一结点。
typedef struct
{
char key[10];
char name[15];
int age;
}Data;
typedef struct Node
{
Data nodeData;
struct Node *nextNode;
}CLType;
3.添加结点
添加结点即在链表的尾部加入结点,操作步骤如下:
(1)分配内存空间,用以保存将要添加的结点;
(2)从头指针head开始检查,找到最后一个结点;
(3)将表尾结点的地址设置为新增结点地址;
(4)将新增结点的地址设置为NULL。
程序使用malloc为新增结点申请内存空间。
CLType *CLAddEnd(CLType *head,Data nodeData)
{
CLType *node,*temp;
if(!(node=(CLType*)malloc(sizeof(CLType))))
{
printf("内存申请失败!\n");
return NULL;
}
else
{
node->nodeData=nodeData;
node->nextNode=NULL;
if(head=NULL)
{
head=node;
return head;
}
temp=head;
while(temp!=NULL)
{
temp=temp->nextNode;
}
temp->nextNode=node;
return head;
}
}
4.插入头结点
在链表首部插入结点,步骤如下:
(1)分配内存空间,用以保存新增结点;
(2)将新增结点指向head所指的结点;
(3)使head指向新增结点。
CLType *CLAddFist(CLType *head,Data nodeData)
{
CLType *node;
if(!(node=(CLType *)malloc(sizeof(CLType))))
{
printf("内存分配失败!\n");
return NULL;
}
else
{
node->nodeData=nodeData;
node->nextNode=head;
head=node;
return head;
}
}
5.查找结点
通过关键字key来查找结点,使用strcmp函数比较查找,找到后返回该结点指针。
CLType *CLFindNode(CLType *head,char *key)
{
CLType *temp;
temp=head;
while(temp)
{
if(strcmp(temp->nodeData.key,key)==0)
{
return temp;
}
temp=temp->nextNode;
}
return NULL;
}
6.插入结点
链表中插入结点步骤如下:
(1)分配内存空间,用以保存新的结点;
(2)找到要插入的位置;
(3)将插入的结点指向插入位置指向的结点,插入位置指向新增结点。
本程序使用关键字找到要插入的位置。
CLType *CLInsertNode(CLType *head,char *key,Data nodeData)
{
CLType *node,*temp;
if(!(node=(CLType *)malloc(sizeof(CLType))))
{
printf("内存申请失败!\n");
return 0;
}
node->nodeData=nodeData;
temp=CLFindNode(head,key);
if(temp)
{
node->nextNode=temp->nextNode;
temp->nextNode=node;
}
else
{
printf("插入的位置不对!\n");
free(node);
}
return head;
}
7.删除结点
链表中删除结点的操作如下:
(1)找到要删除的目标结点;
(2)使该结点的前一个结点指向后一个结点;
(3)删除该结点。
int CLDeleteNode(CLType *head,char *key)
{
CLType *node,*temp;
temp=head;
node=head;
while(temp)
{
if(strcmp(temp->nodeData.key,key)==0)
{
node->nextNode=temp->nextNode;
free(temp);
return 1;
}
else
{
node=temp;
temp=temp->nextNode;
}
}
return 0;
}
8.计算链表长度
由于链表不是在内存中不是连续存储的,所以需要遍历整个链表才能计算出链表长度。
int CLLength(CLType *head)
{
int len=0;
CLType *temp;
temp=head;
while(temp)
{
len++;
temp=temp->nextNode;
}
return len;
}
9.显示所有结点
void CLAllNode(CLType *head)
{
CLType *temp;
Data nodeData;
temp=head;
printf("当前共有%d个结点。链表所有数据如下:\n",CLLength(head));
while(temp)
{
nodeData=temp->nodeData;
printf("结点(%s,%s,%d)\n",nodeData.key,nodeData.name,nodeData.age);
temp=temp->nextNode;
}
}
本文源代码:单链表操作.cpp