文章目录
往期相关文章推荐:线性表之顺序表
1. 概念
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
链表在逻辑上是连续的,物理上则不一定连续(因为每个节点内存由操作系统分配),节点一般从堆内存申请,堆内存空间是按照一定策略分配的,两次申请的空间可能连续,也可能不连续。
2. 链表分类
以下不同情况下组合起来有8种链表结构:
- 单向或双向;
- 带头或不带头;
- 循环或非循环;
不过实际中最常用还是两种结构:
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构来存储数据,如作为哈希桶、图的邻接表的子结构存储数据,另外这种结构在笔试面试中出现很多。
- 带头双向循环链表::结构最复杂,一般用于单独存储数据。实际中使用的链表数据结构,大多都是带头双向循环链表。这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,不同功能的实现反而简单了。
3. 链表与顺序表对比
这里的对比是双向带头循环链表与顺序表对比,这更有意义。这是因为单向不带头非循环链表在实际使用时,并不会单独用来存储数据,所以单向不带头非循环链表对比顺序表的优缺点是意义不大的。
不同角度 | 顺序表 | 链表 |
---|---|---|
存储结构 | 逻辑、物理连续 | 逻辑连续,物理不一定连续 |
随机访问 | 支持,效率高 | 不支持,效率较低 |
插入或删除 | 效率低 | 效率高 |
容量 | 容量不足时需要扩容 | 不需要扩容 |
缓存利用率 | 高 | 低 |
应用场景 | 高效存储和频繁访问 | 频繁插入和删除 |
4. 单向无头非循环链表
4.1 SingleLinkedList.h
#pragma once
/* 单向、无头(无哨兵位)、不循环链表 */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/* -------------
* 链表结构说明。
* -------------
* @datatype:值的数据类型;
* @ListNode:节点;
*/
typedef int datatype;
typedef struct ListNode {
datatype val;
struct ListNode* next;
} ListNode;
// 创建节点
ListNode* CreateNode(datatype val);
/* -----------------------
* 增操作:向链表添加节点。
* -----------------------
* @PushFront:头插;
* @PushBack:尾插。
* @InsertBefore:在指定节点之前添加新节点,需要配合Find函数使用;
* @InsertAfter:在指定节点之后添加新节点,需要配合Find函数使用。
*/
void PushFront(ListNode** pphead, datatype val);
void PushBack(ListNode** pphead, datatype val);
void InsertBefore(ListNode** pphead, ListNode* pos, datatype val);
void InsertAfter(ListNode** pphead, ListNode* pos, datatype val);
/* ---------------------
* 删操作:删除链表节点。
* ---------------------
* @Clear:清空链表中所有节点;
* @PopFront:头删;
* @PopBack:尾删;
* @Erase:删除一个节点,需要配合Find函数使用;
* @EraseBefore:删除指定前一个节点;
* EraseAfter:删除指定后一个节点;
* @Remove:根据值指定删除节点。
*/
void Clear(ListNode** pphead);
void PopFront(ListNode** pphead);
void PopBack(ListNode** pphead);
void Erase(ListNode** pphead, ListNode* pos);
void EraseBefore(ListNode** pphead, ListNode* pos);
void EraseAfter(ListNode** pphead, ListNode* pos);
void Remove(ListNode** pphead, datatype val);
/* ---------------------
* 查操作:查询链表节点。
* ---------------------
* @PrintList:打印所有节点值;
* @Find:根据值查询返回一个节点;
*/
void PrintList(ListNode** pphead);
ListNode* Find(ListNode** pphead, datatype val);
4.2 SingleLinkedList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SingleLinkedList1.h"
// 创建并返回一个节点
ListNode* CreateNode(datatype val) {
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
if (node == NULL) {
perror("CreateNode(val) malloc failed.");
return NULL;
}
node->val = val;
node->next = NULL;
return node;
}
// 1.头插
void PushFront(ListNode** pphead, datatype val) {
assert(pphead);
ListNode* newNode = CreateNode(val);
newNode->next = *pphead;
*pphead = newNode;
}
// 2.尾插
void PushBack(ListNode** pphead, datatype val) {
assert(pphead);
ListNode* newNode = CreateNode(val);
if (*pphead == NULL) {
*pphead = newNode;
}
else { // 找尾
ListNode* tail = *pphead;
while (tail->next) {
tail = tail->next;
}
tail->next = newNode;
tail = newNode;
}
}
// 3.在指定节点前插入新节点
void InsertBefore(ListNode** pphead, ListNode* pos, datatype val) {
assert(pphead);
if(pos == NULL) { // 1.直接尾插
PushBack(pphead, val);
}
else if (*pphead == pos || *pphead == NULL) { // 2.直接头插
PushFront(pphead, val);
}
else { // 3.不用考虑链表为NULL情况,这时pos也为NULL
// 找pos的前一个节点
ListNode* prev = *pphead;
while (prev->next != pos) {
prev = prev->next;
}
ListNode* newNode = CreateNode(val);
newNode->next = prev->next;
prev->next = newNode;
}
}
// 4.在指定节点后插入新节点
void InsertAfter(ListNode** pphead, ListNode* pos, datatype val) {
assert(pphead);
/* 关于assert(pos):
* 1.NULL后面不能插入节点;
* 2.链表为NULL,pos也一定是NULL;
* 3.pos为NULL,链表不一定为NULL。
*/ assert(pos);
ListNode* cur = *pphead;
while (cur != pos) {
cur = cur->next;
}
ListNode* newNode = CreateNode(val);
newNode->next = cur->next;
cur->next = newNode;
}
// 1.清空链表
void Clear(ListNode** pphead) {
assert(pphead);
ListNode* cur = *pphead;
ListNode* del = NULL;
while (cur) {
del = cur;
cur = cur->next;
free(del);
}
*pphead = NULL;
}
// 2.头删
void PopFront(ListNode** pphead) {
assert(pphead && *pphead);
ListNode* del = *pphead;
*pphead = (*pphead)->next;
free(del);
}
// 3.尾删
void PopBack(ListNode** pphead) {
assert(pphead && *pphead);
if ((*pphead)->next == NULL) {
free(*pphead);
*pphead = NULL;
}
else {
ListNode* cur = *pphead;
while (cur->next->next) {
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
}
// 指定删除
void Erase(ListNode** pphead, ListNode* pos) {
assert(pphead);
/* 关于assert(pos):
* 1.不能删除NULL指;
* 2.链表为NULL,pos也一定是NULL;
* 3.pos为NULL,链表不一定为NULL。
*/ assert(pos);
if (pos == *pphead) {
PopFront(pphead);
}
else {
ListNode* prev = *pphead;
while (prev->next != pos) {
prev = prev->next;
}
ListNode* del = prev->next;
prev->next = prev->next->next;
free(del);
}
}
// 删除指定节点前一个
void EraseBefore(ListNode** pphead, ListNode* pos) {
assert(pphead && *pphead);
if (*pphead != pos) {
if ((*pphead)->next == pos) {
free(*pphead);
*pphead = pos;
}
else {
ListNode* prev = *pphead;
while (prev && prev->next && prev->next->next != pos) {
prev = prev->next;
}
free(prev->next);
prev->next = pos;
}
}
}
// 删除指定节点后一个
void EraseAfter(ListNode** pphead, ListNode* pos) {
assert(pphead && *pphead && pos);
if (pos->next) {
ListNode* del = pos->next;
pos->next = pos->next->next;
free(del);
}
}
// 根据值指定删除
void Remove(ListNode** pphead, datatype val) {
assert(pphead && *pphead);
ListNode* cur = *pphead;
ListNode* next = NULL;
while (cur) {
next = cur->next;
if (cur->val == val) {
Erase(pphead, cur);
}
cur = next;
}
}
// 打印链表
void PrintList(ListNode** pphead) {
for (ListNode* cur = *pphead; cur != NULL; cur = cur->next) {
printf("%d->", cur->val);
}
printf("NULL\n");
}
// 查询并返回节点
ListNode* Find(ListNode** pphead, datatype val) {
ListNode* cur = *pphead;
while (cur) {
if (cur->val == val) {
return cur;
}
cur = cur->next;
}
return NULL;
}
5.双向带头循环链表
5.1 DoubleLinkedList.h
#pragma once
/* 双向、带头(哨兵位)、循环链表 */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int datatype;
typedef struct ListNode {
datatype val;
struct ListNode* prev;
struct ListNode* next;
} ListNode;
// 初始化链表
ListNode* InitList();
// 创建节点
ListNode* CreateNode(datatype val);
// 打印节点
void PrintList(ListNode* phead);
// 查找节点
ListNode* Find(ListNode* phead, datatype val);
// 头插
void PushFront(ListNode* phead, datatype val);
// 头删
void PopFront(ListNode* phead);
// 尾插
void PushBack(ListNode* phead, datatype val);
// 尾删
void PopBack(ListNode* phead);
// 在pos节点前插入一个新节点
void Insert(ListNode* pos, datatype val);
// 删除pos节点
void Erase(ListNode* pos);
// 清空链表
void Clear(ListNode* phead);
5.1 DoubleLinkedList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "DoubleLinkedList.h"
// 初始化链表
ListNode* InitList() {
ListNode* phead = CreateNode(-1);
phead->prev = phead;
phead->next = phead;
return phead;
}
// 创建节点
ListNode* CreateNode(datatype val) {
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
if (newNode == NULL) {
perror("CreateNode(val) malloc failed.");
return NULL;
}
newNode->val = val;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
// 打印节点
void PrintList(ListNode* phead) {
if (phead) {
printf("head<->");
for (ListNode* cur = phead->next; cur != phead; cur = cur->next) {
printf("%d<->", cur->val);
}
printf("head\n");
}
else {
printf("null\n");
}
}
// 查找节点
ListNode* Find(ListNode* phead, datatype val) {
for (ListNode* cur = phead->next; cur != phead; cur = cur->next) {
if (cur->val == val) return cur;
}
return NULL;
}
// 头插
void PushFront(ListNode* phead, datatype val) {
//ListNode* newNode = CreateNode(val);
//newNode->next = phead->next;
//phead->next->prev = newNode;
//phead->next = newNode;
//newNode->prev = phead;
Insert(phead->next, val);
}
// 头删
void PopFront(ListNode* phead) {
if (phead->next != phead) {
ListNode* first = phead->next;
phead->next = first->next;
first->next->prev = phead;
free(first);
}
}
// 尾插
void PushBack(ListNode* phead, datatype val) {
//ListNode* newNode = CreateNode(val);
//phead->prev->next = newNode;
//newNode->prev = phead->prev;
//newNode->next = phead;
//phead->prev = newNode;
Insert(phead, val);
}
// 尾删
void PopBack(ListNode* phead) {
if (phead->next != phead) {
ListNode* tail = phead->prev;
phead->prev = tail->prev;
tail->prev->next = phead;
free(tail);
}
}
// 在pos节点前插入一个新节点
void Insert(ListNode* pos, datatype val) {
assert(pos);
ListNode* newNode = CreateNode(val);
newNode->next = pos;
newNode->prev = pos->prev;
pos->prev = newNode;
newNode->prev->next = newNode;
}
// 删除pos节点
void Erase(ListNode* pos) {
assert(pos);
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
}
// 清空链表
void Clear(ListNode* phead) {
ListNode* cur = phead->next;
while (cur != phead) {
ListNode* del = cur;
cur = cur->next;
free(del);
}
free(phead);
}