文章目录
《链表:详解其结构、优势及适用场景》
什么是链表?
链表是一种线性数据结构,由一系列节点(Node)组成,每个节点包含两部分:
-
数据域:存储节点的数据(如整数、字符串等)。
-
指针域:存储下一个节点的地址(或引用),通过指针将节点串联成链状结构。
链表的第一个节点称为头节点(Head),最后一个节点的指针域为null
(表示链表结束)。
链表的结构(以单链表为例)
// C语言单链表节点定义
struct Node {
int data; // 数据域
struct Node* next; // 指针域(指向下一节点的首地址)
};
链表的核心优势
1. 动态扩容,无需预分配内存
-
数组需要预先指定大小(如
int arr[100]
),若数据量超过大小需重新分配内存并复制元素,效率低。 -
链表的节点在运行时动态创建,只需为新节点分配内存并调整指针,无需预先定义容量,内存利用率更高。
2. 插入 / 删除操作高效(时间复杂度 O (1))
-
数组中插入 / 删除元素时,需移动目标位置后的所有元素(如在数组第
i
位插入元素,需移动n-i
个元素),时间复杂度为O(n)。 -
链表中插入 / 删除元素时,只需修改相邻节点的指针,无需移动其他元素,时间复杂度为O(1)(前提是已找到目标节点位置)。
3. 内存利用率高,避免空间浪费
-
数组可能存在空间浪费(如定义了大小为 100 的数组,实际只用了 10 个元素)。
-
链表的节点数量与数据量严格匹配,每新增一个数据才分配一个节点的内存,几乎无空间浪费。
4. 灵活的结构扩展
- 链表可轻松扩展为双向链表(每个节点增加
prev
指针,支持双向遍历)、循环链表(尾节点指向头节点,适合环形结构场景)、链表树 / 图(如二叉树的节点本质是双向链表的扩展)等,适应更多复杂场景。
链表 vs 数组:核心差异总结
特性 | 链表 | 数组 |
---|---|---|
内存分配 | 动态分配,非连续 | 静态分配(或预分配),连续 |
容量 | 无固定容量,动态增长 | 固定容量,超容需扩容 |
访问效率 | 按顺序访问(O (n)) | 随机访问(O (1)) |
插入 / 删除效率 | 已知位置时 O (1) | O (n)(需移动元素) |
内存利用率 | 高(按需分配) | 可能浪费(预分配过剩) |
适合链表的场景
-
频繁进行插入 / 删除操作的场景(如链表式队列、栈)。
-
数据量不确定,需动态扩容的场景(如日志记录、动态列表)。
-
内存碎片化严重,无法分配连续大内存的场景。
例如:操作系统的进程调度链表、Java 的 LinkedList
、Python 的列表(底层部分逻辑类似链表)等。
常见链表封装
有头链表
第一个节点不储存数据,好处:基本不需要考虑传参的问题
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
struct Node{
int data;
struct Node* next;
};
typedef struct Node Node;
//创建节点的函数
struct Node* creatListNode(int data);
//往链表中添加节点
//尾插
void push_back(struct Node* pHead, int data);
//中间插入
//插入成为第pos个节点
void insertByPos(struct Node* pHead, int pos,int insertData);
//遍历
void travel(struct Node* head);
//找链表中数据为 findData的节点并返回其地址 找不到返回NULL
struct Node* findNodeByData(struct Node* pHead, int findData);
//找链表中第pos个节点并返回其地址 找不到返回NULL
struct Node* findNodeByPos(struct Node* pHead, int pos);
//修改
bool modifyNodeByPos(struct Node* pHead, int pos, int modifyData);
bool modifyNodeByData(struct Node* pHead, int findData, int modifyData);
//删除
//删除pHead链表中第pos个节点
bool deleteNodeByPos(struct Node* pHead, int pos);
//删除pHead链表中为delData的数据
bool deleteNodeByData(struct Node* pHead, int delData);
//头插
void push_front(struct Node* pHead, int data);
//遍历链表
void travel(Node* pHead);
int main() {
Node* list = creatListNode(999);
for (int i = 0; i < 10; i++){
push_back(list, i);
travel(list);
}
int n;
Node* p;
while (1) {
/*
printf("请输入要查的数:");
scanf_s("%d", &n);
p=findNodeByPos(list, n);
if (p) {
printf("%d\n", p->data);
}
else {
printf("找不到\n");
}*/
printf("删第几个:");
scanf_s("%d", &n);
//bool isfind = deleteNodeByPos(list, n);
//bool isModify = modifyNodeByPos(list, n, 999);
insertByPos(list, n, 888);
travel(list);
}
return 0;
}
void travel(Node* pHead) {
if (NULL == pHead) {
return;
}
Node* pTemp = pHead->next;
printf("list:");
while (1){
if (pTemp == NULL) break;
printf("%d ", pTemp->data);
pTemp = pTemp->next;
}
printf("\n");
}
//创建节点
Node* creatListNode(int data) {
Node* pNew = (Node*)malloc(sizeof(Node));
assert(pNew);
pNew->data = data;
pNew->next = NULL;
return pNew;
}
void push_back(Node* pHead, int data) {
if (NULL == pHead) return;
Node* p = pHead;
while (1) {
if (p->next == NULL) break;
p = p->next;
}
p->next = creatListNode(data);
}
//找链表中数据为 findData的节点并返回其地址 找不到返回NULL
struct Node* findNodeByData(struct Node* pHead, int findData) {
if (NULL == pHead) return NULL;
Node* p = pHead->next;
while (1){
if (p == NULL) break;
if (p->data == findData) return p;
p = p->next;
}
return NULL;
}
//找链表中第pos个节点并返回其地址 找不到返回NULL
struct Node* findNodeByPos(struct Node* pHead, int pos) {
if (NULL == pHead) return NULL;
if (pos < 1) return NULL;
for (int i = 0; i < pos; i++){
pHead = pHead->next;
if (NULL == pHead) break;
}
return pHead;
}
//删除pHead链表中第pos个节点
bool deleteNodeByPos(struct Node* pHead, int pos) {
if (pos < 1) return false;
Node* p = findNodeByPos(pHead, pos);
if (!p) return false;
if (pos == 1) {
pHead->next = p->next;
free(p);
return true;
}
else {
pHead = findNodeByPos(pHead, pos - 1);
}
pHead->next = p->next;
free(p);
return true;
}
//修改
bool modifyNodeByPos(struct Node* pHead, int pos, int modifyData) {
Node* p = findNodeByPos(pHead, pos);
if (p) {
p->data = modifyData;
return true;
}
return false;
}
bool modifyNodeByData(struct Node* pHead, int findData, int modifyData) {
Node* p = findNodeByData(pHead, findData);
if (p) {
p->data = modifyData;
return true;
}
return false;
}
//插入成为第pos个节点
void insertByPos(struct Node* pHead, int pos, int insertData) {
if (pos < 1) return;
if (NULL == pHead) return;
Node* p = NULL;
if (pos == 1) {
p = pHead;
}
else {
p = findNodeByPos(pHead, pos - 1);
if (p == NULL) {
push_back(pHead,insertData);
return;
}
}
Node* pNode = creatListNode(insertData);
pNode->next = p->next;
p->next = pNode;
}
//删除pHead链表中为delData的数据
bool deleteNodeByData(struct Node* pHead, int delData) {
if (NULL == pHead) return false;
Node* p = findNodeByData(pHead, delData);
if (NULL == p) return false;
Node* pTemp = pHead;
while (1) {
if (pTemp == NULL) break;
if (pTemp->next == p) {
pTemp->next = p->next;
free(p);
return true;
}
pTemp = pTemp->next;
}
return false;
}
//头插
void push_front(struct Node* pHead, int data) {
if (pHead == NULL) return;
Node* p = creatListNode(data);
p->next = pHead->next;
pHead->next = p;
}
无头链表
需要改变第一个节点的时候需要传入二级指针改变指针的指向
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
struct Node {
int data;
struct Node* next;
};
typedef struct Node Node;
//创建节点的函数
struct Node* creatListNode(int data);
//往链表中添加节点
//尾插
void push_back(struct Node** pHead, int data);
//中间插入
//插入成为第pos个节点
void insertByPos(struct Node** pHead, int pos, int insertData);
//遍历
void travel(struct Node* head);
//找链表中数据为 findData的节点并返回其地址 找不到返回NULL
struct Node* findNodeByData(struct Node* pHead, int findData);
//找链表中第pos个节点并返回其地址 找不到返回NULL
struct Node* findNodeByPos(struct Node* pHead, int pos);
//修改
bool modifyNodeByPos(struct Node* pHead, int pos, int modifyData);
bool modifyNodeByData(struct Node* pHead, int findData, int modifyData);
//删除
//删除pHead链表中第pos个节点
bool deleteNodeByPos(struct Node** pHead, int pos);
//删除pHead链表中为delData的数据
bool deleteNodeByData(struct Node** pHead, int delData);
//头插
void push_front(struct Node** pHead, int data);
void travel(Node* pHead) {
if (NULL == pHead) {
return;
}
Node* pTemp = pHead;
printf("list:");
while (1) {
if (pTemp == NULL) break;
printf("%d ", pTemp->data);
pTemp = pTemp->next;
}
printf("\n");
}
int main() {
Node* list= NULL;
push_front(&list, 99);
travel(list);
return 0;
}
//创建节点
Node* creatListNode(int data) {
Node* pNew = (Node*)malloc(sizeof(Node));
assert(pNew);
pNew->data = data;
pNew->next = NULL;
return pNew;
}
void push_back(Node** pHead, int data) {
if (NULL == pHead) return;
if (NULL == *pHead) {
*pHead = creatListNode(data);
return;
}
Node* p = *pHead;
while (1) {
if (p->next == NULL) break;
p = p->next;
}
p->next = creatListNode(data);
}
//找链表中数据为 findData的节点并返回其地址 找不到返回NULL
struct Node* findNodeByData(struct Node* pHead, int findData) {
if (NULL == pHead) return NULL;
Node* p = pHead->next;
while (1) {
if (p == NULL) break;
if (p->data == findData) return p;
p = p->next;
}
return NULL;
}
//找链表中第pos个节点并返回其地址 找不到返回NULL
struct Node* findNodeByPos(struct Node* pHead, int pos) {
if (NULL == pHead) return NULL;
if (pos < 1) return NULL;
for (int i = 0; i < pos-1; i++) {
pHead = pHead->next;
if (NULL == pHead) break;
}
return pHead;
}
//删除pHead链表中第pos个节点
bool deleteNodeByPos(struct Node** pHead, int pos) {
if (pos < 1) return false;
if (*pHead == NULL) return false;
Node* p = findNodeByPos(*pHead, pos);
Node* pTemp = NULL;
if (NULL==p) return false;
if (pos == 1) {
pTemp = *pHead;
*pHead = (*pHead)->next;
free(pTemp);
return true;
}
else {
pTemp = findNodeByPos(*pHead, pos - 1);
}
pTemp->next = p->next;
free(p);
return true;
}
//修改
bool modifyNodeByPos(struct Node* pHead, int pos, int modifyData) {
Node* p = findNodeByPos(pHead, pos);
if (p) {
p->data = modifyData;
return true;
}
return false;
}
bool modifyNodeByData(struct Node* pHead, int findData, int modifyData) {
Node* p = findNodeByData(pHead, findData);
if (p) {
p->data = modifyData;
return true;
}
return false;
}
//插入成为第pos个节点
void insertByPos(struct Node** pHead, int pos, int insertData) {
if (pos < 1) return;
if (NULL == pHead) return;
Node* p = NULL;
Node* pNode = creatListNode(insertData);
if (pos == 1) {
pNode->next = *pHead;
*pHead = pNode;
return;
}
else {
p = findNodeByPos(*pHead, pos - 1);
if (p == NULL) {
push_back(pHead, insertData);
return;
}
}
pNode->next = p->next;
p->next = pNode;
}
//删除pHead链表中为delData的数据
bool deleteNodeByData(struct Node** pHead, int delData) {
if (NULL == pHead) return false;
if (*pHead == NULL) return false;
Node* p = NULL;
if ((*pHead)->data == delData) {
p = *pHead;
*pHead = (*pHead)->next;
free(p);
return true;
}
p = findNodeByData(*pHead, delData);
if (NULL == p) return false;
Node* pTemp = *pHead;
while (1) {
if (pTemp == NULL) break;
if (pTemp->next == p) {
pTemp->next = p->next;
free(p);
return true;
}
pTemp = pTemp->next;
}
return false;
}
//头插
void push_front(struct Node** pHead, int data) {
if (pHead == NULL) return;
Node* p = creatListNode(data);
/*if (NULL == *pHead) {
*pHead = p;
return;
}*/
p->next = *pHead;
*pHead = p;
}
循环单链表
链表最后一个节点的next指针指向链表头结点(无头的)
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
struct Node {
int data;
struct Node* next;
};
typedef struct Node Node;
//创建节点的函数
struct Node* creatListNode(int data);
//尾插
void push_back(struct Node** pHead, int data);
//遍历
void travel(struct Node* head);
//头插
void push_front(struct Node** pHead, int data);
void travel(Node* pHead) {
if (NULL == pHead) {
return;
}
Node* pTemp = pHead;
bool isStar = true;
printf("list:");
while (1) {
if (pTemp == pHead) {
if (isStar) isStar = false;
else break;
}
printf("%d ", pTemp->data);
pTemp = pTemp->next;
}
printf("\n");
}
int main() {
Node* list = NULL;
for (int i = 0; i < 10; i++) {
push_front(&list, i);
travel(list);
}
while (1);
return 0;
}
//创建节点
Node* creatListNode(int data) {
Node* pNew = (Node*)malloc(sizeof(Node));
assert(pNew);
pNew->data = data;
pNew->next = NULL;
return pNew;
}
void push_back(Node** pHead, int data) {
if (NULL == pHead) return;
Node* pNew = creatListNode(data);
if (NULL == *pHead) {
*pHead = pNew;
(*pHead)->next = *pHead;
return;
}
Node* p = *pHead; //找到最后一个节点
while (1) {
if (p->next == *pHead) break;
p = p->next;
}
p->next = pNew;
pNew->next = *pHead;
}
void push_front(struct Node** pHead, int data) {
if (pHead == NULL) return;
Node* pNew = creatListNode(data);
if (*pHead == NULL) {//0-1
*pHead = pNew;
(*pHead)->next = *pHead;
return;
}
Node* pTail = *pHead;
while (1){ //找到最后一个节点
if (pTail->next == *pHead) break;
pTail = pTail->next;
}
//新节点成为头结点
pNew->next = *pHead;
*pHead = pNew;
//最后一个节点的next指针指向新的头结点
pTail->next = *pHead;
}
双链表
- next指针指向下一个节点
- pret指针指向前一个节点
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
struct Node{
int data;
struct Node* pNext;
struct Node* pPret;
};
struct List{
struct Node* pHead; //指向链表的头结点
struct Node* pTail; //指向链表的尾结点
};
void init(struct List* l) {
l->pHead = l->pTail = NULL;
}
struct Node* createListNode(int data);
void travel_back(struct List* list);
void travel_front(struct List* list);
void push_back(struct List* list,int data);
void push_front(struct List* list,int data);
void deleteNodeByPos(struct List* list, int pos);
void deleteNodeByData(struct List* list, int data);
int main() {
struct List* list;
init(&list);
for (int i = 0; i < 10; i++){
push_back(&list, i);
}
travel_back(&list);
travel_front(&list);
int n;
while (1) {
printf("要删第几个:");
scanf_s("%d", &n);
//deleteNodeByPos(&list, n);
deleteNodeByData(&list, n);
travel_back(&list);
travel_front(&list);
}
return 0;
}
struct Node* createListNode(int data) {
struct Node* pNew = malloc(sizeof(struct Node));
assert(pNew);
memset(pNew, 0, sizeof(struct Node));
pNew->data = data;
return pNew;
}
void travel_back(struct List* list) {
if (NULL == list) return;
printf("travel_back:");
struct Node* p = list->pHead;
while (p) {
printf("%d ", p->data);
p = p->pNext;
}
printf("\n");
}
void travel_front(struct List* list) {
if (NULL == list) return;
printf("travel_front:");
struct Node* p = list->pTail;
while (p) {
printf("%d ", p->data);
p = p->pPret;
}
printf("\n");
}
void push_back(struct List* list,int data) {
if (NULL == list) return;
struct Node* pNew = createListNode(data);
if (NULL == list->pTail) {
list->pHead = list->pTail = pNew;
return;
}
list->pTail->pNext = pNew;
pNew->pPret = list->pTail;
list->pTail = pNew;
}
void push_front(struct List* list, int data) {
if (NULL == list) return;
struct Node* pNew = createListNode(data);
if (NULL == list->pHead) {
list->pHead = list->pTail = pNew;
return;
}
list->pHead->pPret = pNew;
pNew->pNext = list->pHead;
list->pHead = pNew;
}
void deleteNodeByPos(struct List* list, int pos) {
if (NULL == list || pos < 1) return;
struct Node* p = list->pHead; //从前往后
int len = 0; //链表长度
while (p) {
len++;
p = p->pNext;
}
if (len < pos) return;
if (1 == pos) {//改头
if (len == 1) {
list->pHead = list->pTail = NULL;
}
else {
list->pHead = list->pHead->pNext;
list->pHead->pPret = NULL;
}
free(p);
return;
}
if (pos == len) {//改尾
p = list->pTail;
list->pTail = list->pTail->pPret;
list->pTail->pNext = NULL;
free(p);
return;
}
p = list->pHead; //重新指向头结点
//移到要删节点
for (int i = 1; i < pos; i++){
p = p->pNext;
}
p->pPret->pNext = p->pNext;
p->pNext->pPret = p->pPret;
free(p);
}
void deleteNodeByData(struct List* list, int data) {
if (NULL == list) return;
struct Node* p = list->pHead;
while (1) {
if (NULL == p) return;
if (data == p->data) {
if (p == list->pHead && p == list->pTail) {
free(p);
list->pHead = list->pTail = NULL;
return;
}
if (p == list->pHead) {//删头
p->pNext->pPret = NULL;
list->pHead = p->pNext;
free(p);
return;//结束
}
if (p == list->pTail) {//删尾
p->pPret->pNext = NULL;
list->pTail = p->pPret;
free(p);
return;
}
//删中间
//pDel的前一个的pNext指针指向pDel的后一个
p->pPret->pNext = p->pNext;
//pDel的后一个的pPret指针指向pDel的前一个
p->pNext->pPret = p->pPret;
free(p);
return;
}
p = p->pNext;
}
}
循环双链表
- 第一个节点的pPret指针指向尾节点
- 最后一个节点的怕Next指针指向头节点
构成循环
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
struct Node {
int data;
struct Node* pNext;
struct Node* pPret;
};
struct List {
struct Node* pHead; //指向链表的头结点
struct Node* pTail; //指向链表的尾结点
};
void init(struct List* l) {
l->pHead = l->pTail = NULL;
}
struct Node* createListNode(int data);
void travel_back(struct List* list);
void travel_front(struct List* list);
void push_back(struct List* list, int data);
void push_front(struct List* list, int data);
void deleteNodeByPos(struct List* list, int pos);
void deleteNodeByData(struct List* list, int data);
int main() {
struct List* list;
init(&list);
for (int i = 0; i < 10; i++) {
push_back(&list, i);
}
travel_back(&list);
travel_front(&list);
int n;
while (1) {
printf("要删第几个:");
scanf_s("%d", &n);
//deleteNodeByPos(&list, n);
deleteNodeByData(&list, n);
travel_back(&list);
travel_front(&list);
}
return 0;
}
struct Node* createListNode(int data) {
struct Node* pNew = malloc(sizeof(struct Node));
assert(pNew);
memset(pNew, 0, sizeof(struct Node));
pNew->data = data;
return pNew;
}
void travel_back(struct List* list) {
if (NULL == list) return;
printf("travel_back:");
struct Node* p = list->pHead;
bool is = true;
while (p) {
if (p == list->pHead) {
if (is) is = false;
else return;
}
printf("%d ", p->data);
p = p->pNext;
}
printf("\n");
}
void travel_front(struct List* list) {
if (NULL == list) return;
printf("travel_front:");
struct Node* p = list->pTail;
bool is = true;
while (p) {
if (p == list->pTail) {
if (is) is=false;
else return;
}
printf("%d ", p->data);
p = p->pPret;
}
printf("\n");
}
void push_back(struct List* list, int data) {
if (NULL == list) return;
struct Node* pNew = createListNode(data);
if (list->pHead == list->pTail) {
list->pHead = list->pTail = pNew;
return;
}
list->pTail->pNext = pNew;
pNew->pPret = list->pTail;
list->pTail = pNew;
list->pTail->pNext = list->pHead;
}
void push_front(struct List* list, int data) {
if (NULL == list) return;
struct Node* pNew = createListNode(data);
if (list->pTail == list->pHead) {
list->pHead = list->pTail = pNew;
return;
}
list->pHead->pPret = pNew;
pNew->pNext = list->pHead;
list->pHead = pNew;
list->pHead->pPret = list->pTail;
}
较简单,不多写
复杂数据类型
只需要在赋值的时候改变就行
- 可以直接memcpy内存拷贝
- 分别赋值
数据是不影响结构的