①基本定义
线性表是具有相同特征的数据结构元素的一个有限序列。
链表即线性表的链式存储结构,与顺序表相比,链表可以实现存储空间的动态管理,而顺序表必须事先分配一整块的存储空间,这样会降低存储空间的利用率。
②存储方式与结构
线性表(a1,a2,...,an-1,an)
↓映射
链表
链表中每个结点都含有一个指针,用于指向后继结点。通常每个链表都含有一个头结点,并通过头结点的指针指向该链表,注意:头结点不存储数据,只含一个指向链表的指针,尾结点中的指针不需要指向其他任何结点,则置为空(NULL),值得一提的是,若尾结点的指针又指向了头结点,那么这个单链表就成了循环链表。
③实现
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LinkNode;
/*****建立单链表(头插法)*****/
void CreateListF(LinkNode * &L,ElemType a[],int n)
{
LinkNode *s;
L=(LinkNode *)malloc(sizeof(LinkNode));
L->next=NULL; //创建头结点,其next域置于NULL
for(int i=0;i<n;i++) //循环建立数据节点s
{
s=(LinkNode *)malloc(sizeof(LinkNode));
s->data=a[i]; //创建数据节点s
s->next=L->next; //将结点s插入到原首结点之前,头结点之后
L->next=s;
}
}
/*****建立单链表(尾插法)*****/
void CreateListR(LinkNode * &L,ElemType a[],int n)
{
LinkNode *s,*r;
L=(LinkNode *)malloc(sizeof(LinkNode)); //创建头结点
r=L; //r始终指向尾结点,初始时指向头结点
for(int i;i<n;i++) //循环建立数据结点
{
s=(LinkNode *)malloc(sizeof(LinkNode));
s->data=a[i]; //创建数据结点s
r->next=s; //将结点s插入到结点r之后
r=s;
}
r->next =NULL; //尾结点的next域置为NULL
}
/*****初始化线性表*****/
void InitList(LinkNode * &L)
{
L=(LinkNode *)malloc(sizeof(LinkNode));
L->next=NULL; //创建头结点,其next域指向NULL
}
/*****销毁线性表*****/
void DestroyList(LinkNode * &L)
{
LinkNode *pre=L,*p=L->next; //pre指向结点p的前驱结点
while(p!=NULL) //扫描单链表L
{
free(pre); //释放pre结点
pre=p; //pre,p同步移后一个结点
p=pre->next;
}
free(pre); //循环结束时p为NULL,pre指向尾结点,释放它
}
/*****判断线性表是否为空表*****/
bool ListEmpty(LinkNode *L)
{
return (L->next==NULL);
}
/*****求线性表的长度*****/
int ListLength(LinkNode *L)
{
int n=0;
LinkNode *p=L; //p指向头结点,n置为0,(即头结点的序号为0)
while(p->next !=NULL)
{
n++;
p->next;
}
return n; //循环结束,p指向尾结点,其序号n为结点个数
}
/*****输出线性表*****/
void DispList(LinkNode *L)
{
LinkNode *p=L->next; //p指向首结点
while(p!=NULL) //p不为NULL,输出p结点的data域
{
printf("%d",p->data);
p=p->next; //p移向下一个结点
}
printf("\n");
}
/*****求线性表中的某个数据元素值*****/
bool GetElem(LinkNode *L,int i,ElemType &e)
{
int j=0;
LinkNode *p=L; //p指向头结点,j置为0(头结点序号为0)
if(i<=0)
return false; //i错误返回假
while(j<i && p!=NULL) //找到第i个结点
{
j++;
p->next;
}
if(p==NULL) //不存在第i个元素结点,返回false
return false;
else
{
e=p->data;
return true;
}
}
/*****按元素值查找*****/
int LocateElem(LinkNode *L,ElemType e)
{
int i=1;
LinkNode *p=L->next; //p指向首结点,i置为1(即首结点的序号为1)
while(p!=NULL && p->data!=e) //查找data为e的结点,其序号为i
{
p=p->next;
i++;
}
if(p==NULL) //不存在结点为e的结点,返回其逻辑序号i
return 0;
else
return (i);
}
/*****插入数据元素*****/
bool ListInsert(LinkNode * &L,int i,ElemType e)
{
int j=0;
LinkNode *p=L,*s; //p指向头结点,j置为0,(即头结点序号为0)
if(i<=0)
return false; //错误返回false
while(j<i-1 && p!=NULL) //查找第i-1个结点p
{
j++;
p=p->next;
}
if(p==NULL) //未找到第i-1个结点,返回false
return false;
else //找到第i-1个结点p,插入新结点并返回true
{
s=(LinkNode *)malloc(sizeof(LinkNode));
s->data=e; //创建新结点s,其data域置为e
s->next=p->next; //将结点s插入到结点p之后
p->next=s;
return true;
}
}
/*****删除数据元素*****/
bool ListDelete(LinkNode * &L,int i,ElemType &e)
{
int j=0;
LinkNode *p=L,*q; //p指向头指针,j置为0(即头结点的序号为0)
if(i<=0)
return false; //i错误返回false
while(j<i-1 && p!=NULL) //查找第i-1个结点
{
j++;
p=p->next;
}
if(p==NULL) //未找到第i-1个结点,返回false
return false;
else //找到第i-1个结点
{
q=p->next; //q指向第i个结点
if(q==NULL) //若不存在第i个结点,返回false
return false;
e=q->data;
p->next=q->next; //从单链表中删除q结点
free(q); //释放q结点
return true; //返回true表示成功删除第i个结点
}
}
int main()
{
return 0;
}
注:建立单链表可采用头插法或尾插法,头插法将每一次插入的数据排在第一位,也就是说,使用头插法创建的链表和原先线性表中的数据的顺序是相反的,所以说,若非特殊情况需要倒置线性表,我们一般采用尾插法创建链表。
---代码和部分内容参考自《数据结构教程》