顺序结构需要一块连续的存储空间,那如果我们只有零散的空间呢?
线性表的链式存储是指通过一组任意的存储单元来存储线性表中的数据元素。
为了建立起数据元素之间的线性关系,对每个链表结点,除了存放元素自身的信息之外,还需要存放一个指向其后继的指针。
typedef struct LNode{ //定义单链表结点类型
ElemType data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList;
因为每个结点只有一个指针指向下一个结点,故又称单链表
通常用“头指针”来标识一个单链表, 例如Linklist L 那么头指针L就代指一个单链表,头指针为“NULL”时则表示一个空表。
单链表第一个结点之前附加一个结点,称为头结点。头结点的数据域可以不设任何信息,也可以记录表长等相关信息。头结点的指针域指向线性表的第一个元素结点。
头结点和头指针的区别?
不管带不带头结点,头指针始终指向链表的第一个结点,而头结点是带头结点链表中的第一个结点,结点内通常不存储信息
为什么要设置头结点?
1.处理操作起来方便 例如:对在第一元素结点前插入结点和删除第一结点的操作与其它结点的操作就统一了。
2.无论链表是否为空,其头指针是指向头结点的非空指针,因此空表和非空表的处理也就统一了。
头插法建立单链表
建立新的结点分配内存空间,将新结点插入到当前链表的表头
LinkList CreatList1(LinkList &L){
LNode *s; //辅助指针
int x;
L=(LinkList)malloc(sizeof(LNode)); //创建头结点
L->next=NULL; //初始为空链表
scanf(”%d”,&x); //输入结点的值
while(x!=9999){ //输入9999表示结束
s=(LNode*)malloc(sizeof(LNode)); //创建新结点
s->data=x;
s->next=L->next;
L->next=s; //将新结点插入表中,L为头指针
scanf(”%d”,&x); //读入下一个结点值
}
return L;
}
头插法建立单链表,读入数据的顺序与生成的链表中元素的顺序是相反的。
尾插法建立单链表
建立新的结点分配内存空间,将新结点插入到当前链表的表尾,需要增加一个指向表尾元素的尾指针。
LinkList CreatList2(LinkList &L){
int x;
L=(LinkList)malloc(sizeof(LNode));
LNode *s, *r=L; //r为表尾指针 指向表尾
scanf(”%d”,&x); //输入结点的值
while(x!=9999){ //输入9999表示结束
s=(LNode *)malloc(sizeof(LNode));
s->data=x;
r->next=s;
r=s; //r指向新的表尾结点
scanf(”%d”,&x);
}
r->next=NULL; //尾结点指针置空
return L;
}
尾插法建立单链表,读入数据的顺序与生成的链表中元素L 的顺序是相同的。
按序号查找结点
在单链表中从第一个结点出发,顺指针next域逐个往下搜索,直到找到第i个结点为止,否则返回最后一个结点指针域NULL。
LNode * GetElem(LinkList L,int i){
int j=1; //计数,初始为1
LNode *p=L->next; //第一个结点指针赋给p
if(i==0) return L; //若i等于0,则返回头结点
if(i<1) return NULL; //若i无效,则返回NULL
while(p&&j<i){ //从第1个结点开始找,查找第i个结点
p=p->next;
j++;
}
return p; //返回第i个结点的指针,如果i大于表长,直接返回p即可
}
按值查找结点
从单链表第一个结点开始,由前往后依次比较表中各结点数据域的值,若某结点数据域的值等于给定值e,则返回该结点的指针;若整个单链表中没有这样的结点,则返回NULL。
LNode *LocateElem(LinkList L,ElemType e){
LNode *p=L->next;
while(p!=NULL&&p->data!=e)//从第1个结点开始查找data域为e的结点
p=p->next;
return p; //找到后返回该结点指针,否则返回NULL
}
插入
插入操作是将值为x的新结点插入到单链表的第i个位置上。先检查插入位置的合法性,然后找到待插入位置的前驱结点,即第i−1个结点,再在其后插入新结点。
算法思路:
1.取指向插入位置的前驱结点的指针
① p=GetElem(L,i-1);
2.令新结点s的指针域指向p的后继结点
② s->next=p->next;
3.令结点p的指针域指向新插入的结点s
③ p->next=s;
删除
删除操作是将单链表的第i个结点删除。先检查删除位置的合法性,然后查找表中第i−1个结点,即被删结点的前驱结点,再将其删除。
算法思路:
1.取指向删除位置的前驱结点的指针 p=GetElem(L,i-1);
2.取指向删除位置的指针 q=p->next;
3.p指向结点的后继指向被删除结点的后继 p->next=q->next
4.释放删除结点 free(q);