- child::单向链表常用操作
适用范围:
优缺点:
优点:
- 可以快速删除、插入、追加节点
缺点:
- 查找困难
- 比顺序表占用多一点内存
单向链表结构梗概:
- 若干个结点(NODE), 具有
数据域
和地址域
- 特殊的结点: 一个头结点
- 其数据域一般用来存储链表的元数据
- 地址域指向第一个中间结点 (没有则指向
空
)
- 特殊的结点: 一个尾节点(TAIL)(一般很少用到)
- 数据域为
空
- 地址域为
空
- 数据域为
- 普通的结点: 中间结点
- 数据域存放该结点所代表的数据
- 地址域指向下一个中间结点(没有则指向
空
)
- 特殊的结点: 一个头结点
- 一个指向链表头结点的头指针(head)
单向链表在c语言中的实现:
定义单向链表的节点类型
typedef struct Node{
元素类型 data; //数据域
struct Node * next; //地址域
} Node, * LinkList; //Node为该结构体类型的名字 , LinkList为该结构体指针类型的名字
C语言中链表的惯用标识符:
- Node用来定义节点
- LinkList用来定义节点指针
- L表示头指针
作用:
- 创建LInkList结构体指针类型的作用:
- 方便把地址做强制类型转换为该节点的类型
- 方便定义指向该节点的指针
对链表操作的实现:
初始化链表(建立空链表)
InitList(LinkList *L){ //传入头指针的地址
*L = (LinkList)calloc(1,sizeof(Node)); //把传入的地址作为头指针, 创建头结点, 并让头指针指向头节点
(*L) -> next = NULL; //把头结点指向空
}
作用:
- 传入Node类型的地址, 返回一个空链表
头插法建表
梗概:
- 把新的节点都插到头结点之后
性质:
- 逻辑顺序与输入顺序相反
- 头结点处于最后一个
c实现:
int CreateFromHead(LinkList L){
Node *s;//插入的节点地址
ElemType c;
while ((c=getchar())!='$'){//'$'为结束符
s = (Node*)calloc(1,sizeof(Node));
if (s == NULL) return ERROR;
s->data = c; //给数据域赋值
s->next = L->next;//先把新节点连上
L->next = s;//把前驱断开,连上新节点
}
return OK;
}
尾插法
梗概:
- 把新节点插入到尾节点处
- 把最后一个节点的地址域设为空
按序号查找
/*找第i个节点,返回其地址或空*/
Node *Get(LinkList L, int i){
Node *p;//p为节点探针
int j;//j为计数器
if (i<1) return NULL;//处理不合理的节点
p=L->next; j=0; //先测探第一个
while (p!=NULL){ //分析探测存不存在
j++; //探测后计数
if(i==j)break;//计数后够了就停
p = p->next; //探测下一个
}
return p;//保证返回找不到NULL
}
按值查找
/*查找节点值为key的第一个节点,返回其地址或空*/
Node *Locate(LinkList L, ElemType key){
Node *p;//探针
p=L->next;//先探测第一个
while(p!=NULL){//分析探测结果存不存在
if(p->data==key) break;//找到就停下
p=p->next;//继续探测下一个
}
return p; //保证找不到返回空
}
求链表长度
/*求长度,返回长度*/
int ListLength(LinkList L){
Node *p;//探针
int j=0;//计数
p=L->next;//探测第一个
while (p!=NULL){//分析探测结果存不存在
j++;//结果并入计数
p=p->next;//探测下一个
}
return j;
}
单链表插入操作
梗概:
- 插在头结点后的任意位置
考虑情况:
- 空链表
- 中间位置
- 最后一个节点的后面
c语言实现:
/*在单链表L中第i个节点之前插入值为e的新节点*/
int InsList(LinkList L, int i, ElemType e){
Node *pre, *s;//pre为直接前驱, *s为插入节点
if (i<1) return ERROR;//处理前驱越界
pre=((i==1)?L:Get(L,i-1));//找前驱
if (pre==NULL){//处理前驱不存在
printf("插入位置不合理!");
return ERROR;
}
s =(Node*)calloc(1,sizeof(Node));
s->data=e;
s->next =pre->next;//先接上后继
pre->next=s;//后断开,接上前驱
return OK;
}
删除操作:
梗概:
- 删除除头结点的任意节点
考虑:
- 第一个节点
- 中间位置
- 最后一个节点
- 最后一个节点指向空
- 删除最后一个节点之后, 前驱指向直接后继, 也为空, 和中间位置的情况一样
- 删除的节点不存在
c实现:
/*在链表L中删除第i个元素,并将删除的元素保存到*e中*/
int DelList(LinkList L,int i,ElemType *e){
Node *pre,*r; //pre直接前驱,*r为目标
if(i<1) return ERROR; //处理前驱越界
pre=((i==1))?L:Get(L,i-1)); //找前驱
if (pre==NULL||pre->next==NULL){ //处理前驱不存在和目标不存在
printf("删除的节点位置不合理!");
return ERROR;
}
r = pre->next; //通过前驱找到目标
pre->next=r->next;//前驱跳过目标接上后继
*e=r->data; free(r);
return OK;
}
实际运用
常见需求
child::单链表就地逆置