①基本定义
线性表是具有相同特征的数据结构元素的一个有限序列。
链表即线性表的链式存储结构,与顺序表相比,链表可以实现存储空间的动态管理,而顺序表必须事先分配一整块的存储空间,这样会降低存储空间的利用率。
②存储方式与结构
线性表(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; }
注:建立单链表可采用头插法或尾插法,头插法将每一次插入的数据排在第一位,也就是说,使用头插法创建的链表和原先线性表中的数据的顺序是相反的,所以说,若非特殊情况需要倒置线性表,我们一般采用尾插法创建链表。
---代码和部分内容参考自《数据结构教程》