大家好,我是Abert。
这篇文章和大家分享用C语言实现数据结构中的单链表。
单链表可以看作是“拆分开”的顺序表。在顺序表的基础上,单链表做了一些优化,但同时也丧失了一些顺序表有的功能。
我们一起来学习单链表吧!!!
上图可以看作是单链表的一个简单结构,多个节点链接起来(实际在内存中的存储可能不是这样的一条规则的链)
链表插入数据不会像顺序表一样一次开辟很多空间,插入一个类型的数据,开辟一个节点,通过改变指针的指向来把新数据与原数据串接起来,如下图
同理,单链表删除数据也只需要改变指针的指向的空间,然后free掉要删除的空间。
如下图:
该项目要创建三个文件。
头文件 SLlist.h (存放程序中需要的头文件以及声明实现功能的函数)
源文件 SLlist.c (存放实现程序功能的代码)
源文件 test.c (存放主函数及测试函数功能的代码)
typedef int SLDataType;
typedef struct SLlistNode
{
SLDataType data;//存放数据
struct SLlistNode* next;//节点
}SLTNode;
这里创建好结构体来成为单链表的子结构,假设创建了一些结构体变量:
SLTNode* n1, *n2, *n3, *n4;
把它们链接起来只需要改变它们内部指针变量的指向。如下:
n1->next = n2;
n2->next = n3;
n3->next = n4;
n4->next = NULL;
如此这些节点会链接起来,就是一个简单的单链表。
这里利用了二级指针,改变结构体指针需要传入该指针的地址,即为二级指针。
下面需要改变头节点的功能皆需要二级指针
(可以不使用二级指针,但必须return 一个 SLTNode* 的头节点指针。)
本文使用二级指针的方式。
//尾插
void SLlistPushBack(SLTNode** pphead, SLDataType x)
{
assert(pphead);
SLTNode* newnode = BuySLlistNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
//找尾节点
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
//头插
void SLlistPushFront(SLTNode** pphead, SLDataType x)
{
assert(pphead);
SLTNode* newnode = BuySLlistNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
//尾删
void SLlistPopBack(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
//1.只有一个节点
//2.有多个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
//头删
void SLlistPopFront(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
//查询
SLTNode* SLlistFind(SLTNode* phead, SLDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data = x)
{
return cur;
cur = cur->next;
}
}
return NULL;
}
//在pos位置之前插入
void SLlistInsert(SLTNode** pphead, SLTNode* pos, SLDataType x)
{
assert(pos);
assert(pphead);
//头插
if (pos == *pphead)
{
SLlistPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySLlistNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
//删除pos位置的值
void SLlistErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
//头删
if (pos = *pphead)
{
SLlistPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
//在pos位置之后插入x
void SLlistInsertAfter(SLTNode** pphead, SLTNode* pos, SLDataType x)
{
assert(pos);
SLTNode* newnode = BuySLlistNode(x);
SLTNode* next = pos->next;
pos->next = newnode;
newnode->next = next;
}
//删除pos之后的值
void SLlistEraseAfter(SLTNode* pos)
{
assert(pos);
if (pos->next == NULL)
{
return;
}
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
#pragma once
#include
#include
#include
typedef int SLDataType;
typedef struct SLlistNode
{
SLDataType data;
struct SLlistNode* next;
}SLTNode;
//初始化单链表
SLTNode* BuySLlistNode(SLDataType x);
//打印单链表
void SLPrint(SLTNode* phead);
//尾插
void SLlistPushBack(SLTNode** pphead, SLDataType x);
//头插
void SLlistPushFront(SLTNode** pphead, SLDataType x);
//尾删
void SLlistPopBack(SLTNode** pphead);
//头删
void SLlistPopFront(SLTNode** pphead);
//查询
SLTNode* SLlistFind(SLTNode* phead, SLDataType x);
//在pos位置之前插入
void SLlistInsert(SLTNode** pphead, SLTNode* pos, SLDataType x);
//删除pos位置的值
void SLlistErase(SLTNode** pphead, SLTNode* pos);
//在pos位置之后插入x
void SLlistInsertAfter(SLTNode** pphead, SLTNode* pos, SLDataType x);
//删除pos之后的值
void SLlistEraseAfter(SLTNode* pos);
#define _CRT_SECURE_NO_WARNINGS 1
#include"SLlist.h"
//创建一个节点
SLTNode* BuySLlistNode(SLDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//打印单链表
void SLPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
//尾插
void SLlistPushBack(SLTNode** pphead, SLDataType x)
{
assert(pphead);
SLTNode* newnode = BuySLlistNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
//找尾节点
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
//头插
void SLlistPushFront(SLTNode** pphead, SLDataType x)
{
assert(pphead);
SLTNode* newnode = BuySLlistNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
//尾删
void SLlistPopBack(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
//1.只有一个节点
//2.有多个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
//头删
void SLlistPopFront(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
//查询
SLTNode* SLlistFind(SLTNode* phead, SLDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data = x)
{
return cur;
cur = cur->next;
}
}
return NULL;
}
//在pos位置之前插入
void SLlistInsert(SLTNode** pphead, SLTNode* pos, SLDataType x)
{
assert(pos);
assert(pphead);
//头插
if (pos == *pphead)
{
SLlistPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySLlistNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
//删除pos位置的值
void SLlistErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
//头删
if (pos = *pphead)
{
SLlistPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
//在pos位置之后插入x
void SLlistInsertAfter(SLTNode** pphead, SLTNode* pos, SLDataType x)
{
assert(pos);
SLTNode* newnode = BuySLlistNode(x);
SLTNode* next = pos->next;
pos->next = newnode;
newnode->next = next;
}
//删除pos之后的值
void SLlistEraseAfter(SLTNode* pos)
{
assert(pos);
if (pos->next == NULL)
{
return;
}
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"SLlist.h"
//测试尾插
void TestSList1()
{
//struct SListNode*
SLTNode* n1 = (SLTNode*)malloc(sizeof(SLTNode));
assert(n1);
SLTNode* n2 = (SLTNode*)malloc(sizeof(SLTNode));
assert(n2);
SLTNode* n3 = (SLTNode*)malloc(sizeof(SLTNode));
assert(n3);
SLTNode* n4 = (SLTNode*)malloc(sizeof(SLTNode));
assert(n4);
n1->data = 1;
n2->data = 2;
n3->data = 3;
n4->data = 4;
n1->next = n2;
n2->next = n3;
n3->next = n4;
n4->next = NULL;
SLTNode* plist = n1;
SListPrint(plist);
SListPushBack(&plist, 5);
SListPushBack(&plist, 6);
SListPushBack(&plist, 7);
SListPushBack(&plist, 8);
SListPrint(plist);
}
//测试头插
void TestSList2()
{
SLTNode* plist = NULL;
SListPushFront(&plist, 0);
SListPushFront(&plist, 1);
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 4);
SListPrint(plist);
SListPopFront(&plist);
SListPopFront(&plist);
SListPopFront(&plist);
SListPopFront(&plist);
SListPopFront(&plist);
//SListPopFront(&plist);
SListPrint(plist);
}
//测试尾插与尾删
void TestSList3()
{
SLTNode* plist = NULL;
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListPrint(plist);
SListPopBack(&plist);
SListPrint(plist);
SListPopBack(&plist);
SListPrint(plist);
SListPopBack(&plist);
SListPrint(plist);
SListPopBack(&plist);
SListPrint(plist);
//SListPopBack(&plist);
//SListPrint(plist);
}
//测试查询
void TestSList4()
{
SLTNode* plist = NULL;
SListPrint(plist);
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListPrint(plist);
SLTNode* ret = SListFind(plist, 3);
if (ret)
{
printf("找到了\n");
ret->data = 30;
}
SListPrint(plist);
SLTNode* pos = SListFind(plist, 4);
if (pos)
{
SListInsert(&plist, pos, 40);
}
SListPrint(plist);
//SListPushBack(NULL, 1);
//SListInsert(&plist, NULL, 40);
//SListPrint(plist);
pos = SListFind(plist, 4);
if (pos)
{
SListErase(&plist, pos);
}
SListPrint(plist);
}
int main()
{
//测试哪个模块的功能,只需解引用该模块的函数
/*TestSList1();
TestSList2();
TestSList3();*/
TestSList4();
return 0;
}
单链表的优点:
单链表中插入一个类型的数据仅需开辟一个结点的空间,空间利用率高。
删除数据只需释放结点的空间,不需要挪动数据。
任意删除或添加数据,时间复杂度尾O(1)
单链表的缺点:
单链表中任意删除/添加/查找/修改的功能不能使用(不支持数据的随机访问)。
单链表相比顺序表有些许提升,但同时也存在一系列问题。
总体来说,需要不断插入删除数据的时候,采用单链表会比顺序表效率高。
以上就是这篇文章的全部内容,希望对大家有帮助。