单链表介绍,单链表讲解以及实现
大家好,还是我这位老实人,今天来讲解和实现一下单链表。但是我们要先说一下链表,链表,是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。那么,这种链式结构在逻辑上是连续的,但是在物理上不一定连续。 下面进入正题环节
单链表的讲解
回到单链表,顾名思义,便是单方向的链表,有单链表肯定也有双链表,这个下节再议。单链表结构简单,一般不会用来存储数据,但是因为我是老实初学者,所以还是要脚踏实地,踏踏实实的写出来,下图便是单链表的示意图,非常的简单易懂。
单链表的实现
接口声明
进入正题,想要实现单链表,进行接口的声明不能少,所以,下面直接介绍单链表的接口:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <malloc.h>
typedef int SLTDateType;
typedef struct SListNode {
SLTDateType data;
struct SListNode* next;
}SListNode,SN;
SN* BuySListNode(SLTDateType x);
void SListPrint(SN* plist);
void SListPushBack(SN** pplist, SLTDateType x);
void SListPushFront(SN** pplist, SLTDateType x);
void SListPopBack(SN** pplist);
void SListPopFront(SN** pplist);
SN* SListFind(SN* plist, SLTDateType x);
//分析思考为什么不在pos位置前插入
//答:因为太麻烦了,要是在前面插入的话根本没有前面的地址,只能遍历出来再搞
void SListInsertAfter(SN* pos, SLTDateType x);
//分析思考为什么不删除pos位置
//答:和上面的一样,因为没有上面的地址
void SListEraseAfter(SN* pos);
void SListDestroy(SListNode** pphead);
相信大家可能会有疑问,但是有的疑问后续会讲,有的疑问已经放在代码块里了。请大家自行翻阅
接口实现
void SListPrint(SN* plist) {
SN* cur = plist;
while (cur) {
printf("%d->",cur->data);
cur = cur->next;
}
printf("NULL\n");
}
当然,先来的还是最简单的打印单链表
SN* BuySListNode(SLTDateType x) {
SN* SListNode = (SN*)malloc(sizeof(SN));
if (SListNode == NULL) {
printf("buy slistnode fail\n");
exit(-1);
}
else {
SListNode->data = x;
SListNode->next = NULL;
}
return SListNode;
}
顾名思义,买一个节点,便是创造出一个新的节点,使用开辟内存的malloc创造,并将新节点的值设置为要设置的值,将新节点的下一个链接的地址置为NULL
void SListPushBack(SN** pplist, SLTDateType x) {
assert(pplist);
SN* newnode = BuySListNode(x);
if (*pplist == NULL) {
*pplist = newnode;
}
else {
SN* tail = *pplist;
while (tail->next) {
tail = tail->next;
}
tail->next = newnode;
}
}
void SListPushFront(SN** pplist, SLTDateType x) {
assert(pplist);
SN* newnode = BuySListNode(x);
/*if (*pplist) {
newnode->next = *pplist;
*pplist = newnode;
}
else {
*pplist = newnode;
}*/
//没必要上面的,下面的确实更方便,即使链表为空也是没问题
newnode->next = *pplist;
*pplist = newnode;
}
void SListPopBack(SN** pplist) {
assert(pplist);
if (*pplist == NULL) {
return;
}
else if((*pplist)->next == NULL){
free(*pplist);
*pplist = NULL;
}
else {
SN* tail = *pplist;
while (tail->next->next) {
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SListPopFront(SN** pplist) {
assert(pplist);
if (*pplist == NULL) {
return;
}
else {
SN* newhead = (*pplist)->next;
free(*pplist);
*pplist = newhead;
}
}
这便是经典的push和pop四组函数了,但是根据上述代码的分析,pushfront和popfront都是非常简单的操作,所以单链表尽量进行pushfront和popfront,还是因为单链表过于简单所以其函数都比较极端,方便的很方便,麻烦的很麻烦。
当然,这里面最重要的还是这四个函数的输入参数都是SN指针的指针,也就是双指针,因为如果pushfront和pushback的情况是单链表中没有元素,那么就需要将开头元素的NULL改成需要push的元素的地址,但是如果传的SN的指针的话,那么将无法修改,因为在函数里这只是形参的改变,实参不会改变,就类似于int a后,使用一个参数为int的函数changeint(int)是无法对a进行修改的,我们需要将changeint的输入参数变为int的地址或者int的指针才可以对a的值进行修改;而popback和popfront也是同理,这里不再过多赘述。
SN* SListFind(SN* plist, SLTDateType x) {
/*if (plist) {
printf("no data\n");
}
else {
SN* cur = plist;
while (cur->next) {
if (cur->data == x) {
return cur;
}
cur = cur->next;
}
printf("can not find\n");
return NULL;
}*/
//下面的更方便一点,我这个考虑的过多了
SListNode* cur = plist;
while (cur != NULL)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SListInsertAfter(SN* pos, SLTDateType x) {
assert(pos);
SN* newnode = BuySListNode(x);
SN* next = pos->next;
pos->next = newnode;
newnode->next = next;
}
void SListEraseAfter(SN* pos) {
assert(pos);
if (pos->next) {
SN* cur = pos->next;
pos->next = pos->next->next;
free(cur);
cur = NULL;
}
}
void SListDestroy(SListNode** pphead) {
assert(pphead);
SN* cur = *pphead;
while (cur) {
SN* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
好的,最后四组函数没有过多的含金量,相信大家在理解前面的代码后,看到这四个函数会发现其实很简单,也只是进行最基础的一个应用。
单链表的总结
单链表作为数据结构的基础,对于新手来说还是非常友好的,也希望我能一直坚持下去,努力完成学业,也希望看到这里的观众事业顺利,心想事成,下次再见