目录
一.为什么要使用链表存储数据?
二.链表的分类
单向或者双向链表:
带头或者不带头:
循环或者非循环:
三.链表的实现
3.1无头单向非循环链表的实现:
3.1.1单向无头非循环链表的声明
3.1.2动态申请一个节点
3.1.3单链表打印
3.1.4单链表尾插
3.1.5单链表的头插
3.1.6单链表的尾删
3.1.7单链表头删
3.1.8单链表查找
3.1.9单链表在pos位置之前插入x
3.1.10单链表在pos位置之后插入x
3.1.11单链表删除pos之后的值
3.1.12单链表删除pos位置的值
3.1.13销毁单链表
头文件:
测试文件:
3.2带头双向循环链表的实现
3.2.1带头双向循环链表的声明
3.2.2动态申请一个节点
3.2.3哨兵位初始化(创建链表的头结点)
3.2.4带头双向循环链表打印
3.2.5双向链表尾插
3.2.6双向链表头插
3.2.7双线链表尾删
3.2.8双线链表头删
3.2.9双向链表查找
3.2.10双向链表在pos的前面进行插入
3.2.11双向链表删除pos位置的结点
3.2.12双向链表销毁
头文件:
测试文件:
四.链表总结
内存空间是所有程序的公共资源,在一个复杂的系统运行环境下,空闲的内存空间可能散落在内存各处。我们知道,存储数组的内存空间必须是连续的,而当数组非常大时,内存可能无法提供如此大的连续空间。此时链表的灵活性优势就体现出来了。
让我们来看看链表的结构:
虽然链表的种类很多,但我们主要使用的还是无头单向非循环链表(OJ题中最常见的链表)和带头循环双向链表(实践应用)。
对于项目我们需要区分测试文件和接口文件,这样做有利于培养良好的代码能力。
#pragma once
#include
#include
#include
typedef int SLNDataType;
// 声明一个 Single Link List Node (单向无头链表)
typedef struct SLLN
{
//节点值
SLNDataType val;
//指向下一个节点的指针
struct SLLN* next;
}SLNode;
// 动态申请一个节点
SLNode* CreateNode(SLNDataType x)
{
SLNode* newnode = (SLNode*)calloc(1, sizeof(SLNode));
if (newnode == NULL)
{
perror("calloc");
//直接终止程序
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
return newnode;
}
// 单链表打印
void SLNodePrint(SLNode* plist)
{
SLNode* cur = plist;
if (cur != NULL)
{
while (cur)
{
printf("%d->", cur->val);
cur = cur->next;
}
printf("NULL");
printf("\n");
}
else
{
printf("链表为空无需打印\n");
}
}
// 单链表尾插
void SLNodePushBack(SLNode** pplist, SLNDataType x)
{
//先创造一个新节点
SLNode* newnode = CreateNode(x);
SLNode* tail = *pplist;//将plist赋值给tail
//找尾
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
// 单链表的头插
void SLNodePushFront(SLNode** pplist, SLNDataType x)
{
//先创建一个新节点
SLNode* newnode = CreateNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
// 单链表的尾删
void SLNodePopBack(SLNode** pplist)
{
//如果链表为空则不能删除,报错
assert(*pplist);
//找尾
SLNode* tail = *pplist;
//tail移动时我们还需要有一个前驱指针 prev 跟在tail后面
SLNode* prev = NULL;
//单链表只有一个节点情况下尾删
if (tail->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
//单链表有多个节点情况下尾删
else
{
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
}
}
// 单链表头删
void SLNodePopFront(SLNode** pplist)
{
//当链表为空时不能删除,报错
assert(*pplist);
SLNode* cur = *pplist;
SLNode* newplist = (*pplist)->next;
free(cur);
cur = NULL;
*pplist = newplist;
}
// 单链表查找(配合在pos位置插入或者删除使用)
SLNode* SLNodeFind(SLNode* plist, SLNDataType x)
{
while (plist != NULL)
{
if (plist->val == x)
{
return plist;
}
else
{
plist = plist->next;
}
}
return NULL;
}
//单链表在pos位置之前插入x
void SLNodeInsertBefore(SLNode** pplist, SLNode* pos, SLNDataType x)//此处传入二级指针pplist是为了在头插时改变plist的值,传址调用
{
//此处需要对哪个指针进行断言检查呢?
assert(pos && *pplist);//防止人为乱传空
SLNode* cur = *pplist;
SLNode* prev = NULL;//前驱指针prev保存cur前一个节点的地址
if (pos == *pplist)
{
//在头节点位置前插入x实质上就是头插,我们调用之前写的头插函数即可
SLNodePushFront(pplist, x);
}
else
{
SLNode* newnode = CreateNode(x);
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
prev->next = newnode;
newnode->next = cur;
}
}
// 单链表在pos位置之后插入x
void SLNodeInsertAfter(SLNode* pos, SLNDataType x)
{
//链表为空无法在pos位置之后插入x
assert(pos);
SLNode* newnode = CreateNode(x);
if (newnode == NULL)
{
perror(calloc);
return;
}
SLNode* next = pos->next;
pos->next = newnode;
newnode->next = next;
}
//单链表删除pos之后的值
void SLNodeEraseAfter(SLNode* pos)
{
//链表为空无法删除
assert(pos);
//当链表只剩下一个节点或者pos后面无节点时也无法删除
assert(pos->next);
SLNode* del = pos->next;
pos->next = pos->next->next;
free(del);
del == NULL;
}
//单链表删除pos位置的值
void SLNodeErasepos(SLNode** pplist, SLNode* pos)
{
//链表为空不能删除
assert(*pplist);
SLNode* cur = *pplist;
SLNode* prev = NULL;//前驱指针prev保存cur前一个节点的地址
if (pos != *pplist)
{
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
SLNode* next = cur->next;
free(cur);
cur = NULL;
//链接两个节点
prev->next = next;
}
else
{
SLNode* next = cur->next;
free(cur);
cur = NULL;
//更新头节点
*pplist = next;
}
}
//销毁链表
void SLNodeDestory(SLNode** pplist)
{
assert(pplist);
assert(*pplist);
SLNode* cur = *pplist;
while (cur)
{
SLNode* next = cur->next;
free(cur);
cur = next;
}
*pplist = NULL;
}
以上代码为单链表所实现的所有功能(接口)
#pragma once
#include
#include
#include
typedef int SLNDataType;
// 声明一个 Single Link List Node (单向无头链表)
typedef struct SLLN
{
//节点值
SLNDataType val;
//指向下一个节点的指针
struct SLLN* next;
}SLNode;
// 动态申请一个节点
SLNode* CreateNode(SLNDataType x);
// 单链表打印
void SLNodePrint(SLNode* plist);
// 单链表尾插
void SLNodePushBack(SLNode** pplist, SLNDataType x);
// 单链表的头插
void SLNodePushFront(SLNode** pplist, SLNDataType x);
// 单链表的尾删
void SLNodePopBack(SLNode** pplist);
// 单链表头删
void SLNodePopFront(SLNode** pplist);
// 单链表查找
SLNode* SLNodeFind(SLNode* plist, SLNDataType x);
//单链表在pos位置之前插入x
void SLNodeInsertBefore(SLNode** pplist, SLNode* pos, SLNDataType x);
//单链表在pos位置之后插入x
void SLNodeInsertAfter(SLNode* pos, SLNDataType x);
//单链表删除pos位置之后的值
void SLNodeEraseAfter(SLNode* pos);
//单链表删除pos位置的值
void SLNodeErasepos(SLNode** pplist, SLNode* pos);
//销毁单链表
void SLNodeDestory(SLNode** pplist);
#define _CRT_SECURE_NO_WARNINGS 1
#include"Single_linked_lists.h"
//单链表尾插测试
void text1()
{
SLNode* plist = NULL;
SLNodePushBack(&plist, 1);
SLNodePushBack(&plist, 2);
SLNodePushBack(&plist, 3);
SLNodePushBack(&plist, 4);
SLNodePrint(plist);
}
//单向链表头插测试
void text2()
{
SLNode* plist = NULL;
SLNodePushFront(&plist, 1);
SLNodePushFront(&plist, 2);
SLNodePushFront(&plist, 3);
SLNodePushFront(&plist, 4);
SLNodePrint(plist);
}
//单向链表尾删测试
void text3()
{
//SLNode* plist = NULL;
//SLNodePushBack(&plist, 1);
//SLNodePushBack(&plist, 2);
//SLNodePushBack(&plist, 3);
//SLNodePushBack(&plist, 4);
多节点尾删测试
//SLNodePopBack(&plist);
SLNode* plist = NULL;
SLNodePushBack(&plist, 1);
//单节点尾删测试
SLNodePopBack(&plist);
SLNodePrint(plist);
}
//单向链表头删测试
void text4()
{
SLNode* plist = NULL;
SLNodePushBack(&plist, 1);
SLNodePushBack(&plist, 2);
SLNodePushBack(&plist, 3);
SLNodePushBack(&plist, 4);
//头删测试
SLNodePopFront(&plist);
SLNodePrint(plist);
}
//单向链表查找 val 测试
void text5()
{
SLNode* plist = NULL;
SLNodePushBack(&plist, 1);
SLNodePushBack(&plist, 2);
SLNodePushBack(&plist, 3);
SLNodePushBack(&plist, 4);
//查找val测试
SLNode* pos = SLNodeFind(plist, 4);
printf("%p\n", pos);
}
// 单链表在pos位置之后插入 x 测试
void text6()
{
SLNode* plist = NULL;
SLNodePushBack(&plist, 1);
SLNodePushBack(&plist, 2);
SLNodePushBack(&plist, 3);
SLNodePushBack(&plist, 4);
//pos之后插入x
SLNode* pos = SLNodeFind(plist, 3);
SLNodeInsertAfter(pos, 0);
SLNodePrint(plist);
}
//单链表删除pos之后的值测试
void text7()
{
SLNode* plist = NULL;
SLNodePushBack(&plist, 1);
SLNodePushBack(&plist, 2);
SLNodePushBack(&plist, 3);
SLNodePushBack(&plist, 4);
//单链表删除pos之后的值测试
SLNode* pos = SLNodeFind(plist, 3);
SLNodeEraseAfter(pos);
SLNodePrint(plist);
}
//单链表在pos前一个位置插入 x 测试
void text8()
{
SLNode* plist = NULL;
SLNodePushBack(&plist, 1);
SLNodePushBack(&plist, 2);
SLNodePushBack(&plist, 3);
SLNodePushBack(&plist, 4);
SLNodePrint(plist);
SLNode* posbefore = SLNodeFind(plist, 3);
//单链表在pos前一个位置插入 x 测试
SLNodeInsertBefore(&plist, posbefore, 0);
SLNodePrint(plist);
}
//单链表删除pos位置的值
void text9()
{
SLNode* plist = NULL;
SLNodePushBack(&plist, 1);
SLNodePushBack(&plist, 2);
SLNodePushBack(&plist, 3);
SLNodePushBack(&plist, 4);
SLNodePrint(plist);
//单链表删除pos位置的值
SLNode* pos1 = SLNodeFind(plist, 1);
SLNode* pos2 = SLNodeFind(plist, 3);
SLNodeErasepos(&plist, pos1);
SLNodeErasepos(&plist, pos2);
SLNodePrint(plist);
}
int main()
{
//text1();//单链表尾插测试
//text2();//单向链表头插测试
//text3();//单向链表尾删测试
//text4();//单向链表头删测试
//text5();//单向链表查找 val 测试
//text6();// 单链表在pos位置之后插入 x 测试
//text7();//单链表删除pos之后的值测试
//text8();//单链表在pos前一个位置插入 x 测试
//text9();//单链表删除pos位置的值测试
return 0;
}
#pragma once
#include
#include
#include
typedef int DataType;
typedef struct ListNode
{
DataType val;
struct LTNode* next;
struct LTNode* prev;
}LTNode;
//Create one newnode
LTNode* Createnode(DataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
//哨兵位初始化(创建链表的头结点)
LTNode* LTInit()
{
LTNode* plist = Createnode(-1);
plist->next = plist;
plist->prev = plist;
return plist;
}
//带头双向循环链表打印
void LTPrint(LTNode* plist)
{
assert(plist);
LTNode* cur = plist->next;
printf("哨兵位<=>");
while (cur != plist)
{
printf("%d<=>", cur->val);
cur = cur->next;
}
printf("\n");
}
// 双向链表尾插
void LTNodePushBack(LTNode* plist, DataType x)
{
LTNode* newnode = Createnode(x);
LTNode* tail = plist->prev;
tail->next = newnode;
newnode->prev = tail;
plist->prev = newnode;
newnode->next = plist;
}
// 双向链表头插
void LTNodePushFront(LTNode* plist, DataType x)
{
assert(plist);
LTNode* first = plist->next;
LTNode* newnode = Createnode(x);
newnode->next = first;
first->prev = newnode;
plist->next = newnode;
newnode->prev = plist;
}
// 双向链表尾删
void LTNodePopBack(LTNode* plist)
{
//防止链表不存在
assert(plist);
//防止链表为空
assert(plist->next);
LTNode* tail = plist->prev;
plist->prev = tail->prev;
LTNode* tailprev = tail->prev;
tailprev->next = plist;
}
// 双向链表头删
void LTNodePopFront(LTNode* plist)
{
//防止链表不存在
assert(plist);
//防止链表为空
assert(plist->next);
LTNode* first = plist->next;
LTNode* second = first->next;
plist->next = second;
second->prev = plist;
free(first);
first = NULL;
}
// 双向链表查找
LTNode* LTNodeFind(LTNode* plist, DataType x)
{
//防止链表不存在
assert(plist);
//防止链表为空
assert(plist->next);
LTNode* cur = plist->next;
while (cur != plist)
{
if (cur->val == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// 双向链表在pos的前面进行插入
void LTNodeInsert(LTNode* pos, DataType x)
{
assert(pos);
LTNode* newnode = Createnode(x);
LTNode* posprev = pos->prev;
posprev->next = newnode;
newnode->next = pos;
pos->prev = newnode;
newnode->prev = posprev;
}
// 双向链表删除pos位置的结点
void LTNodeErase(LTNode* pos)
{
//防止传空
assert(pos);
LTNode* posnext = pos->next;
LTNode* posprev = pos->prev;
posprev->next = posnext;
posnext->prev = posprev;
free(pos);
}
// 双向链表销毁
void LTNodeDestory(LTNode* plist)
{
//防止链表不存在
assert(plist);
LTNode* cur = plist->next;
while (cur != plist)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(plist);
plist = NULL;
}
以上代码为单链表所实现的所有功能(接口)
#pragma once
#include
#include
#include
typedef int DataType;
typedef struct ListNode
{
DataType val;
struct LTNode* next;
struct LTNode* prev;
}LTNode;
//生成新节点
LTNode* Createnode(DataType x);
//哨兵位初始化
LTNode* LTInit();
//带头双向循环链表打印
void LTPrint(LTNode* plist);
// 双向链表销毁
void LTNodeDestory(LTNode* plist);
// 双向链表尾插
void LTNodePushBack(LTNode* plist, DataType x);
// 双向链表尾删
void LTNodePopBack(LTNode* plist);
// 双向链表头插
void LTNodePushFront(LTNode* plist, DataType x);
// 双向链表头删
void LTNodePopFront(LTNode* plist);
// 双向链表查找
LTNode* LTNodeFind(LTNode* plist, DataType x);
// 双向链表在pos的前面进行插入
void LTNodeInsert(LTNode* pos, DataType x);
// 双向链表删除pos位置的结点
void LTNodeErase(LTNode* pos);
#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
// 初步测试
void test1()
{
// 创建一个哨兵位头节点
LTNode* plist = LTInit();
LTPrint(plist);
}
// 尾插尾删测试
void test2()
{
LTNode* plist = LTInit();
LTNodePushBack(plist, 1);
LTNodePushBack(plist, 2);
LTNodePushBack(plist, 3);
LTNodePushBack(plist, 4);
LTNodePushBack(plist, 5);
LTPrint(plist);
LTNodePopBack(plist);
LTPrint(plist);
}
// 头插头删测试
void test3()
{
LTNode* plist = LTInit();
LTNodePushFront(plist, 1);
LTNodePushFront(plist, 2);
LTNodePushFront(plist, 3);
LTNodePushFront(plist, 4);
LTNodePushFront(plist, 5);
LTPrint(plist);
LTNodePopFront(plist);
LTPrint(plist);
}
// 链表查找测试
void test4()
{
LTNode* plist = LTInit();
LTNodePushBack(plist, 1);
LTNodePushBack(plist, 2);
LTNodePushBack(plist, 3);
LTNodePushBack(plist, 4);
LTNodePushBack(plist, 5);
LTPrint(plist);
LTNode* pos = LTNodeFind(plist, 3);
printf("%d\n", pos->val);
}
// 双向链表在pos的前面进行插入测试
void test5()
{
LTNode* plist = LTInit();
LTNodePushBack(plist, 1);
LTNodePushBack(plist, 2);
LTNodePushBack(plist, 3);
LTNodePushBack(plist, 4);
LTNodePushBack(plist, 5);
LTPrint(plist);
LTNode* pos = LTNodeFind(plist, 3);
LTNodeInsert(pos, 100);
LTNodeInsert(pos, 200);
LTPrint(plist);
}
// 双向链表删除pos位置的结点测试
void test6()
{
LTNode* plist = LTInit();
LTNodePushBack(plist, 1);
LTNodePushBack(plist, 2);
LTNodePushBack(plist, 3);
LTNodePushBack(plist, 4);
LTNodePushBack(plist, 5);
LTPrint(plist);
LTNode* pos = LTNodeFind(plist, 3);
LTNodeErase(pos);
LTPrint(plist);
}
// 链表销毁测试
void test7()
{
LTNode* plist = LTInit();
LTNodePushBack(plist, 1);
LTNodePushBack(plist, 2);
LTNodePushBack(plist, 3);
LTNodePushBack(plist, 4);
LTNodePushBack(plist, 5);
LTNodeDestory(plist);
plist = NULL;
LTPrint(plist);
}
int main()
{
// 初步测试
test1();
// 尾插尾删测试
test2();
// 头插头删测试
test3();
// 链表查找测试
test4();
// 双向链表在pos的前面进行插入测试
test5();
// 双向链表删除pos位置的结点测试
test6();
// 链表销毁测试
//test7();
return 0;
}
这是一个我个人做的思维导图,对于学习链表的一些总结,希望对你有所帮助: