(关于哨兵位结点)
哨兵位结点也叫哑节点。哨兵位结点也是头结点 。该节点不存储有效数据,只是为了方便操作 (如尾插时用带哨兵位的头结点很爽,不需要判空)。
有哨兵位结点的链表,第一个元素应该是链表第二个节点(head -> next,head为哨兵位结点)对应的元素。
有哨兵位结点的链表永不为空 (因为至少有一个结点——哨兵位结点),这样可以避免判断头是否为空,起到简化代码、减少出错的作用。
一、不带哨兵位单链表结点的创建
🚩
下面的自定义类型、函数名里SLT:
来源于单链表的英文:Single Linked List
1.1 typedef 链表的数据类型
typedef 一下链表数据域的数据类型,目的 是如果以后需要改变链表数据类型直接在typedef后改一下即可,否则要在程序中一个个的改,麻烦并且易出错
typedef int SLTDataType;
1.2 结点的结构体创建
凡是有多个数据的 → 创建结构体。
数据域: 存储的数据data,类型是SLTDataType。
指针域: 存下一个结点的地址next,类型是结构体指针 struct SListNode*。
typedef struct SListNode //line1 { //line2 SLTDataType data;//数据域 //line3 struct SListNode* next;//指针域 //line4 }SLTNode; //line5
🔺 注意:指针域的结构体指针不可以是SLTNode*
编译器的查找规则:编译的时候,如果要用到一个函数或者一个类型,它不会向下查找,只能向上查找。具体来说,SLTNode*在第五行以后才起作用,在第四行的时候还没有定义“SLTNode* ”
二、单链表要实现的功能
1、打印链表:将链表各个结点数据域中的数据按顺序打印出来
2、创建一个新结点:插入一个结点的时候要创建一个新结点,干脆封装成一个函数,后面直接调用即可
3、在链表尾部插入一个数据(尾插)
4、删除链表尾部的结点(尾删)
5、在链表头部插入一个数据(头插)
6、删除链表头部的结点(头删)
7、查找某个结点:返回结点地址
8、删除某个结点
9、单链表中插入结点
10、销毁链表
三、需要包含的头文件
#include<stdio.h> #include<stdlib.h> #include<assert.h>
四、函数接口一览
//打印 单链表 void SLTPrint(SLTNode* phead); //动态申请一个节点 SLTNode* BuySLTNode(SLTDataType x); //尾插(并给节点中的data赋值) void SLTPushBack(SLTNode** pphead, SLTDataType x); //尾删 void SLTPopBack(SLTNode** pphead); //头插(并给节点中的data赋值) void SLTPushFront(SLTNode** pphead, SLTDataType x); //头删 void SLTPopFront(SLTNode** pphead); //查找并返回结点地址 SLTNode* SLFind(SLTNode* phead, SLDataType x); //删除某个结点 void SLErase(SLTNode** pphead, SLTNode* pos); //pos前 插入结点 void SLInsert(SLTNode** pphead, SLTNode* pos, SLDataType x); //销毁 void SLDestroy(SLTNode** pphead);
为什么有些函数参数传递的是二级指针,有些是一级指针?
因为有些函数需要改变传入的结点 。
phead可能为空:链表一开始为空(main()函数中定义SLTNode* phead = NULL),对于插入类的函数,第一次插入时phead为空,那么就要改变phead指向的空间(要在函数中创建一个新结点,phead改变为该结点的地址),即需要改变phead,而phead是一级指针,因为要改变指针需要传递指针的指针——二级指针,即传递指针变量phead的地址——&phead。
但是像打印这样的函数,不需要改变phead,只需要遍历一遍链表,打印出各结点的数据即可,所以传phead(一级指针)就好,不需要二级指针。