文章简介:基本数据结构系列讲解之单链表(附源码)
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