单链表
1.定义
利用 不连续 的存储空间来存储 的线性结构
typedef struct LNode{
ElemType data;
struct LNode* next;
}LNode,*LinkList // 注意中有解释为什么可以这样写
2.操作的实现
2.1 不带头结点(不太方便)
2.2 带头结点(方便)
初始化、判空、插入(按位序插入、指定结点前/后插)、按位序删除、按位查找、按值查找、求单链表的长度
typedef struct LNode{
int data;
struct LNode* next;
}LNode,*LinkList;
// 初始化
bool InitList(LinkList &L){
// 初始化指向头结点的指针
L=(LNode*)malloc(sizeof(LNode));
if(L==NULL)
return false;
else{
L->next=NULL; // 指针 访问成员变量 用 ->
return true;
}
}
// 判空
bool IsEmpty(LinkList L){
if(L->next==NULL)
return true;
else
return false;
}
// 插入
// 1.按位序插入带头结点
bool InsertByI(LinkList &L, int i, int data){
LNode *LB=L;
int j=1; // j=1 表示 初始化时候LB->next==第一个结点
// 插入位置不合法
if(i<1)
return false;
while(j<i && LB!=NULL){
j++;
LB=LB->next;
}
if(LB=NULL)
// 插入位置不合法
return false;
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL)
return false;
s->data=data;
s->next=LB->next;
LB->next=s;
return true;
}
// 2.指定结点的后插操作
bool InsertNextByP(LNode *p, int e){
if(p==NULL)
return false;
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL)
return false;
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
// 3.指定结点的前插操作 找p结点的前驱
bool InsertPriorByP(LinkList &L, LNode *p, int e){
LNode *LB=L;
while(LB->next!=p && LB!=NULL)
LB=LB->next;
if(LB==NULL)
return false;
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL)
return false;
s->data=e;
s->next=LB->next;
LB->next=s;
return true;
}
// 3.指定结点的前插操作 不用找 直接在p结点之后插入一个结点 将两个赋值交换
bool InsertPriorByP_2(LNode *p, int e){
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL)
return false;
s->next=p->next;
p->next=s;
int pe=p->data;
s->data=pe;
p->data=e;
return true;
}
// 按位序删除
bool DeleteByI(LinkList &L, int i, int &e){
int j=1;
LNode *LB=L;
while(j<i && LB!=NULL){
j++;
LB=LB->next;
}
if(LB==NULL)
return false;
if(LB->next==NULL)
return false;
LNode *p=LB->next;
e=LB->next->data;
LB->next=LB->next->next;
free(p);
return true;
}
// 查找
// 按位置查找 返回该位置上的值
bool SelectByI(LinkList L, int i,int &e){
if(i==0)
return false;
if(L==NULL)
return false;
int j=1;
LNode *LB=L;
while(j<i && LB!=NULL){
j++;
LB=LB->next;
}
if(LB==NULL)
return false;
if(LB->next==NULL)
return false;
e=LB->next->data; //j==i的时候,LB->next指向的就是第i个元素
return true;
}
// 按值查找 返回位置
bool SelectByVal(LinkList L, int &i, int e){
if(L==NULL)
return false;
int j=1;
LNode *LB=L;
while(LB->next!=NULL && LB->next->data!=e){
// 进入循环,表示在第j个位置上不符合e
j++;
LB=LB->next;
}
if(LB->next==NULL)
return false;
i=j;
return true;
}
// 求单链表的长度
int ListLength(LinkList L){
if(L->next==NULL)
return 0;
LNode *LB=L;
int j=0;
while(LB->next!=NULL){
j++;
LB=LB->next;
}
return j;
}
// 给一定量的数据,建立单链表 头插法 尾插法
// 尾插法 添加一个尾指针,指向最后一个元素
bool TailInsertLinkList(LinkList &L){
L=(LNode*)malloc(sizeof(LNode));
if(L==NULL)
return false;
L->next=NULL;
// 尾指针 新来的元素
LNode *r=L, *s;
int x=-1;
scanf("%d",&x);
while(x!=9999){
s=(LNode*)malloc(sizeof(LNode));
if(s==NULL)
return false;
s->data=x;
s->next=NULL;
r->next=s;
r=s;
scanf("%d",&x);
}
return true;
}
// 头插法建立单链表
bool HeadInsertLinkList(LinkList &L){
L=(LNode*)malloc(sizeof(LNode));
if(L==NULL)
return false;
L->next=NULL;
// 头指针
LNode *LB=L;
int x=0;
scanf("%d",&x);
while(x!=9999){
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL)
return false;
s->data=x;
s->next=LB->next;
LB->next=s;
scanf("%d",&x);
}
return true;
}
3.优缺点
相较于顺序表(不管是静态实现还是动态实现),
1.不需要大片的连续空间,改变容量方便(增删快)
2.存储密度相对较低
3.不能随机存取 (查询时间长)
4.注意⚠️:
1.typedef关键字
语法格式:
typedef <数据类型> <别名>
例如:
typedef int zhengshu
typedef struct LNode LNode(解释:用LNode来作为 struct LNode的别名)
typedef struct LNode *LinkList (LinkList 作为 LNode类型的指针)
2.LinkList和LNode*的区别
1.本质区别不大 都是指向一个单链表结点的指针
2.LinkList:通常指 单链表的头指针
LNode* :指 指向单链表一个结点的指针
3.常见方法的应用‼️
1.头插法
单链表的逆置
2.单链表中指定结点p的前插操作
3.单链表中删除结点p