头指针
头指针是一个指针,它指向链表或者链表中的第一个节点。在链表中,每个节点包含一个数据项和一个指向下一个节点的指针。通过头指针,我们可以访问链表中的第一个节点,然后可以通过节点的指针访问下一个节点,以此类推。使用头指针,我们可以对链表进行插入、删除、查找等操作。(通俗理解:头指针就是头结点的地址)
头结点
头结点是链表中的一个特殊节点,它没有存储实际数据。头结点的作用是为链表提供一个统一的起点,使链表的操作更加方便。头结点通常位于链表的开头,它的下一个节点才是实际的第一个节点。头结点的指针指向第一个节点,通过头结点,我们可以轻松地插入、删除、遍历链表,而无需关心链表为空的特殊情况。头结点的优点是简化链表的操作代码,统一了链表的起点。。另外,头结点还可以存储链表的附加信息,如链表的长度等。并非所有的链表都使用头结点。
首元结点
首元结点指的是链表中的第一个实际节点,也叫做头节点或者头元素节点。它是链表中存储数据的第一个节点,即包含实际数据的节点。和头结点不同,首元结点存储了实际的数据,而不仅仅是一个占位符。它是链表的起始节点,通过首元结点可以遍历整个链表,访问和操作链表中的其他节点。
三者的关系图
引入头节点后,可以带来两个优点:
(1)由于第一个结点的位置被存放在头结点的指针域中,所以链表在第一个位置的操作和在表其他位置上的操作一致,无需进行特殊处理。‘
(2)无论链表是否为空,其头指针是指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也就统一了。带头结点的单链表L的判空条件为:L->next==NULL。
综上所述:头结点的引入统一了插入和删除操作对于在起始端和在其他位置的代码。
对比
无头结点
插入
//在起始端插入
LinkList L;
p=(LinkList)malloc(sizeof(LNode));
L=p;
//在其他位置插入
LinkList n=L
while(n&&j<i-1){//i为要插入的位置
n=n->next;
j++;
}
p=(LinkList)malloc(sizeof(LNode));
p->data=e;
p->next=p->next;
n->next=p;
删除
//在起始端删除
LinkList L;
p=L->next;
L=p->next;
free(p);
//在其他位置删除
LinkList n=L,q
while(n&&j<i-1){//i为要删除的位置
n=n->next;
j++;
}
q=n->next;
n->next=q->next;
free(q);
有头结点
插入
LinkList n=L
while(n&&j<i-1){//i为要插入的位置
n=n->next;
j++;
}
p=(LinkList)malloc(sizeof(LNode));
p->data=e;
p->next=p->next;
n->next=p;
删除
LinkList n=L,q
while(n&&j<i-1){//i为要删除的位置
n=n->next;
j++;
}
q=n->next;
n->next=q->next;
free(q);
添加头结点避免了特殊情况,他是我们能够表达基本的指针操作且又不致使特殊情况的代码混消不清。