数据结构 | C语言链表讲解(新手入门)
什么是链表
链表(Linked List)是一种在计算机科学中常见的数据结构,用于存储和组织数据。链表由
节点
(Node)组成,每个节点包含数据和一个指向下一个节点的指针。链表不像数组那样在内存中是一块连续的存储空间,而是通过指针将节点分散存储在内存中。
链表的特性:
动态分配内存
: 链表的节点在运行时动态分配内存,允许灵活地增加或删除节点,而不受固定大小的限制。
非连续存储
: 节点可以在内存中分散存储,不需要一块连续的存储空间。
插入和删除高效
: 插入和删除节点的操作相对高效,因为只需调整指针,而不需要移动大量数据。
链表的实现
链表的定义
//定义链表
typedef struct SListNode
{
SLNDataType val;
struct SListNode* next;
}SLNode;
打印链表
void SLTPrint(SLNode* phead)
{
SLNode* cur = phead;
while (cur)
{
printf("%d->", cur->val);
cur = cur->next;
}
printf("NULL\n");
}
节点的创建
SLNode* CreateNode(SLNDataType x)
{
SLNode* node = (SLNode*)malloc(sizeof(SLNode));
if (node == NULL)
{
perror("malloc");
return NULL;
}
node->val = x;
node->next = NULL;
return node;
}
链表尾插
void SLTPushBack(SLNode** pphead,SLNDataType x)
{
//判断不能为空
assert(pphead);
//创建新的节点
SLNode* node = CreateNode(x);
//分两种情况
//若链表为空,则吧创建的节点当作头节点
if (*pphead == NULL)
{
*pphead = node;
return 1;
}
//若链表不为空,则遍历找到尾节点,进行尾插
SLNode* pcur = *pphead;
while (pcur->next != NULL)
{
pcur = pcur->next;
}
pcur->next = node;
}
链表头插
void SLTPushFront(SLNode** pphead, SLNDataType x)
{
//判断不能为空
assert(pphead);
//创建节点
SLNode* node = CreateNode(x);
//分两种情况
//若链表为空,则直接插,设置为头
if (*pphead == NULL)
{
*pphead = node;
}
else
{
//若不为空
node->next = *pphead;
*pphead = node;
}
}
链表尾删
void SLTPopBack(SLNode** pphead)
{
assert(pphead && *pphead);
//只有1个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
//多个节点
else
{
SLNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
链表头删
void SLTPopFront(SLNode** pphead)
{
assert(pphead && *pphead);
SLNode* del = *pphead;
(*pphead) = (*pphead)->next;
free(del);
del = NULL;
}
节点的查找
//查找数据
SLNode* SLTFind(SLNode** pphead, SLNDataType x)
{
assert(pphead);
SLNode* pcur = *pphead;
while (pcur)
{
if (pcur->val == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
节点的插入
//指定位置之前插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{
assert(pphead && *pphead && pos);
//创建节点
SLNode* n = CreateNode(x);
//pos为第一个节点(只有一个节点)
if (pos == (*pphead))
{
n->next = *pphead;
*pphead = n;
return;
}
//pos不为第一个节点
//找pos前一个节点
SLNode* prev = *pphead;
while (prev && prev->next != pos)
{
prev = prev->next;
}
if (prev)
{
n->next = pos;
prev->next = n;
}
else
{
SLTPushBack(&pphead, x);
}
}
节点后的插入
//指定位置之后插入
void SLTInsertAfter(SLNode* pos, SLNDataType x)
{
assert(pos);
//创建空间
SLNode* n = CreateNode(x);
n->next = pos->next;
pos->next = n;
}
删除节点
//删除pos节点
void SLTEraase(SLNode** pphead, SLNode* pos)
{
assert(pphead && pos && *pphead);
//pos是第一个节点
if (pos == (*pphead))
{
*pphead = (*pphead)->next;
free(pos);
pos = NULL;
return;
}
//pos不是第一个节点
SLNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
删除后节点
//删除pos之后的节点
void SLTEraseAfter(SLNode* pos)
{
//尾节点不可以,空指针也不行
assert(pos && pos->next);
SLNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
销毁
//销毁
void SLTDesTroy(SLNode** pphead)
{
assert(pphead);
SLNode* pcur = *pphead;
while (pcur)
//注意:如果是pcur->next,那么循环将结束于尾节点没有free的时候
{
SLNode* next = pcur->next;
free(pcur);
pcur = next;
}
*pphead = NULL;
}
代码
SList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
void SLTPrint(SLNode* phead)
{
SLNode* cur = phead;
while (cur)
{
printf("%d->", cur->val);
cur = cur->next;
}
printf("NULL\n");
}
SLNode* CreateNode(SLNDataType x)
{
SLNode* node = (SLNode*)malloc(sizeof(SLNode));
if (node == NULL)
{
perror("malloc");
return NULL;
}
node->val = x;
node->next = NULL;
return node;
}
void SLTPushBack(SLNode** pphead,SLNDataType x)
{
//判断不能为空
assert(pphead);
//创建新的节点
SLNode* node = CreateNode(x);
//分两种情况
//若链表为空,则吧创建的节点当作头节点
if (*pphead == NULL)
{
*pphead = node;
return 1;
}
//若链表不为空,则遍历找到尾节点,进行尾插
SLNode* pcur = *pphead;
while (pcur->next != NULL)
{
pcur = pcur->next;
}
pcur->next = node;
}
void SLTPushFront(SLNode** pphead, SLNDataType x)
{
//判断不能为空
assert(pphead);
//创建节点
SLNode* node = CreateNode(x);
//分两种情况
//若链表为空,则直接插,设置为头
if (*pphead == NULL)
{
*pphead = node;
}
else
{
//若不为空
node->next = *pphead;
*pphead = node;
}
}
void SLTPopBack(SLNode** pphead)
{
assert(pphead && *pphead);
//只有1个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
//多个节点
else
{
SLNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SLTPopFront(SLNode** pphead)
{
assert(pphead && *pphead);
SLNode* del = *pphead;
(*pphead) = (*pphead)->next;
free(del);
del = NULL;
}
//查找数据
SLNode* SLTFind(SLNode** pphead, SLNDataType x)
{
assert(pphead);
SLNode* pcur = *pphead;
while (pcur)
{
if (pcur->val == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
//指定位置之前插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{
assert(pphead && *pphead && pos);
//创建节点
SLNode* n = CreateNode(x);
//pos为第一个节点(只有一个节点)
if (pos == (*pphead))
{
n->next = *pphead;
*pphead = n;
return;
}
//pos不为第一个节点
//找pos前一个节点
SLNode* prev = *pphead;
while (prev && prev->next != pos)
{
prev = prev->next;
}
if (prev)
{
n->next = pos;
prev->next = n;
}
else
{
SLTPushBack(&pphead, x);
}
}
//指定位置之后插入
void SLTInsertAfter(SLNode* pos, SLNDataType x)
{
assert(pos);
//创建空间
SLNode* n = CreateNode(x);
n->next = pos->next;
pos->next = n;
}
//删除pos节点
void SLTEraase(SLNode** pphead, SLNode* pos)
{
assert(pphead && pos && *pphead);
//pos是第一个节点
if (pos == (*pphead))
{
*pphead = (*pphead)->next;
free(pos);
pos = NULL;
return;
}
//pos不是第一个节点
SLNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
//删除pos之后的节点
void SLTEraseAfter(SLNode* pos)
{
//尾节点不可以,空指针也不行
assert(pos && pos->next);
SLNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
//销毁
void SLTDesTroy(SLNode** pphead)
{
assert(pphead);
SLNode* pcur = *pphead;
while (pcur)
//注意:如果是pcur->next,那么循环将结束于尾节点没有free的时候
{
SLNode* next = pcur->next;
free(pcur);
pcur = next;
}
*pphead = NULL;
}
SList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLNDataType;
//定义链表
typedef struct SListNode
{
SLNDataType val;
struct SListNode* next;
}SLNode;
//打印链表
void SLTPrint(SLNode* phead);
//链表尾插
void SLTPushBack(SLNode** pphead, SLNDataType x);
//链表头插
void SLTPushFront(SLNode** pphead, SLNDataType x);
//链表尾删
void SLTPopBack(SLNode** pphead);
//链表头删
void SLTPopFront(SLNode** pphead);
//链表查找
SLNode* SLTFind(SLNode** pphead, SLNDataType x);
//指定位置之前插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);
//指定位置之后插入
void SLTInsertAfter(SLNode* pos, SLNDataType x);
//删除pos节点
void SLTEraase(SLNode** pphead, SLNode* pos);
//删除pos之后的节点
void SLTEraseAfter(SLNode* pos);
//销毁
void SLTDesTroy(SLNode** pphead);
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
void Test1()
{
//创建节点,存储数据
SLNode* plist = NULL;
SLNode* n1 = (SLNode*)malloc(sizeof(SLNode));
n1->val = 1;
SLNode* n2 = (SLNode*)malloc(sizeof(SLNode));
n2->val = 2;
SLNode* n3 = (SLNode*)malloc(sizeof(SLNode));
n3->val = 3;
SLNode* n4 = (SLNode*)malloc(sizeof(SLNode));
n4->val = 4;
SLNode* n5 = (SLNode*)malloc(sizeof(SLNode));
n5->val = 5;
//将节点连接起来
n1->next = n2;
n2->next = n3;
n3->next = n4;
n4->next = n5;
n5->next = NULL;
SLTPrint(n1);
free(n1);
free(n2);
free(n3);
free(n4);
free(n5);
}
void Test2()
{
//创建节点,存储数据
SLNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPushBack(&plist, 5);
SLTPushBack(&plist, 6);
SLTPrint(plist);
}
void Test3()
{
SLNode* plist = NULL;
SLTPushFront(&plist, 1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
SLTPushFront(&plist, 4);
SLTPrint(plist);
}
void Test4()
{
SLNode* plist = NULL;
SLTPushFront(&plist, 1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
SLTPushFront(&plist, 4);
SLTPopBack(&plist);
SLTPrint(plist);
}
void Test5()
{
SLNode* plist = NULL;
SLTPushFront(&plist, 1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
SLTPushFront(&plist, 4);
SLTPopFront(&plist);
SLTPrint(plist);
}
void Test6()
{
SLNode* plist = NULL;
SLTPushFront(&plist, 1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
SLTPushFront(&plist, 4);
SLNode* p1 = SLTFind(&plist, 2);
SLNode* p2 = SLTFind(&plist, 3);
SLTInsert(&plist, p1, 8);
SLTInsertAfter(p2, 10);
SLTPrint(plist);
SLTEraase((&plist), SLTFind(&plist,1));
SLTPrint(plist);
SLTEraseAfter(SLTFind(&plist,3));
SLTPrint(plist);
SLTDesTroy(&plist);
SLTPrint(plist);
}
int main()
{
//Test1();
//Test2();
//Test3();
//Test4();
//Test5();
Test6();
return 0;
}