单链表的概念
定义:采用链式存储结构的线性表称为链表。
从实现角度可以分为:
静态链表、动态链表;
从链接方式可以分为:
单链表、双向链表、循环链表;
单链表:链表中的每一个结点只有一个指针域。
单链表包括两个域:
- 数据域:用来存储结点的数据值;
- 指针域:用来存储数据元素的直接后继的地址。
结点的定义:
typedef struct Node{
ElemType data;
struct Node *next;
}LNode,*LinkList;
基本操作
单链表建立
有时为了操作方便,在单链表的第一个结点之前附加一个结点,称为头结点。头结点的数据域可以存储标题、表长等信息,也可以不存储任何信息,其指针域存储第一个结点的首地址。
头指针变量定义:
LinkList H;p=(LinkList) malloc(sizeof(LNode));
表示申请一块LNode类型的存储单元的操作,并将其地址赋值给变量p;
free( p) 表示释放指针p所指向的结点空间。
1. 头插法建表
在链表的头部插入结点建立单链表,首先申请一个头结点,并且将头结点指针域置空(NULL);然后每读入一个数据元素则申请一个结点,并插在链表的头结点之后。
算法:时间复杂度为O(n)
LinkList CreateFromHead(){
LinkList L;
LNode *s;
int x;
int flag=1;
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
scanf("%d",&x);
while(x!=-1){
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
return L;
}
2. 尾插法建表
在单链表的尾部插入结点建立单链表,增加一个指针r来始终指向链表中的尾结点,以便能够将新结点插入到链表的尾部。首先初始化一个空表,使指针r指向头结点,然后插入一个新结点到r的后一个结点,同时r指向新结点。
算法时间复杂度为O(n)
LinkList CreateFromTail(){
LinkList L;
LNode *r,*s;
int x;
L=(LNode*)malloc(sizeof(LNode));
L->next=NULL;
r=H;
scanf("%d",&x);
while(x!=-1){
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
s->next=r->next;
r->next=s;
r=s;//让r指向尾结点
scanf("%d",&x);
}
r->next=NULL;
return L;
}
存储空间的分配和释放
如果在一个单链表里面插入一个结点,那么该新结点的存储空间从何处来呢?还有当把这个结点删掉以后,这个存储空间又把它归还给谁呢?
用下面几个函数就可以实现:
malloc 函数
void *malloc(unsigned int size);
calloc 函数
void *calloc(unsigned n,unsigned size);
free函数
void free(void *p)。
单链表插入
要在带头结点的单链表L中第i个数据元素之前插入一个数据元素e,需要首先在单链表中找到第i-1个结点并由指针pre指示,然后申请一个新的结点并由指针s指示,其数据域的值为e,并修改第i-1个结点的指针使其指向s,然后使s结点的指针域指向第i个结点。
首先要找到要进行操作的前驱结点,然后改链的操作比较重要,先将待插入的结点s的next指向原先结点的下一个位置,再将原先结点的next指向待插入的s.
算法:
void InsList(LinkList L,int i,ElemType e){
LNode *pre,*s;
int k=0;
pre=L;
while(pre!=NULL&&k<i-1){
pre=pre->next;
k=k+1;
}
if(k!=i-1){
printf("插入位置不合理!");
return ;
}
s=(Node*)malloc(sizeof(Node));
s->data=e;
s->next=pre->next;
pre->next=s;
}
单链表删除
欲在带头结点的单链表L中删除第i个结点,则首先要通过计数方式找到第i-1个结点并使pre指向第i-1个结点,而后删除第i个结点并释放结点空间。
删除之后要free释放掉空间,不然以后这个空间就不能被其他结点所分配。
删除算法:
void DelList(LinkList L,int i,ElemType e){
LNode *pre,*r;
int k=0;
pre=L;
while(pre!=NULL&&k<i-1){
pre=pre->next;
k=k+1;
}
if(k!=i-1){
printf("删除结点的位置i不合理!");
return ERROR;
}
r=pre->next;
pre->next=pre->next->next;
free(r);
return OK;
}