2022 -1-14

1. 定义:

头结点(不一定有),存在为了方便操作,头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度),对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了。

首元结点:实际记录数据的第一个节点;

头指针:指向第一个物理节点地址的指针,就是定义的链表名,这个头指针的意义在于,在访问链表时,总要知道链表存储在什么位置(从何处开始访问),由于链表的特性(next指针),知道了头指针,那么整个链表的元素都能够被访问,也就是说头指针是必须存在的。


2021-10-31【数据结构练习题】【删除结点*s的直接前驱】

2. 哨兵(头结点)

如果我们在结点 p 后面插入一个新的结点,只需要下面两行代码就可以搞定。

new_node->next = p->next;
p->next = new_node;

当我们要向一个空链表中插入第一个结点,刚刚的逻辑就不能用了。我们需要进行下面这样的特殊处理,其中 head 表示链表的头结点。所以,从这段代码,我们可以发现,对于单链表的插入操作,第一个结点和其他结点的插入逻辑是不一样的。

if (head == null) {
   
    head = new_node;
}

单链表结点删除操作。如果要删除结点 p 的后继结点,我们只需要一行代码就可以搞定。

p->next = p->next->next;

如果我们要删除链表中的最后一个结点,前面的删除代码就不工作了。跟插入类似,我们也需要对于这种情况特殊处理。写成代码是这样子的:

if (head->next == null) {
   
    head = null;
}

针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理,这样代码实现起来就会很繁琐,不简洁,哨兵就是解决“边界问题”的,不直接参与业务逻辑。

空链表:head=null 表示链表中没有结点了。其中 head 表示头结点指针,指向链表中的第一个结点。

引入哨兵结点,在任何时候,不管链表是不是空,head 指针都会一直指向这个哨兵结点。我们也把这种有哨兵结点的链表叫带头链表。相反,没有哨兵结点的链表就叫作不带头链表。

哨兵结点是不存储数据的。因为哨兵结点一直存在,所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一为相同的代码实现逻辑了。

3. 有无头结点的单链表的创建

3.1 有头结点

3.1.1 头插法:

typedef struct ListNode{
   
    int val;
    ListNode *next;
}ListNode,*Listline;
// 头插法
ListNode* CreateList(int length){
   
    if (length < 1)
        return NULL;
    ListNode *head = (ListNode*)malloc(length*sizeof(ListNode));
    head -> next = NULL;
    int k = 1;
    ListNode *s = NULL;
    while (k <= length){
   
        s = (ListNode*)malloc(sizeof(ListNode));
        s -> next = head -> next;
        head -> next = s;
        k++;
    }
    return head;
}
 

3.1.2 尾插法:

typedef struct ListNode{
   
    int val;
    ListNode *next;
}ListNode,*Listline;
// 尾差法
ListNode* CreateList(int length){
   
    if (length < 1)
        return NULL;
    ListNode *head = (ListNode*)malloc(length*sizeof(ListNode));
    ListNode *s = head;
    int k = 1;
    ListNode *r = NULL;
    while (k <= length){
   
        r = (ListNode*)malloc(sizeof(ListNode));
        s -> next = r;
        s = r;
        k++;
    }
    s -> next = NULL;
    return head;
}

3.2 无头结点

3.2.1 头插法:

typedef struct ListNode{
   
    int val;
    ListNode *next;
}ListNode,*Listline;
 
// 头插法
ListNode* CreateList(int length){
   
    if (length < 1)
        return NULL;
    ListNode *head = (ListNode*)malloc(sizeof(ListNode));
    head -> next = NULL;
    ListNode *s = NULL;
    int k = 1;
    while (k <= length - 1){
   
        s = (ListNode*)malloc(sizeof(ListNode));
        s -> next = head;
        head = s;
        k++
    }
    return head;
}

3.2.2 尾插法:

typedef struct ListNode{
   
    int val;
    ListNode *next;
}ListNode,*Listline;
 
// 尾插法
ListNode* CreateList(int length){
   
    if (length < 1)
        return NULL;
    ListNode *head = (ListNode*)malloc(sizeof(ListNode)); 
    ListNode *s = head, *r = NULL;
    int k = 1;
    while (k <= length - 1){
   
        r = (ListNode*)malloc(sizeof(ListNode));
        s -> next = r;
        s = r;
        k++
    }
    s -> next = NULL;
    return head;
}