1. 单向链表介绍
- 链表的定义:是一种物理存储结构上非连续、非顺序的存储结构,数据元素(结点)的逻辑顺序是通过链表中的指针链接次序实现的。
注:结点包含两个域:数据域和指针域; - 链表的特点:
- 存储单元可以是可连续的,也可以是不连续的;
- 作插入和删除操作时,不需要移动元素,仅需修改指针;
- 不可随机存取;
- 单向链表:链表的每一个结点中只包含一个指针域,如下图所示:
其中,单向链表的第一个结点之前附设的结点,称为头结点head;链表的结点位置从0开始,例如第一个结点在链表中所对应的位置pos=0.
2. 单向链表的基本操作(C/C++实现)
2.1 初始化
由以上图示可知,单向链表可由头指针唯一确定,在C语言中可用结构指针分别对单向链表结点和单向链表进行描述。
2.1.1 单向链表结点存储结构
typedef struct LINKNODE {
void* data; //void* :无类型指针,可以指向任何类型数据
struct LINKNODE* next;
}LinkNode;
void*为无类型指针,其他任何类型都可以直接赋值给它,无需进行强转,但是反过来不可以。
2.1.2 单向链表的存储结构体
typedef struct LINKLIST {
LinkNode* head; //头结点
int size;
//没有容量的概念
}LinkList;
分别构造单向链表的结点和存储结构类型,首先申请内存空间创建链表,然后根据结点的数据结构类型进行头结点的创建,此时即可创建出唯一确定的单向链表。初始化链表代码如下所示:
LinkList* Init_LinkList() {
//申请内存
LinkList* list = (LinkList*)malloc(sizeof(LinkList));
list->size = 0;
//创建头结点,不保存数据信息
list->head = (LinkNode*)malloc(sizeof(LinkNode)); //申请头结点内存
list->head->data = NULL;
list->head->next = NULL;
return list;
}
其中,malloc()函数分配一块连续的内存,释放内存时利用free()函数进行释放;sizeof(LinkList)为malloc分配的内存大小,该处malloc()函数返回值为LinkList*。
2.2 单向链表的插入
与线性表和顺序存储结构不同,链表在进行插入和删除元素时,不需要移动大量元素,仅需修改指针即可实现。单向链表的插入操作可分为两步进行:1.创建辅助指针变量pCurrent,表示插入位置的前一个结点;2.新结点入链表。插入链表示意图如下:
pNext为新节点需要结点插入的位置,因此需要找到pNext结点的前一个结点,故创建辅助指针变量pCurrent表示pNext结点的前一个结点;newnode结点为需要插入元素的新节点。找到pCurrent后,只需要newnode->next = pCurrent->next;
(令newnode指针指向下一个结点pNext),同时pCurrent->next = newnode;
当前结点指针指向newnode结点,即可完成单向链表按位置插入操作。
具体实现代码如下:
//指定位置插入结点
void Insert_LinkList(LinkList* list, int pos,void* data) {
if (list == NULL) {
return ;
}
if (data == NULL) {
return;
}
//友好的处理,pos越界
if (pos<0||pos>list->size) {
pos = list->size;
}
//创建新的节点,将数据赋值给结点
LinkNode* newnode = (LinkNode*)malloc(sizeof(LinkNode));
newnode->data = data;
newnode->next = NULL;
//按指定位置插入
//1.创建辅助指针变量,找到插入位置前一个结点
LinkNode* pCurrent = list->head;
for (int i = 0; i < pos;i++) {
pCurrent = pCurrent->next;
}
//2.新节点入链表
newnode->next = pCurrent->next;
pCurrent->next = newnode;
list->size++;
}
其中,LinkNode* pCurrent = list->head;
为辅助指针变量,旨在找到插入位置的前一个结点;LinkNode* newnode = (LinkNode*)malloc(sizeof(LinkNode));
为存储待入链表数据的结点。
2.3 单向链表的删除
如下图所示,在单向链表中删除元素pDel,仅需找到pDel的前一个结点pCurrent并修改结点pCurrent的指针域,使pCurrent指向下一个结点即可。修改指针语句为:pCurrent->next = pDel->next;
具体代码实现如下:
//删除指定位置的值
void RemoveByPos_LinkList(LinkList* list, int pos) {
if (list == NULL) {
return ;
}
if (pos < 0 || pos >= list->size) {
return;
}
//创建辅助指针变量找到删除的前一个结点
LinkNode* pCurrent = list->head;
for (int i = 0; i < pos;i++) {
pCurrent = pCurrent->next;
}
//缓存删除的结点
LinkNode* pDel = pCurrent->next;
pCurrent->next = pDel->next;
//释放删除结点的内存
free(pDel);
list->size--;
}
2.4 单向链表的元素查找
查找链表中是否存在元素data,仅需对链表进行遍历即可。1.创建辅助指针pCurrent对链表遍历;2.遍历查找,直到找到第i个元素data,返回i,或者pCurrent指向空,遍历结束。
//查找
int Find_LinkList(LinkList* list, void* data) {
if (list == NULL) {
return -1;
}
if (data == NULL) {
return -1;
}
//遍历查找
LinkNode* pCurrent = list->head->next; //pCurrent指向链表的第一个结点
int i = 0;
while (pCurrent != NULL) {
if (pCurrent->data == data) {
break;
}
i++;
pCurrent = pCurrent->next;
}
return i;
}
2.4 单向链表的内存释放
//释放链表内存
void FreeSpace_LinkList(LinkList* list) {
if (list == NULL) {
return;
}
//1.创建辅助指针变量
LinkNode* pCurrent = list->head;
while (pCurrent != NULL) {
//每次均创建当前结点的下一个结点进行缓存
LinkNode* pNext = pCurrent->next;
free(pCurrent);
pCurrent = pNext;
}
//释放链表内存
list->size = 0;
free(list);
}
以上仅为本人学习过程中的笔记记录,有错误和不足之处希望大家指出~