咱们继续——单向链表。
目录
1.链表
1.1链表的诞生
学完顺序表后,我们了解到数组作为存储结构有一定的缺点:
- 无序数组:搜索十分低效
- 有序数组:插入十分低效
- 两种数组:删除都很低效,且大小无法改变
为了解决顺序表的缺陷,链表就此诞⽣,链表也是继数组之后第⼆种使⽤的最⼴泛的通⽤数据结构。
1.2链式存储结构
1.概念:用指针将相互关联的结点链接起来。
2.特点
- 物理上不连续,逻辑上连续
- 大小不固定
- 基于指针实现
- 结点=数据域+指针域
1.3.分类
- 单向链表
- 单向循环链表
- 双向链表
- 双向循环链表
2.单向链表
2.1概念
单向链表的每个结点只包含⼀个指针域,即构成链表的每个结点只有⼀个指向后继节点的指针。
2.2头指针和头结点
1.单链表有带头结点和不带头结点两种结构。
2.第⼀个结点存储的位置叫头指针。如果链表有头结点,那么头指针就是指向头结点的指针。
3.头指针所指的不存数据元素的第⼀个结点就叫做头结点,头结点又指向⾸元结点。头结点⼀般不放数据(有时也放链表的⻓度,用做监视)。
4.存放第⼀个数据元素的结点叫作首元结点。
2.3操作
以带头结点的单向链表举例元素的插入(头插+中间插入)与删除。
1.头插法
在头结点后插入新的结点。需要先给插入结点的指针赋值,再改变头结点指针,使其指向插入结点,两者顺序不可颠倒。
/*头插法*/
//在头结点后,其他结点前插入元素
//node:头结点,n:待插入元素,insert:待插入结点
void HeadInsert(Link* node, int n)
{
Link* insert = (Link*)malloc(sizeof(Link));
insert->data = n;
insert->next = node->next;
node->next = insert;
}
2.中间插入
在中间位置插入结点。插入逻辑同头插法,但需要一个临时指针temp指向插入位置的前一个结点(通过循环实现),而不移动头指针。
void MidInsert(Link* node, int n, int i)
{
Link* insert = (Link*)malloc(sizeof(Link));
Link* temp = (Link*)malloc(sizeof(Link));
temp = node->next;
//将temp指向插入位置后的元素
int j = 0;
while (temp != NULL && j < i - 1)
{
temp = temp->next;
j++;
}
if (temp == NULL && i > j - 1)
printf("没有此位置,插入失败\n");
insert->data = n;
insert->next = temp->next;
temp->next = insert;
}
3.删除
删除结点在中间位置时,同样需要一个临时指针temp,原因同上。先保存要删除的结点,将删除结点的前一个结点指针指向删除结点的后一个结点,然后释放待删除结点。
* 将图左上方“中间插入”改为“删除”
/*删除元素*/
void delete(Link* node, int n)
{
Link* temp = (Link*)malloc(sizeof(Link));
temp = node->next;
//找到删除元素下标,并把temp指向要删除结点的前一个结点
int i = find(node, n);
if (i == -1)
printf("没有此元素,删除失败\n");
else
{
int j = 0;
while (j < i - 1)
{
temp = temp->next;
j++;
}
//先保存要删除的结点,再改变指针指向
Link* FreeNode = temp->next;
temp->next = temp->next->next;
free(FreeNode);
}
}
2.4完整代码
完整C语言代码嗷~
#include<stdio.h>
#include<stdlib.h>
typedef struct LinkList
{
int data;//数据域
struct LinkList* next;//指针域
}Link;
Link* InitList();
void HeadInsert(Link* node, int n);
void MidInsert(Link* node, int n, int i);
int find(Link* node, int n);
void delete(Link* node, int n);
void show(Link* node);
int main()
{
Link* node = InitList();
HeadInsert(node, 1);
HeadInsert(node, 2);
HeadInsert(node, 3);
HeadInsert(node, 4);
show(node);//输出4321
printf("\n");
MidInsert(node, 5, 1);
show(node);//输出45321
printf("\n");
int i = find(node, 2);
printf("%d\n", i);//输出3
delete(node, 3);
show(node);//输出4521
return 0;
}
/*初始化单链表*/
//创建一个头结点
Link* InitList()
{
Link* node = (Link*)malloc(sizeof(Link));
if (node == NULL)
printf("初始化函数已执行,内存分配失败\n");
else
node->next = NULL;
return node;
}
/*头插法*/
//在头结点后,其他结点前插入元素
//node:头结点,n:待插入元素,insert:待插入结点
void HeadInsert(Link* node, int n)
{
Link* insert = (Link*)malloc(sizeof(Link));
insert->data = n;
insert->next = node->next;
node->next = insert;
}
/*中间插入*/
//在第i个位置前插入一个元素n
//temp:临时指针,用于移动找位置,而不用移动头指针
void MidInsert(Link* node, int n, int i)
{
Link* insert = (Link*)malloc(sizeof(Link));
Link* temp = (Link*)malloc(sizeof(Link));
temp = node->next;
//将temp指向插入位置后的元素
int j = 0;
while (temp != NULL && j < i - 1)
{
temp = temp->next;
j++;
}
if (temp == NULL && i > j - 1)
printf("没有此位置,插入失败\n");
insert->data = n;
insert->next = temp->next;
temp->next = insert;
}
/*查找元素*/
//temp一直挨个向后移动,若发现有,则返回下标,直到指向空
int find(Link* node, int n)
{
Link* temp = (Link*)malloc(sizeof(Link));
temp = node->next;
int i = 0;
while (temp != NULL)
{
if (temp->data == n)
return i;
else
{
temp = temp->next;
i++;
}
}
return -1;
}
/*删除元素*/
void delete(Link* node, int n)
{
Link* temp = (Link*)malloc(sizeof(Link));
temp = node->next;
//找到删除元素下标,并把temp指向要删除结点的前一个结点
int i = find(node, n);
if (i == -1)
printf("没有此元素,删除失败\n");
else
{
int j = 0;
while (j < i - 1)
{
temp = temp->next;
j++;
}
//先保存要删除的结点,再改变指针指向
Link* FreeNode = temp->next;
temp->next = temp->next->next;
free(FreeNode);
}
}
/*展示数据元素*/
void show(Link* node)
{
Link* temp = (Link*)malloc(sizeof(Link));
temp = node->next;
while (temp != NULL)
{
printf("%d", temp->data);
temp = temp->next;
}
}