《链表:结构、优势、类型及操作实现》


《链表:详解其结构、优势及适用场景》

什么是链表?

链表是一种线性数据结构,由一系列节点(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内存拷贝
  • 分别赋值

数据是不影响结构的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值