链表是数据结构最为基础的一种 学习之后想要自己实现一下 所以就开了一个长期更新的博客 记录自己实现各种数据结构的帖子 都是一些比较基础的
链表由一系列不必在内存中连续的结构组成。每一个结构都含有表元素和指向包含该元素的后继元的结构的指针,我们称之为Netx指针,最后一个单元的Next指针指向NULL,即指向空;因此就有以下定义的结构体表示的链表节点:
/*定义线性链表的结构*/
typedef struct LNode
{
int date; //数据域
struct LNode *next; //指向下一个节点的指针,即指针域
}LNode;
有了节点之后,我们就需要创建一个链表,因此用以下函数来创建一个带有头结点的链表(该创建方法被称为尾插法)
LNode* CreateList(int n)
{
LNode* s;
LNode* r; //分别创建用来指向新节点(s)和尾节点(r)的指针
/*我认为这里是对节点L的重复初始化,但是在主函数中不对节点L进行初始化会报错 L未被初始化*/
LNode *L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
r = L;
for (int i = 0; i < n; i++)
{
s = (LNode*)malloc(sizeof(LNode)); //为新节点申请内存空间
printf("输入数据:");
scanf("%d", &s->date);
r->next = s; //将新节点“连接”在尾节点之后
r = s; //新节点变为尾节点
}
if(L->next != NULL)
r->next = NULL; //将尾节点的指针域置位空
return L;
}
成功创建了一个链表之后,相信很多人都想看看链表所保存的数据是不是自己所输入的数据,因此,我们就用一个遍历链表的函数将数据全部输出,并检查是否和自己所输入的数据相符合:
//遍历链表,输出数据
void OutputLinkList(LNode *L)
{
LNode* temp = L; //使用临时变量temp暂存头结点的值 防止遍历链表时头结点移动到尾节点导致链表丢失
while (temp->next != NULL)
{
//由于链表含有头结点 所以应该从链表的第二个节点开始输出并且依次后移
temp = temp->next;
printf("%d ", temp->date);
printf("\n");
}
}
注意,由于这是带有头结点的链表,所以应该从链表的第二个节点输出,上述代码已经说明,就会不赘述了
判断链表是否为空
//判断是否为空链表
void IsEmpty(LNode *L)
{
/*判断是否为空列表只需要判断头结点的指针域是否置空,若置空则为空链表,反之则为非空链表*/
if (L->next == NULL)
printf("空链表!");
else printf("非空链表!");
}
有了链表,我们当然就需要获取链表中指定位置的元素
//定位获取链表中某一个元素 参数position是指定的位置
int GetNode(LNode *L, int *n, int position)
{
LNode *P = L; //创建临时变量并赋值为头结点 以免后移时丢失头结点位置
int j = 0; //计数器 由于这是带有头结点的链表 头结点中的数据域数据位空 所以计数器从0开始
while ((P != NULL) && (j <= position))
{
//指针后移 查找指定元素
P = P->next;
++j;
}
//未查找到指定元素的条件
if ((P == NULL) || (j > position))
return false;
else
{
*n = P->date;
return true;
}
}
其中,应该注意区分获取指定元素和指定位置元素的区别,获取指定位置的元素指的是链表中第几个元素。
程序员的工作被称为增删改查,上面有了对链表的查,接下来介绍对链表的增·删·改
向链表中增加节点,也被称作向链表中插入节点:
//插入新节点到链表中指定的位置
LNode* InsertNode(LNode *L)
{
LNode *P = L;
int j = 0; //计数器 由于有数据域为空的头结点所以从0开始
int inser_position; //新节点将插入到position之前
printf("插入到链表中的位置:");
scanf("%d", &inser_position);
//定位第inset_position-1个节点的位置
while ((P != NULL) && (j < inser_position - 1))
{
P = P->next;
++j;
}
LNode *new_node = (LNode*)malloc(sizeof(LNode)); //为新插入的节点申请内存空间
printf("输入节点的值:");
scanf("%d", &new_node->date);
new_node->next = P->next;
P->next = new_node;
return L;
}
由于链表在内存中的不连续性,所以要先将新节点和第position个节点链接起来,然后再讲第position-1个节点和新节点链接起来
链表节点的删除:
//删除链表中指定位置的节点
LNode* DeleteNode(LNode *L)
{
int j = 0; //计数器
LNode *P = L;
int delete_position; //删除节点的位置
printf("输入需要删除节点的位置:");
scanf("%d", &delete_position);
//寻找第delete_position - 1个节点的地址并保存在 P 中
while ((P->next != NULL) && j < delete_position - 1)
{
P = P->next;
++j;
}
if ((P->next == NULL) && (j > delete_position - 1))
{
printf("输入的位置非法,程序退出!\n");
return L;
}
LNode *Q = P->next; //记录第delete_position节点的地址
P->next = Q->next; //删除指定的节点
free(Q); // 释放指定节点的内存空间
return L;
}
链表节点数据域的值改动过于简单就不做叙述,至此链表基本的增删改查全部介绍完。