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