数据结构中,单向链表(又名单链表、线性链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过从头部开始,依序往下读取。
下面的代码是使用指针实现的一个单链表,称为动态单链表;当然,也可以使用数组实现一个单链表,为静态单链表,后面学习之后,再用数组实现单链表。
完整代码如下:
一、定义头文件:list.h
#ifndef LIST_H_INCLUDED
#define LIST_H_INCLUDED
struct Node;
typedef struct Node *PtrToNode;
typedef PtrToNode List;
typedef PtrToNode Position;
List MakeEmpty(List L); //生成空链表L
void DestroyList(List L); //销毁链表L
_Bool IsEmpty(List L); //判定链表是否为空
_Bool IsLast(Position P, List L); //判定位置P的节点是否为尾结点
int Length(List L); //查看链表长度
Position Find(int X, List L); //在链表L中查找数据项为X的第一个结点
void Delete(int X, List L); //在链表L中删除数据项为X的第一个结点
Position FindPrevious(int X, List L); //在链表L中查找数据项为X的第一个结点的前驱位置
Position FindNext(int X,List L); //在链表L中查找数据项为X的第一个结点的后继位置
void Insert(int X, List L, Position P); //在链表L中P位置插入数据项为X的结点
void DeleteList(List L); //删除链表L头结点外的所有结点
Position Header(List L); //获得链表L中头结点位置
Position First(List L); //获得链表L中第一个数据结点的位置
Position Advance(Position P); //获得P位置的后继结点位置
int Retrieve(Position P); //获取P位置结点的数据项
struct Node
{
int data;
Position next;
};
#endif // LIST_H_INCLUDED
二、各个函数的实现: list.c
#include "list.h"
#include <malloc.h>
#include <stdlib.h>
#include<stdio.h>
/*初始化:创建一个空的单链表*/
List MakeEmpty(List L)
{
L = (PtrToNode)malloc(sizeof(PtrToNode)); //创建一个头结点
if(!L)
{
printf("申请空间失败!\n");
exit(0);
}
L->data = 0; //无效值,不赋值的话就是随机值
L->next = NULL;
return L;
}
/*销毁链表L*/
void DestroyList(List L)
{
Position P, Temp;
P = L;
while(P->next != NULL)
{
Temp = P->next;
free(P);
P = Temp;
}
}
/*测试空表*/
_Bool IsEmpty(List L)
{
return L->next == NULL; //Return ture if L is empty
}
/*测试是否为尾节点*/
_Bool IsLast(Position P, List L)
{
return P->next == NULL; //Return ture if P is the last position in List L
}
/*返回链表长度*/
int Length(List L)
{
int length = 0;
Position P;
P = L->next;
while(P)
{
length++;
P = P->next;
}
return length;
}
/*查找给定元素在表中的位置*/
Position Find(int X, List L)
{
Position P;
P = L->next;
while(P != NULL && P->data != X)
{
P = P->next;
}
return P; //Return Position of X in L; NULL if not found
}
/*删除表中某个元素X*/
void Delete(int X, List L)
{
Position P, Temp;
P = FindPrevious(X, L); //首先找到该元素的先前节点
if(!IsLast(P, L))
{
Temp = P->next; //此时Temp即是X所在结点
P->next = Temp->next;
free(Temp);
}
}
/*查找表元X的前驱*/
Position FindPrevious(int X, List L)
{
Position P;
P = L;
while(P->next != NULL && P->next->data != X)
{
P = P->next;
}
return P;
}
/*查找表元X的后继*/
Position FindNext(int X,List L)
{
Position P;
P = L;
while(P != NULL && P->data != X) //当P->next == X 时,说明此事P结点即为X所在元素的结点
{
P = P->next;
}
P = P->next; //此时P即为X所在结点位置,P重新赋值为P的下一个即后继结点
return P;
}
/*向表L中P位置之后插入元素*/
void Insert(int X, List L, Position P)
{
Position Temp;
Temp = malloc(sizeof(struct Node));
if(Temp == NULL)
{
printf("申请空间失败!\n");
exit(0);
}
Temp->data = X;
Temp->next = P->next;
P->next = Temp;
}
/*清空链表L*/
void DeleteList(List L)
{
Position P, Temp;
P = L->next; //P此时为头结点
while(P != NULL) //从头结点开始,从前往后,依次释放每一个结点,清除
{
Temp = P->next;
free(P);
P = Temp;
}
L->next = NULL; //保留头结点
}
/*获取位置P后继结点的位置*/
Position Advance(Position P)
{
if(P != NULL)
{
P = P->next;
}
return P;
}
/*获取位置为P的结点的数据项*/
int Retrieve(Position P)
{
if(P != NULL)
return P->data;
else
return 0;
}
/*获取链表L第一个结点的位置*/
Position First(List L)
{
if(L->next != NULL)
return L->next;
else
return L; //为空就返回头结点位置
}
/*获取链表L头结点位置*/
Position Header(List L)
{
return L;
}
三、main函数测试 : Main.c
#include "list.h"
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
List list = NULL, p;
int i;
list = MakeEmpty(list);
if(IsEmpty(list))
printf("空表!\n");
else
printf("非空!\n");
p = list;
for(i=0; i<5; i++)
{
Insert(i*5, list, p);
p = Advance(p);
printf("已插入值为%d的新节点\n", i*5);
}
printf("链表长度为%d\n", Length(list));
p = FindNext(5, list);
printf("数据项为5的结点的后继结点数据项为%d \n", Retrieve(p));
Delete(10, list);
p = FindNext(5, list);
printf("数据项为5的结点的后继结点数据项为%d \n", Retrieve(p));
p = First(list);
printf("第一个结点数据项为%d\n", Retrieve(p));
printf("头结点位置为%p\n", Header(list));
return 0;
}