2.3.1线性链表
单链表,用一个指针表示逻辑关系。
特点:每个数据元素由节点(Node)构成。
|data|next|
线性结构
head->a1|->a2|->a3|^
![](https://i-blog.csdnimg.cn/blog_migrate/f6823028ebf902ae810076b7331c45d4.png)
节点可以连续也可以不连续。
首节点:
链表中第一个元素节点成为首节点
尾节点:
链表中最后一个元素,指针域为空。
单链表分为带头结点和不带头结点两种类型。有头节点可以简化运算的实现过程。
![](https://i-blog.csdnimg.cn/blog_migrate/e860b786183152588835786ad3ad4b0f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0098989cd1e90e188a9271cb5bd24f8e.png)
空链表的标志就是头节点的指针域为空。
代码cpp
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef int Status;
#define OK 1
#define ERROR 0
//-------------------结点的c语言描述
typedef struct node
{
ElemType data; // 数据
struct node *next; // 后继结点指针。指针类型是node,去掉指针后会有语法错误,因为是递归定义
} LNode, *LinkList; // LinkList是结构体指针
Status InitList(LinkList &L);
Status DestroyList(LinkList &L);
int ListLength(LinkList L);
int LocateElem(LinkList L, ElemType e);
Status LintInsert_L(LinkList &L, int i, ElemType e);
Status ListDelete_L(LinkList &L, int i, ElemType &e);
void Dis_List(LinkList &L);
int main()
{
LinkList L;
InitList(L);
printf("刚初始化的链表长度:%d\n", ListLength(L));
LintInsert_L(L, 1, 6);
printf("插入一个元素以后的链表长度:%d\n", ListLength(L));
Dis_List(L);
LintInsert_L(L, 2, 66);
Dis_List(L);
LintInsert_L(L, 3, 666);
Dis_List(L);
int e;
ListDelete_L(L,1,e); //删除第i个元素并返回给e
printf("删除第1个元素后:\n");
Dis_List(L);
printf("删除的值是:e=%d\n",e);
printf("删除后长度:%d\n",ListLength(L));
printf("元素666在列表中的位序:%d",LocateElem(L,666)); //查找指定元素在列表中的下标
DestroyList(L);//销毁线性表
//printf("销毁后长度:%d\n",ListLength(L)); //销毁后无法查看线性表的长度,会报错
}
// (1)
// 初始化单链表算法
// 创建一个空的单链表,他只有一个头结点,由L指向他。该
// 节点的next域为空,data域未设定任何值。
Status InitList(LinkList &L)
{ // 操作结果:构造一个空的线性表
// 产生头节点,并使L指向此头节点
L = (LinkList)malloc(sizeof(LNode));
if (!L) // 空间分配失败
return ERROR;
L->next = NULL; // 指针域为空
return OK;
}
// (2)销毁单链表
// 一个单链表中所有的结点空间都是malloc函数分配的,在不需要的时候要通过free函数释放所有的节点空间
// 小心后继节点消失
Status DestroyList(LinkList &L)
{
// 初始条件:线性表L已存在。操作结果:销毁线性表L
LinkList p; // 定义一个结构体指针p
while (L)
{ // 当L不为空的时候
p = L->next; // p指向首元结点
free(L); // 释放L
L = p; // 将p给L
}
return OK;
}
// (3)求单链表长度算法,需要扫描一遍单链表
// 设置一个计数器,i=0,p初始指向第一个数据节点。然后沿next域逐个往后查找,每次移动一次
// i值增加,当p所指结点为空时,结束这个过程,i值就是表长
int ListLength(LinkList L)
{
// 初始条件:线性表L已存在。操作结果;返回L中数据元素个数
int i = 0;
LinkList p = L->next; // 指向第一个结点
while (p)
{ // 当p不空的时候,没有到表尾
i++;
p = p->next;
}
return i;
}
//(4)求单链表中值为e的元素的下标
// 用p从头开始遍历单链表L中的结点,用计数器i统计遍历过后
// 的节点,起初为0
// 在遍历时,若p不为空,则p所指结即为要找到的结点,
// 查找成功,算法返回位序i
// 否则算法返回0
int LocateElem(LinkList L, ElemType e)
{
// 操作结果:返回L中第i个与e相等的数据元素的位序
// 若这样的数据元素不存在,则返回0
int i = 0;
LinkList p = L->next; // 定义结构体指针p,指向第一个结点
while (p)
{ // 如果第一个结点不存在也就是空,就不会进行下边的循环
// 刚开始p是第一个结点然后p会向后移动,知道p为空
i++;
if (p->data == e)
{
return i; // 找到这样的数据元素
}
p = p->next;
}
return 0;
}
//(5)单链表的插入
// 在单链表L中第i个位置,插入值为x得结点
// 先在单链表L中查找第i-1个节点,若未找到返回0
// 找到后由p指向该节点,创建一个以x为值的新结点s,将其插入到p指结点之后。
Status LintInsert_L(LinkList &L, int i, ElemType e)
{
// printf("e=%d\n", e);
// 在带头结点的单链线性表L的第i个元素之前插入元素e
LinkList p, s;
p = L;
int j = 0;
while (p && j < i - 1)
{ // 找到i-1个结点 让p指向他
p = p->next;
++j;
}
if (!p || j > i - 1)
{ // i<1或大于表长
return ERROR;
}
s = (LinkList)malloc(sizeof(LNode)); // 生成新结点
s->data = e;
s->next = p->next; // 插入L中
p->next = s;
//printf("%d", (s->data));
return OK;
}
//(6)单链表的删除操作
// 在单链表中删除第i个结点
// 先在单链表L中查找第i-1个节点,若未找到返回0
// 找到后由p指向该节点,然后让q指向后继节点(要删除的节点)
// 若q所指向结点为空则返回0,否则删除q结点并释放其占用空间
Status ListDelete_L(LinkList &L, int i, ElemType &e)
{
// 在带头结点的单链表L中,删除第i个元素,并由e返回其值
LinkList p, q;
p = L;
int j = 0;
// 寻找第i个结点,并令p指向其前驱
while (p->next && j < i - 1)
{
p = p->next;
++j;
}
if (!(p->next) || j > i - 1)
return ERROR; // 删除位置不合法
q = p->next; // 把要删除的元素给q
p->next = q->next; // 要删除的元素的下一个节点给p的下一个结点,就是会将要删除的元素给隔过去
e = q->data; // 将要删除的数据赋值给e
free(q); // 释放要删除的元素
return OK;
}
void Dis_List(LinkList &L)
{
LinkList p;
p = L->next;
while (p)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}