1、关于是否有头节点的区别
无论是否有头节点,头指针都不为空。
2、创建链表并初始化
typedef struct SListnode
{
SLTdatatype data;//创建数据域
struct SListnode* next;//创建指针域,保存下一个节点的地址
//对结构体类型重命名后,不能用重新命名的名字对结构体进行自引用
}SLnode;
void test01()
{
//为一个结点分配内存空间
SLnode* n1 = (SLnode*)malloc(sizeof(SLnode));
//断言n1 的空间 防止n1开辟空间失败,返回空指针
assert(n1);
SLnode* n2 = (SLnode*)malloc(sizeof(SLnode));
assert(n2);
SLnode* n3 = (SLnode*)malloc(sizeof(SLnode));
assert(n3);
SLnode* n4 = (SLnode*)malloc(sizeof(SLnode));
assert(n4);
//给每个结点的数据域赋值
// -> 和 '.'的作用都是对结构体成员进行访问
// n1->data 等价于 (*n1).data
//n1 到n4 这些都是指的单个结点的地址(也就是指针变量)
n1->data = 1;
n2->data = 2;
n3->data = 3;
n4->data = 4;
//给每个结点的指针域赋值
n1->next = n2;
n2->next = n3;
n3->next = n4;
//n4为最后一个结点,在单链表中,最后一个结点赋NULL
n4->next = NULL;
3、插入数据
头插需要改变头指针的指向,所以需要传头指针的地址, 形参的类型为二级指针类型。
//尾插
void SListpushback(SLnode** head,SLTdatatype x)
{
SLnode* node = (SLnode*)malloc(sizeof(SLnode));
assert(node);
SLnode* tem = *head;
node->data = x;
//将插入的结点的指针域赋值NULL,这样新插入的结点就变成了尾结点了
node->next = NULL;
if (*head==NULL)
{
//判断头结点是否为NULL
//若不判断,当头结点为NULL时,不满足下面进入循环的条件,插入失败
//
//若为NULL,则直接将新创建的结点赋值给头结点
*head = node;
}
else
{
//尾结点的特征为 指针域的值为NULL
//所以 只要找到指针域为NULL的结点就是尾结点
while (tem->next != NULL)
{
tem = tem->next;
}
//将插入的结点的地址赋值给尾结点的指针域
tem->next = node;
}
}
//头插
void SListpushfront(SLnode** head, SLTdatatype x)
{
SLnode* node = (SLnode*)malloc(sizeof(SLnode));
assert(node);
node->data = x;
node->next = *head;
*head = node;//这里的操作是把让head指针重新成为头节点
//头插不需要判断头结点是否为NULL,因为最后头指针会移动到新的头结点上
}
//在pos位置之前插入
void SListpushpos(SLnode** head,SLnode*pos,SLTdatatype x)
{
assert(pos);
assert(head);
//创建新结点,数据域为x,指针域为NULL
SLnode* node = (SLnode*)malloc(sizeof(SLnode));
assert(node);
node->data = x;
node->next = NULL;
SLnode* tem = *head;
if (pos==*head)
{
//若pos是头结点,则是头插,直接调用头插函数
SListpushfront(head, x);
}
else
{ //遍历找到pos的前一个结点
while (tem->next!=pos)
{
tem = tem->next;
}
node->next = tem->next;
tem->next = node;
}
}
在尾插的时候需要判断头节点是否为空;
4、在当前位置插入结点
这个的注意点是需要有一个节点来保存前一个节点。