提示:
链表结点是动态创建的所以要用指针来操作->
链式结构中,除了要存数据元素信息外,还要存储后继元素的存储地址
对数据元素ai来说除了存储数据元素信息之外还要存储指针,所以一个结点有一个数据域+一个指针域组成(前数据后指针)
n个结点链成一个链表,为逻辑结构线性表的链式储存结构,因此每个结点只包含一个指针域,故此称之为单链表
提示:
链表中第一个结点的储存位置叫做头指针
最后一个指针指向为NULL(他的后继不存在了)
链表的第一个结点前会设置一个头结点,可以不存储数据元素 但要存储在地址域内存第一个结点的地址信息
头结点可以存线性表长度等公共数据

- 头指针:指向链表第一个数据结点的指针
- 头结点:链表中的第一个结点,通常不存数据
- 关系:头指针指向头结点,头结点的 next 指向第一个数据结点
- 选择:工程中通常使用头结点,因为代码更简洁统一
总结不是必须但建议使用 统一结构
由此可以得出链表定义的方法
typedef struct Node
{
int data;//数据域
Node *next;//指针域
} Node;
typedef struct Node *LinkList; //把 struct Node *(指向 Node 结构体的指针)起一个别名叫 LinkList
假设p是一个指针当p->data指向的是一个数据元素,当p->next next是个指针 存储下一个结点的地址,然后指向下一个结点
单链表的读取
必须从头开始 所以时间复杂度为O(n)
核心思想 你调用的指针在内存中根据指针域进行后移
Status GetElem(LinkList L,int i,ElemType *e){
int j;
LinkList P;
p = L->next;
j=1;
while(p&&j<i){
p = p->next;
++j;
}
if(!p||j>i){ //后面没有结点了第i个结点不存在
return ERROR;
}
*e =p->data; // 用e来取数据,定义的指针指向了数据域
return OK;
}
思路:
1.声明一个指针p指向链表指向第一个结点 初始化j从1开始
2.j<i 遍历整个链表 让p向后移动 不断指向下一个结点 j++;
3.如果到链表末尾p为空,则说明第i个结点不存在
4.否则查找成功返回p的数据
单链表的插入
很简单的逻辑既然这个链表结点有指针域来专门存指针,那我们就直接更改指针域的指向即可。
原先是p->next 现在要插入一个带s指针的结点进入
那就应该s->next=p->next; //把p的后继结点改成s的后继结点
p->next=s;再把结点s改成p的后继结点
注意:
顺序不能颠倒:
如果颠倒了:会把p->next赋值为s的地址 s->next = p->next ,相当于s->next=s,他就没有上级结点了所以是失效的
对于链表的表头和表尾其实是一样的只不过是上下级分别是头结点和尾节点罢了
单链表插入结点的思路:
- 声明一个指针p指向头结点 初始化j从1开始
- j<i 遍历链表,将p指针移动向后 j++;
- 到链表末尾p仍然为空那就证明第i个结点不存在
- 否则查找成功生成一个空节点s
- 把元素e 赋值为s->data;
- 单链表插入s->next=p->next;p->next=s;
- 返回值
Status ListInsert(LinkList *L ,int i,ElemType e){
int j;
LinkList p,s;
p=*L;
j=1;
while(p&&j<i){ //找i-1结点
p=p->next;
++j;
}
if(!p||j>i) return ERROR; //第i个结点不存在
s=(LinkList)malloc(sizeof(Node));//分一个新的结点空间
s->data = e;
s->next = p->next;//p的后继结点赋值给s的后继结点
p->next = s;//把s结点的赋值给p->next
return OK;
}
单链表的删除
注意:
后继结点就是结点的下一个,就是
->next指向的那个。
p是q是上一级结点
假如我们要删除q结点那很简单 把q结点的上级结点的后继结点指向q下一级的结点
先记住q结点要删除结点的地址q结点的地址是q=p->next p的后继结点是q结点。
我们记住之后然后把p->next改成q->next也就是p->next->next 这样我们直接把p的后继结点改到了q的下一级 这样就跳过了这个我们要删除的这个部分 然后我们一开始记录的那个q节点的地址就可以被删除释放这个空间了