文章目录
🚩前言
在数据结构中主要的就是对数据的增删查改操作,在前面讲的顺序表就是其中一种结构,顺序表前身的数组(静态和动态)。而该结构是叫单链表,从逻辑结构上看是连续的,而在物理结构上是不连续存储的。下面就来对单链表的具体实现:
1、单链表的内涵
(1) 逻辑结构
什么叫作逻辑结构上是连续的,而物理结构上是不连续的。首先,在一个连续的空间当中就是连续存储,反之就不是,但是从我们思维结构来看可以当做是连续的,下面是单链表的逻辑结构:
(2) 物理结构
物理结构上就得从计算机内存中来看,就是在一个内存块中,每个节点所开辟的空间位置不是紧挨着的,存在一定距离,如下简图:
2、链表的分类
3、单链表的具体实现
(1) 框架结构
(2) SingleLinkList.h头文件的实现
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//创建单链表结构体
typedef int DataType;
typedef struct SingLinkListNode
{DataType data;struct SingLinkListNode *next;
}SLTNode;//打印函数
void PrintLinkList(SLTNode *phead);//尾插
void SLTPushBack(SLTNode **pphead,DataType data);//头插
void SLTPushFront(SLTNode **pphead,DataType data);//尾删
void SLTPopBack(SLTNode **pphead);//头删
void SLTPopFront(SLTNode **pphead);//查找
SLTNode* SLTFind(SLTNode *phead,DataType data);//指定位置之前插入数据
void SLTInsert(SLTNode **pphead,SLTNode *pos,DataType data);//指定位置之后插入数据
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, DataType data);//删除节点函数
void SLTErase(SLTNode** pphead,SLTNode* pos);//销毁链表函数
void SLTDestroy(SLTNode** pphead);
(3)SingleLinkList.c源文件的实现
①SLTPushBack()尾插函数
SLTNode* BuyNode(DataType x)
{SLTNode *NewNode = (SLTNode*)malloc(sizeof(SLTNode));if (NewNode == NULL){perror("BuyNode:: malloc fail!");exit(1);}else{NewNode->data = x;NewNode->next = NULL;}return NewNode;
}void SLTPushBack(SLTNode **pphead, DataType data)
{assert(pphead);SLTNode *NewNode=BuyNode(data);if ((*pphead) == NULL){*pphead = NewNode;}else{//找尾SLTNode *ptail = *pphead;while (ptail->next!=NULL){ptail = ptail->next;}ptail->next = NewNode;}
}
图解:尾插数据得申请新节点BuyNode();
②SLTPushFront()头插函数
void SLTPushFront(SLTNode **pphead, DataType data)
{assert(pphead);SLTNode *NewNode = BuyNode(data);if (*pphead == NULL){*pphead = NewNode;}else{NewNode->next = *pphead;*pphead = NewNode;}
}
图解:
③SLTPopBack()尾删函数
void SLTPopBack(SLTNode **pphead)
{assert(pphead&&(*pphead)->data);SLTNode* prev = NULL;SLTNode* ptail = *pphead;//当只有一个节点的时候if ((*pphead)->next==NULL){free(*pphead);*pphead = NULL;}else{//找尾节点while ((ptail->next)!=NULL){prev = ptail;ptail = ptail->next;}free(ptail);ptail = NULL;prev->next = NULL;}
}
图解:
④SLTPopFront()头删函数
void SLTPopFront(SLTNode **pphead)
{assert(pphead && *pphead);SLTNode *pcur = (*pphead)->next;free(*pphead);*pphead = pcur;
}
图解:头删很简单。
⑤SLTInsert()和SLTInsertAfter()指定位置之前和之后插入数据函数
void SLTInsert(SLTNode** pphead, SLTNode* pos, DataType data)
{assert(pphead && pos);SLTNode* NewNode = BuyNode(data);SLTNode* prev = *pphead;//当插入的数据节点是头节点时就调用头插if (pos == *pphead){SLTPushFront(pphead,data);}else{while (prev->next != pos){prev = prev->next;}prev->next = NewNode;NewNode->next = pos;}
}void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, DataType data)
{assert(pphead && pos);//SLTNode* pcur = *pphead;SLTNode* NewNode = BuyNode(data);//pcur = pos->next;//pos->next = NewNode;//NewNode->next = pcur;NewNode->next = pos->next;pos->next = NewNode;
}
图解:
⑥SLTErase()和SLTDestroy()删除和销毁函数
//删除
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos);SLTNode* prev = *pphead;if (pos == *pphead){//头删SLTPopFront(pphead);}else{while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
//销毁
void SLTDestroy(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}
在销毁的时候得注意,因为节点是一个一个创建的,所以也得一个一个的销毁。
4、完整代码
1、头文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//创建单链表结构体
typedef int DataType;
typedef struct SingLinkListNode
{DataType data;struct SingLinkListNode *next;
}SLTNode;//打印函数
void PrintLinkList(SLTNode *phead);//尾插
void SLTPushBack(SLTNode **pphead,DataType data);//头插
void SLTPushFront(SLTNode **pphead,DataType data);//尾删
void SLTPopBack(SLTNode **pphead);//头删
void SLTPopFront(SLTNode **pphead);//查找
SLTNode* SLTFind(SLTNode *phead,DataType data);//指定位置之前插入数据
void SLTInsert(SLTNode **pphead,SLTNode *pos,DataType data);//指定位置之后插入数据
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, DataType data);//删除节点函数
void SLTErase(SLTNode** pphead,SLTNode* pos);//销毁链表函数
void SLTDestroy(SLTNode** pphead);
2、源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"SingleLinkList.h"void PrintLinkList(SLTNode *phead)
{// assert(phead&&phead->next);SLTNode *pcur = phead;while (pcur != NULL){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL");
}SLTNode* BuyNode(DataType x)
{SLTNode *NewNode = (SLTNode*)malloc(sizeof(SLTNode));if (NewNode == NULL){perror("BuyNode:: malloc fail!");exit(1);}else{NewNode->data = x;NewNode->next = NULL;}return NewNode;
}void SLTPushBack(SLTNode **pphead, DataType data)
{assert(pphead);SLTNode *NewNode=BuyNode(data);if ((*pphead) == NULL){*pphead = NewNode;}else{//找尾SLTNode *ptail = *pphead;while (ptail->next!=NULL){ptail = ptail->next;}ptail->next = NewNode;}
}void SLTPushFront(SLTNode **pphead, DataType data)
{assert(pphead);SLTNode *NewNode = BuyNode(data);if (*pphead == NULL){*pphead = NewNode;}else{NewNode->next = *pphead;*pphead = NewNode;}
}void SLTPopBack(SLTNode **pphead)
{assert(pphead&&(*pphead)->data);SLTNode* prev = NULL;SLTNode* ptail = *pphead;//当只有一个节点的时候if ((*pphead)->next==NULL){free(*pphead);*pphead = NULL;}else{//找尾节点while ((ptail->next)!=NULL){prev = ptail;ptail = ptail->next;}free(ptail);ptail = NULL;prev->next = NULL;}
}void SLTPopFront(SLTNode **pphead)
{assert(pphead && *pphead);SLTNode *pcur = (*pphead)->next;free(*pphead);*pphead = pcur;
}SLTNode* SLTFind(SLTNode *phead, DataType data)
{assert(phead);SLTNode *pcur = phead;while (pcur){if (pcur->data == data){return pcur;}pcur = pcur->next;}return NULL;
}void SLTInsert(SLTNode** pphead, SLTNode* pos, DataType data)
{assert(pphead && pos);SLTNode* NewNode = BuyNode(data);SLTNode* prev = *pphead;//当插入的数据节点是头节点时就调用头插if (pos == *pphead){SLTPushFront(pphead,data);}else{while (prev->next != pos){prev = prev->next;}prev->next = NewNode;NewNode->next = pos;}
}void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, DataType data)
{assert(pphead && pos);//SLTNode* pcur = *pphead;SLTNode* NewNode = BuyNode(data);//pcur = pos->next;//pos->next = NewNode;//NewNode->next = pcur;NewNode->next = pos->next;pos->next = NewNode;
}void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos);SLTNode* prev = *pphead;if (pos == *pphead){//头删SLTPopFront(pphead);}else{while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}void SLTDestroy(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}
3、测试代码文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"SingleLinkList.h"void test()
{SLTNode* sl=NULL;//尾插函数测试SLTPushBack(&sl, 1);SLTPushBack(&sl, 2);SLTPushBack(&sl, 3);SLTPushBack(&sl, 4);//打印PrintLinkList(sl);printf("\n");//头插函数测试SLTPushFront(&sl, 10);SLTPushFront(&sl, 20);SLTPushFront(&sl, 30);//打印PrintLinkList(sl);printf("\n");//尾删函数测试SLTPopBack(&sl);SLTPopBack(&sl);//打印PrintLinkList(sl);printf("\n");//头删函数测试SLTPopFront(&sl);SLTPopFront(&sl);//打印PrintLinkList(sl);//查找函数测试int number = 0;printf("\n请输入要查找的数:");scanf("%d",&number);if (SLTFind(sl, number) == NULL){printf("没找到!\n");}else{printf("找到了!%p\n", SLTFind(sl, number));}//在一节点之前插入数据函数测试int number1 = 0;int number2 = 0;printf("请输入要在那个节点之前插入数据:");scanf("%d",&number1);printf("请输入要插入的数据:");scanf("%d",&number2);SLTInsert(&sl,SLTFind(sl,number1),number2);//打印PrintLinkList(sl);printf("\n");//在一节点之后插入数据函数测试int number3 = 0;int number4 = 0;printf("请输入要在那个节点之后插入数据:");scanf("%d", &number3);printf("请输入要插入的数据:");scanf("%d", &number4);SLTInsertAfter(&sl, SLTFind(sl, number3), number4);//打印PrintLinkList(sl);printf("\n");//删除节点函数测试int number5 = 0;printf("请输入要删除的节点的数据:");scanf("%d",&number5);SLTErase(&sl,SLTFind(sl,number5));PrintLinkList(sl);printf("\n");//销毁函数测试SLTDestroy(&sl);
}int main()
{test();return 0;
}
5、效果展示