线性表的链式存储是指通过一组任意的存储单元来存储线性表中的数据元素。为了建立起数据元素之间的线性关系,对每个链表结点,除了存放元素自身的信息外,还需要存放一个指向其后继的指针。
typedef struct LNode{ //定义单链表结点类型
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList; //结构体的名字LNode,指向这个结构体的指针是*LinkList
因为每个结点只有一个指针指向下一个结点,故这种结点组成的线性表也叫单链表。
通常用“头指针”来标识一个单链表,例如LinkList L,那么头指针L就代指一个单链表。
设置头结点的优点:
1.处理操作起来方便 例如:对在第一元素结点前插入结点和删除第一结点操作与其它结点的操作就统一了。
2.无论链表是否为空,其头指针是指向头结点的非空指针,因此空表与非空表的处理也就统一了。
单链表的操作:
1.头插法建立单链表
LinkList CreatList1(LinkList &L){
LNode *s; //辅助指针
int x;
L=(LinkList)malloc(sizeof(LNode)); //创建头结点
L->next=null; //初始为空链表
while(x!=9999){
s=(LNode *)malloc(sizeof(LNode)); //创建新结点
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
return L;
}
头插法建立单链表,读入数据的顺序与生成的链表中结点的顺序是相反的。
2.尾插法建立单链表
LinkList CreatList2(LinkList &L){
int x;
L=(LinkList)malloc(sizeof(LNode));
LNode *s,*r=L; //r为表尾指针
scanf("%d",&x);
while(!x=9999){
s=(LNode *)malloc(sizeof(LNode));
r->next=s;
r=s; //r指向新的表尾结点
scanf("%d",&x);
}
r->next=NULL; //尾结点指针置空
return L;
}
尾插法建立单链表,读入数据的顺序与生成的链表中结点的顺序是相同的。
3.按序号查找结点
LNode *GetElem(LinkList L,int i){
int j=1;
LNode *p=L->next; //头结点指针赋值给p
if(i==0)
return L; //若i=0,返回头结点
if(i<1)
return NULL; //若i无效则返回NULL
while(p&&j<i) //从第一个结点开始查找第i个结点
{
p=p->next;
j++;
}
return p; //返回第i个结点,若i大于表长,返回NULL
}
按序号查找操作的时间复杂度为O(n);
4.按值查找结点
LNode *LocateElem(LinkList L,ElemType e){
LNode *p=L->next;
while(p!=NULL && p->data!=e)
p=p->next;
return p;
}
按值查找操作的平均时间复杂度为O(n);
5.插入操作
bool LinkListInsert(LinkList &L,int i,ElemType e){
LNode *s;
if(i<1||i>L.length)
return false;
else{
s=(LNode *)malloc(sizeof(LNode));
s->data=e;
LNode *p=GetElem(L,i-1); //查找插入位置的前驱结点
s->next=p->next;
p->next=s;
return true;
}
}
插入结点操作的时间复杂度为O(n),主要开销在于查找第i-1个元素
6.删除操作
bool LinkListDelete(LinkList &L,int i){
LNode *p;
LNode *q;
if(i<1||i>L.length)
return false;
else{
p=GetElem(L,i-1);
q=p->next;
p->next=q->next;
free(q);
return true;
}
}
删除结点操作的时间复杂度为O(n),主要时间开销也是查找第i-1个元素。