目录
前言:
单链表是一种基本的数据结构,它由一系列节点组成,每个节点包含数据部分和一个指向下一个节点的指针。在单链表中,每个节点的地址不一定是连续的,而是通过指针相互链接起来。单链表的特点是存储灵活,可以动态地添加或删除节点,不需要预先分配固定大小的存储空间。
一、单链表
1.1概念
链表是⼀种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的 指针链接次序实现的。
1.2创建
(1)原理思路
节点:与顺序表不同的是,链表⾥的每节"车厢"都是独立申请下来的空间,我们称之为“结点/结点”
结点的组成主要有两个部分:当前结点要保存的数据和保存下⼀个结点的地址(指针变量)。
链表中每个结点都是独立申请的(即需要插⼊数据时才去申请⼀块结点的空间),我们需要通过指针变量来保存下⼀个结点位置才能从当前结点找到下⼀个结点。
● 定义节点结构体:首先需要定义一个结构体来表示链表的节点,通常包括数据域和指针域。
● 动态创建节点:使用malloc函数为每个节点分配内存空间,并初始化数据域和指针域。
● 插入节点:根据需要将新节点插入到链表的适当位置。
● 遍历链表:通过遍历链表,可以访问链表中的每个节点,通常用于打印或搜索特定数据
性质:
链式机构在逻辑上是连续的,在物理结构上不⼀定连续。
结点⼀般是从堆上申请的。
从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续。
(2)图像思路
(3)节点结构体
数域和指针域
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
(4)动态创建节点
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newcode = (SLTDateType*)malloc(sizeof(SListNode));
if (newcode == NULL)
{
perror("malloc fail");
return NULL;
}
newcode->data = x;
newcode->next = NULL;
return newcode;
}
二、单链表的操作
单链表的常见操作包括遍历、插入、删除和查找。这些操作通常涉及到对链表节点的指针进行操作,以实现数据的动态管理。
2.1遍历
链表和顺序表的区别顺序表内存是连续的,链表是由一个一个节点连接的,‘cur’ 指向的下一个节点赋值给‘cur’,cur = cur->next;
void SListPrint(SListNode* plist)
{
SListNode* cur = plist;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
2.2插入
(1)头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
SListNode* newcode = BuySListNode(x);
newcode->next = *pplist;
*pplist = newcode;
}
(2)尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newcode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newcode;
}
else
{
//找尾
SListNode* tail = *pplist;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newcode;
}
}
(3)指定位置(pos)插入
pos位置前面插入
void SLTInsert(SListNode** pplist, SListNode* pos, SLTDateType x)
{
assert(pplist);
assert(*pplist);
SListNode* newcode = BuySListNode(x);
if (pos == *pplist)
{
SListPushFront(pplist,x);
}
else
{
SListNode* cur = *pplist;
while (cur->next != pos)
{
cur = cur->next;
}
newcode->next = pos;
cur->next = newcode;
}
}
pos位置后面插入
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* newcode = BuySListNode(x);
newcode->next = pos->next;
pos->next = newcode;
}
2.3删除
(1)头删
void SListPopFront(SListNode** pplist)
{
//节点断言
assert(*pplist);
SListNode* first = *pplist;
*pplist = first->next;
free(first);
first = NULL;
}
(2)尾删
void SListPopBack(SListNode** pplist)
{
//节点断言
assert(pplist);
assert(*pplist);
//存在节点 1.存在一个节点 2.存在两个节点
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
//找尾
//SListNode* tail = *pplist;
//while (tail->next->next != NULL)
//{
// tail = tail->next;
//}
//free(tail->next);
//tail->next = NULL;
SListNode* prv = NULL;
SListNode* tail = *pplist;
while (tail->next != NULL)
{
prv = tail;
tail = tail->next;
}
free(tail);
prv->next = NULL;
}
}
(3)指定位置(pos)删除
pos位置之前删除
void SListEraseAfter(SListNode* pos)
{
SListNode* del = pos->next;
pos->next = pos->next->next;
free(del);
del = NULL;
}
pos位置删除
void SLTErase(SListNode** pplist, SListNode* pos)
{
assert(pplist);
assert(*pplist);
SListNode* tail = *pplist;
while (tail->next != pos)
{
tail = tail->next;
}
tail->next = pos->next;
free(pos);
pos = NULL;
}
pos位置之后删除
void SListEraseFront(SListNode** pplist,SListNode* pos)
{
assert(pplist);
assert(*pplist);
SListNode* tail = *pplist;
while (tail->next->next != pos)
{
tail = tail->next;
}
SListNode* del = tail->next;
tail->next = pos;
free(del);
del = NULL;
}
2.4查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* cur = plist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
2.5销毁
不可以直接销毁*pplist,内存存贮不是连续的需要一个一个销毁
void SLTDestory(SListNode** pplist)
{
assert(pphead);
SListNode* cur = *pphead;
while (cur)
{
SListNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
三、总结
头文件SList.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 在pos的前面插入
void SLTInsert(SListNode** pplist, SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
// 删除pos位置
void SLTErase(SListNode** pplist, SListNode* pos);
///删除pos前面的值
void SListEraseFront(SListNode* pos);
//单链表的销毁
void SLTDestory(SListNode** pplist);
SList.c
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
//动态申请一个节点
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newcode = (SLTDateType*)malloc(sizeof(SListNode));
if (newcode == NULL)
{
perror("malloc fail");
return NULL;
}
newcode->data = x;
newcode->next = NULL;
return newcode;
}
//打印单链表
void SListPrint(SListNode* plist)
{
SListNode* cur = plist;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
//单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
SListNode* newcode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newcode;
}
else
{
//找尾
SListNode* tail = *pplist;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newcode;
}
}
//单链表尾删
void SListPopBack(SListNode** pplist)
{
//节点断言
assert(*pplist);
//if((*pplist)==NULL)
// return ;
//存在节点 1.存在一个节点 2.存在两个节点
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
//找尾
//SListNode* tail = *pplist;
//while (tail->next->next != NULL)
//{
// tail = tail->next;
//}
//free(tail->next);
//tail->next = NULL;
SListNode* prv = NULL;
SListNode* tail = *pplist;
while (tail->next != NULL)
{
prv = tail;
tail = tail->next;
}
free(tail);
prv->next = NULL;
}
}
//单链表头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
SListNode* newcode = BuySListNode(x);
newcode->next = *pplist;
*pplist = newcode;
}
//单链表头删
void SListPopFront(SListNode** pplist)
{
//节点断言
assert(*pplist);
SListNode* first = *pplist;
*pplist = first->next;
free(first);
first = NULL;
}
//单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* cur = plist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* newcode = BuySListNode(x);
newcode->next = pos->next;
pos->next = newcode;
}
// 在pos的前面插入
void SLTInsert(SListNode** pplist, SListNode* pos, SLTDateType x)
{
assert(pplist);
assert(*pplist);
SListNode* newcode = BuySListNode(x);
if (pos == *pplist)
{
SListPushFront(pplist,x);
}
else
{
SListNode* cur = *pplist;
while (cur->next != pos)
{
cur = cur->next;
}
newcode->next = pos;
cur->next = newcode;
}
}
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
SListNode* del = pos->next;
pos->next = pos->next->next;
free(del);
del = NULL;
}
// 删除pos位置
void SLTErase(SListNode** pplist, SListNode* pos)
{
assert(pplist);
assert(*pplist);
SListNode* tail = *pplist;
while (tail->next != pos)
{
tail = tail->next;
}
tail->next = pos->next;
free(pos);
pos = NULL;
}
//删除pos前面的值
void SListEraseFront(SListNode** pplist,SListNode* pos)
{
assert(pplist);
assert(*pplist);
SListNode* tail = *pplist;
while (tail->next->next != pos)
{
tail = tail->next;
}
SListNode* del = tail->next;
tail->next = pos;
free(del);
del = NULL;
}
//销毁链表
void SLTDestory(SListNode** pplist)
{
assert(pplist);
SListNode* cur = *pplist;
while (cur)
{
SListNode* next = cur->next;
free(cur);
cur = next;
}
*pplist = NULL;
测试文件test.c
#define _CRT_SECURE_NO_WARNINGS
#include "SList.h"
void testSList1()
{
SListNode* plist = NULL;
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListNode* ret = SListFind(plist, 2);
//ret->data = (ret->data) * 3;
//SListInsertAfter(ret, 66);
SLTInsert(&plist, ret, 77);
//SListEraseAfter(ret);
//SLTErase(&plist, ret);
SListEraseFront(&plist, ret);
SListPrint(plist);
}
void testSList2()
{
SListNode* plist = NULL;
SListPushFront(&plist, 1);
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 4);
SListPopFront(&plist);
SListPopFront(&plist);
SListPrint(plist);
}
int main()
{
testSList1();
//testSList2();
return 0;
}
补充
单链表在链表中被称为:无头单向非循环链表
无头单向非循环链表:结构简单,⼀般不会单独用来存数据。实际中更多是作为其他数据结构的⼦ 结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。