一、概念
头结点:是虚拟出来的一个结点,不保存数据。头结点的next指针指向链表中的第一个节点。对于头结点,数据域可以不存储任何信息,也可存储如链表长度等附加信息。头结点不是链表所必需的。
头指针:是指向第一个结点的指针,如果链表没有引入头结点,那么头指针指向链表的第一个结点。头指针是链表所必需的。
[注意]有头结点,头指针指向头结点;没有头结点,头指针指向链表第一个结点,如果链表为空,头指针为NULL
二、为何引入头结点
-
防止头指针为NULL,有头结点,头指针始终指向头结点,那么无论链表是否为空,头指针均不为空;没有头结点,头指针就为NULL
-
插入/删除第一个结点时不需要修改头指针,只需要改变 头结点.next
-
有头结点时,插入/删除第一个结点时,空链表/非空链表操作逻辑一致,不需要额外判断
带头结点的单链表
不带头结点的单链表
三、单链表的创建(带头结点和不带头结点两种实现方式)
下面代码中头插法的两种实现方式可以佐证标题二中2、3两点,带头结点时不需要修改头指针,且空链表/非空链表的插入逻辑一致
if __name__ == '__main__':# 初始化链表class Node:def __init__(self, item):self.item = itemself.next = None# 链表的创建# 头插法:不使用头结点def create_linklist_head(li):if li == []:return Nonehead = Node(li[0]) # 链表为空时,头指针指向第一个结点for element in li[1:]: # 链表不为空时,要插入的结点.next指向头指针的结点,再修改头指针为新插入的结点node = Node(element)node.next = headhead = node # 头指针不断移动return head# 链表的创建# 头插法:使用头结点def create_linklist_head2(li):head = Node(None) # 头结点for element in li:node = Node(element)node.next = head.nexthead.next = nodereturn head.next# 尾插法:不使用头结点def create_linklidt_tail(li):if li == []:return Nonehead = Node(li[0]) # 链表为空时,头指针指向第一个结点tail = head # 尾指针指向同一个内存地址for element in li[1:]: # 链表不为空时,尾结点.next指向新插入的结点,尾指针指向新插入的结点node = Node(element)tail.next = nodetail = node # 尾指针不断移动return head# 尾插法:使用头结点def create_linklidt_tail2(li):head = Node(None) # 头结点tail = head # 尾指针指向同一个内存地址for element in li:node = Node(element)tail.next = nodetail = node # 尾指针不断移动return head.next# 遍历输出链表:def print_linklist(li):while li:print(li.item, end=',')li = li.nextprint(end='\n')li = create_linklist_head([1, 2, 3, 6, 8])print("不带头结点-头插法:")print_linklist(li)li2 = create_linklist_head2([1, 2, 3, 6, 8])print("带头结点-头插法:")print_linklist(li2)lk = create_linklidt_tail([1, 2, 3, 6, 8])print("不带头结点-尾插法:")print_linklist(lk)lk2 = create_linklidt_tail2([1, 2, 3, 6, 8])print("带头结点-尾插法:")print_linklist(lk2)
参考文章:
头结点的含义以及引入头结点的作用_snow_7的博客-CSDN博客_头结点

















