纵横数据结构与算法
目录
欢迎订阅本专栏,说在前面
今天给大家带来单链表数据结构的讲解!
博主为了本系列专栏,做了很多准备,争取图文并茂,让大家看明白!希望大家不要吝啬订阅,与关注,多多评论哦!!
一、前言
那么这里博主先安利一下其他一些干货满满的专栏啦!
玩转Linux操作系统,点击下方蓝字即可跳转:
欢迎订阅,玩转Linux+系统编程+网络编程
CCF相关真题,点击下方蓝字即可跳转:
CCF真题
刷题专栏,点击下方蓝字跳转:
刷题专栏
二 、正文
2.1 与顺序表的比较
关于顺序表问题:
- 中间/头部的插入删除,时间复杂度为O(N)
- 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
- 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
思考:如何解决以上问题呢?下面给出了链表的结构来看看。
2.2 单链表
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
它由一系列节点组成,每个节点包含两个属性:数据域和指针域。单链表的每个节点都可以向前或向后移动,但不能向两端移动。单链表的基本操作包括插入、删除、查找、遍历等。插入操作将新节点插入到单链表的头部或尾部,删除操作将指定节点从单链表中删除,查找操作查找指定节点在单链表中的位置,遍历操作按照指定节点的顺序访问单链表中的所有节点。
单链表的优点是结构简单、插入、删除、查找等操作的时间复杂度均为O(1)(我也会给出O(N)复杂度的向前插入算法),适合用于需要频繁插入、删除、查找等操作的场合。然而,单链表的空间复杂度较高,需要占用一定的存储空间,因此在一些场景下,可能需要使用其他数据结构来代替单链表。
在实际应用中,单链表通常与队列、栈等数据结构结合使用,以实现更复杂的功能。
2.3 相关代码实现与详解图
1. 结点描述与接口
typedef int SLDatatype;
typedef struct SListNode
{
SLDatatype data;
struct SListNode* next;
}SLTNode;
//打印
void SLPrint(SLTNode* phead);
//创建结点
SLTNode* CreateNode(SLDatatype x);
//头插尾插
void Push_Back_SLT(SLTNode** pphead,SLDatatype x);
void Push_Front_SLT(SLTNode** pphead,SLDatatype x);
//头删尾删
void Pop_Back_STL(SLTNode** pphead);
void Pop_Front_STL(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLDatatype x);
//O(N) 算法下的 向前插入 与 原地删除
void SLTInsert_forward(SLTNode** pphead, SLTNode* pos, SLDatatype x);
void SLTErase(SLTNode** pphead, SLTNode* pos);
//O(1)算法下的 向后插入 与 向后删除
void STLInsert_back(SLTNode** pphead, SLTNode* pos, SLDatatype x);
void STLErase_back(SLTNode** pphead, SLTNode* pos);
还是熟悉的增删查改,并且沿用了上一篇文章的相关技巧,代码通用性和命名风格!
2. 相关接口实现与配图
(1) 创建节点
根据以往经验,我们先写出打印函数与结点创建函数,方便复用。
void SLPrint(SLTNode* phead)//这里传一级指针即可,不涉及对于头指针的修改
{
if (phead == NULL)//当是空结点的时候
{
printf("This is NULL !");
}
else {
while (phead->next != NULL)
{
printf("%d -> ", phead->data);
phead = phead->next;
}
printf("%d -> NULL ",phead->data);
//printf("-> NULL");
}
}
SLTNode* CreateNode(SLDatatype x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
return newnode;
}
(2) 尾插与头插与配图
//这里传递二级指针,如果是空结点将涉及到在函数内对于phead的修改
void Push_Back_SLT(SLTNode** pphead, SLDatatype x)
{
assert(pphead);
if (*pphead == NULL)//空结点
{
*pphead = CreateNode(x);
}
else
{
SLTNode* cur = *pphead;
while (cur->next != NULL)
{
cur = cur->next;
}
cur->next = CreateNode(x);
}
}
void Push_Front_SLT(SLTNode** pphead, SLDatatype x)
{
assert(pphead);
if (*pphead == NULL)//空结点
{
*pphead = CreateNode(x);
}
else
{
SLTNode* curFront = CreateNode(x);
curFront->next = *pphead;
*pphead = curFront;
}
}
(3) 尾删头删与配图
void Pop_Back_STL(SLTNode** pphead)
{
assert(pphead);
assert(*pphead); //空的不能删
//一个结点
if ((*pphead) ->next == NULL) //注意两次解引用* -> 带括号区分优先级
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* prev = *pphead;
while (prev->next->next != NULL)
{
prev = prev->next;
}
free(prev->next);
prev->next = NULL;
}
}
void Pop_Front_STL(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);//判空
//一个结点
if ((*pphead)->next == NULL) //注意两次解引用* -> 带括号区分优先级
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
}
(4) 查找
SLTNode* SLTFind(SLTNode* phead, SLDatatype x)
{
assert(phead);
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
(5)O(N)复杂度下的向前插与原地删除
void SLTInsert_forward(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
assert(pphead);
assert(pos);
if ( (*pphead)->next == NULL)
{
Push_Front_SLT(pphead,x);
}
else {
SLTNode* cur = *pphead;
SLTNode* prev = *pphead;
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
SLTNode* newnode = CreateNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if ((*pphead)->next == NULL)
{
Pop_Front_STL(pphead);
}
else {
SLTNode* cur = *pphead;
SLTNode* prev = *pphead;
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
prev->next = cur->next;
free(cur);
cur = NULL;
}
}
(6)O(1)复杂度下的向后插入与向后删除
void STLInsert_back(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
Push_Front_SLT(pphead,x);
}
else
{
SLTNode* newnode = CreateNode(x);
SLTNode* next = pos->next;
pos->next = newnode;
newnode->next = next;
}
}
void STLErase_back(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
Pop_Back_STL(pphead);
}
else if (pos->next == NULL)
{
return;
}
else
{
SLTNode* posnext = pos->next->next;
free(pos->next);
pos->next = posnext;
}
}
3. 完整代码
.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDatatype;
typedef struct SListNode
{
SLDatatype data;
struct SListNode* next;
}SLTNode;
//打印
void SLPrint(SLTNode* phead);
//创建结点
SLTNode* CreateNode(SLDatatype x);
//头插尾插
void Push_Back_SLT(SLTNode** pphead,SLDatatype x);
void Push_Front_SLT(SLTNode** pphead,SLDatatype x);
//头删尾删
void Pop_Back_STL(SLTNode** pphead);
void Pop_Front_STL(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLDatatype x);
//O(N) 算法下的 向前插入 与 原地删除
void SLTInsert_forward(SLTNode** pphead, SLTNode* pos, SLDatatype x);
void SLTErase(SLTNode** pphead, SLTNode* pos);
//O(1)算法下的 向后插入 与 向后删除
void STLInsert_back(SLTNode** pphead, SLTNode* pos, SLDatatype x);
void STLErase_back(SLTNode** pphead, SLTNode* pos);
.cpp
#include "SList.h"
void SLPrint(SLTNode* phead)//这里传一级指针即可,不涉及对于头指针的修改
{
if (phead == NULL)//当是空结点的时候
{
printf("This is NULL !");
}
else {
while (phead->next != NULL)
{
printf("%d -> ", phead->data);
phead = phead->next;
}
printf("%d -> NULL ",phead->data);
//printf("-> NULL");
}
}
SLTNode* CreateNode(SLDatatype x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//这里传递二级指针,如果是空结点将涉及到在函数内对于phead的修改
void Push_Back_SLT(SLTNode** pphead, SLDatatype x)
{
assert(pphead);
if (*pphead == NULL)//空结点
{
*pphead = CreateNode(x);
}
else
{
SLTNode* cur = *pphead;
while (cur->next != NULL)
{
cur = cur->next;
}
cur->next = CreateNode(x);
}
}
void Push_Front_SLT(SLTNode** pphead, SLDatatype x)
{
assert(pphead);
if (*pphead == NULL)//空结点
{
*pphead = CreateNode(x);
}
else
{
SLTNode* curFront = CreateNode(x);
curFront->next = *pphead;
*pphead = curFront;
}
}
void Pop_Back_STL(SLTNode** pphead)
{
assert(pphead);
assert(*pphead); //空的不能删
//一个结点
if ((*pphead) ->next == NULL) //注意两次解引用* -> 带括号区分优先级
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* prev = *pphead;
while (prev->next->next != NULL)
{
prev = prev->next;
}
free(prev->next);
prev->next = NULL;
}
}
void Pop_Front_STL(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);//判空
//一个结点
if ((*pphead)->next == NULL) //注意两次解引用* -> 带括号区分优先级
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
}
SLTNode* SLTFind(SLTNode* phead, SLDatatype x)
{
assert(phead);
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SLTInsert_forward(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
assert(pphead);
assert(pos);
if ( (*pphead)->next == NULL)
{
Push_Front_SLT(pphead,x);
}
else {
SLTNode* cur = *pphead;
SLTNode* prev = *pphead;
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
SLTNode* newnode = CreateNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if ((*pphead)->next == NULL)
{
Pop_Front_STL(pphead);
}
else {
SLTNode* cur = *pphead;
SLTNode* prev = *pphead;
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
prev->next = cur->next;
free(cur);
cur = NULL;
}
}
void STLInsert_back(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
Push_Front_SLT(pphead,x);
}
else
{
SLTNode* newnode = CreateNode(x);
SLTNode* next = pos->next;
pos->next = newnode;
newnode->next = next;
}
}
void STLErase_back(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
Pop_Back_STL(pphead);
}
else if (pos->next == NULL)
{
return;
}
else
{
SLTNode* posnext = pos->next->next;
free(pos->next);
pos->next = posnext;
}
}
4. 测试用例
test.cpp
#include "SList.h"
void test1()//测试SLprint
{
SLTNode* plist1= (SLTNode*)malloc(sizeof(SLTNode));
assert(plist1);
SLTNode* plist2= (SLTNode*)malloc(sizeof(SLTNode));
assert(plist2);
SLTNode* plist3= (SLTNode*)malloc(sizeof(SLTNode));
assert(plist3);
SLTNode* plist4= (SLTNode*)malloc(sizeof(SLTNode));
assert(plist4);
plist1->data = 1;
plist2->data = 2;
plist3->data = 3;
plist4->data = 4;
plist1->next = plist2;
plist2->next = plist3;
plist3->next = plist4;
plist4->next = NULL;
SLPrint(plist1);
}
void test2()
{
/*SLTNode* plist = (SLTNode*)malloc(sizeof(SLTNode));
assert(plist);
plist->data = 0;
plist->next = NULL;//测试正常情况多结点下的尾插
*/
SLTNode* plist = NULL;
Push_Back_SLT(&plist, 1);
Push_Back_SLT(&plist, 2);
Push_Back_SLT(&plist, 3);
Push_Back_SLT(&plist, 4);
SLPrint(plist);
}
void test3()
{
/*SLTNode* plist = (SLTNode*)malloc(sizeof(SLTNode));
assert(plist);
plist->data = 0;
plist->next = NULL;//测试正常情况多结点下的尾插
*/
SLTNode* plist = NULL;
Push_Back_SLT(&plist, 1);
Push_Back_SLT(&plist, 2);
Push_Back_SLT(&plist, 3);
Push_Back_SLT(&plist, 4);
Push_Front_SLT(&plist, -1);
Push_Front_SLT(&plist, -2);
Push_Front_SLT(&plist, -3);
SLPrint(plist);
}
void test4()
{
/*SLTNode* plist = (SLTNode*)malloc(sizeof(SLTNode));
assert(plist);
plist->data = 0;
plist->next = NULL;//测试正常情况多结点下的尾插
*/
SLTNode* plist = NULL;
Push_Back_SLT(&plist, 1);
Push_Back_SLT(&plist, 2);
Push_Back_SLT(&plist, 3);
Push_Back_SLT(&plist, 4);
Push_Front_SLT(&plist, -1);
Push_Front_SLT(&plist, -2);
Push_Front_SLT(&plist, -3);
SLPrint(plist);
printf("\n");
Pop_Back_STL(&plist);
Pop_Back_STL(&plist);
Pop_Back_STL(&plist);
Pop_Back_STL(&plist);
SLPrint(plist);
printf("\n");
Pop_Front_STL(&plist);
Pop_Front_STL(&plist);
Pop_Front_STL(&plist);
SLPrint(plist);
printf("\n");
}
void test5()
{
SLTNode* plist = NULL;
Push_Back_SLT(&plist, 1);
Push_Back_SLT(&plist, 2);
Push_Back_SLT(&plist, 3);
Push_Back_SLT(&plist, 4);
Push_Front_SLT(&plist, -1);
Push_Front_SLT(&plist, -2);
Push_Front_SLT(&plist, -3);
SLPrint(plist);
printf("\n");
SLTInsert_forward(&plist, SLTFind(plist,2), 999);
SLPrint(plist);
printf("\n");
SLTErase(&plist, SLTFind(plist, -2));
SLPrint(plist);
printf("\n");
printf("#########");
printf("\n");
//测试一个结点
SLTNode* plist2 = NULL;
Push_Back_SLT(&plist2, 1);
SLTInsert_forward(&plist2, SLTFind(plist2, 1), 999);
SLPrint(plist2);
printf("\n");
SLTErase(&plist2, SLTFind(plist2, 1));
SLPrint(plist2);
printf("\n");
}
void test6()
{
SLTNode* plist = NULL;
Push_Back_SLT(&plist, 1);
Push_Back_SLT(&plist, 2);
Push_Back_SLT(&plist, 3);
Push_Back_SLT(&plist, 4);
Push_Front_SLT(&plist, -1);
Push_Front_SLT(&plist, -2);
Push_Front_SLT(&plist, -3);
SLPrint(plist);
printf("\n");
STLInsert_back(&plist, SLTFind(plist, 2), 999);
SLPrint(plist);
printf("\n");
STLErase_back(&plist, SLTFind(plist, -2));
SLPrint(plist);
printf("\n");
}
int main()
{
//test1();//测试打印函数与结构体定义
//test2();
//test3(); //测试头插尾插
//test4(); //测试头删尾删
//test5(); //测试前插原地删除
test6();
return 0;
}
三、 结尾
感谢大家的点赞关注,希望大家多多评论!!!