大家好呀,本文我们将进行链表的学习。链表是一种在我们的逻辑结构上连续,但是在物理结构上不连续的存储结构。我们可以看成这个这样:
分类:链表根据单双方向,是否带头,是否循环分成了3种类型:
1:单向链表,双向列表
2:带头,不带头
3:循环,不循环
在我们的生活中最为常用的结构有两种:无头单项不循环链表,带头双向循环列表
那么通过上图我们可以得出链表为一个结构体,其中包含数据与指向下一位的指针。接下来我们来实现一下我们的链表。
首先我们实现一下无头单向不循环链表。
我们先创建工程,这个工程由:源文件SListNode.c 和 Test.c 头文件:SListNode.h 组成
SList.h :声明功能函数,引用库函数
SList.c:完善功能函数
Test.c:调试代码
首先:这个链表我们需要创建一个结构体,这个结构体由一个数据+一个指针组成,由此我们设计出一个结构体:SListNode
接下来我们来完善几个基本功能:链表创建(CreateNode),数据尾插(SLTPushBack),打印链表(SLTPrint),内存销毁( SLTDexstroy)
链表创建(CreateNode):首先我们来创建一个链表,我们使用malloc函数来动态开辟一个空间,然后将数据设置为我们赋予的数值,并且将他指向下一位的指针指向NULL,最后返回我们开辟的结构体指针。
数据尾插(SLTPushBack):在尾插时,我们需要对我们的链表有实质性的作用,所以我们将链表的指针(SLNode** pphead)传输过去。我们先创建一个新的结构体(newnode)并且赋予相应的数值。接着对我们的链表进行判断:如果我们的链表当前为空链表,那我们直接将我们的链表指针指向newnode即可。如果不为空链表,那我们需要进行找尾操作,找到尾巴后我们直接将最后一位的下一个指针指向newnode即可。
打印链表(SLTPrint):我们只需要使用到一个while循环,判断条件为值是否为NULL即可
内存销毁( SLTDexstroy):我们需要将链表逐一遍历,逐一清空。
由此我们可以进行第一次调试,查看我们当前已完成的基础功能函数是否有BUG。
调试无误后,我们进行功能函数的扩展:头插(SLTPushFront),尾删(SLTPopBack),头删(SLTPopFront),查找(SLTFind),删除(SLTErase),位置前面插入(SLTInsert),位置之后插入(SListInsertAfter),位置之后删除(SListEraseAfter)
接下来我们按照顺序逐一完成:
头插(SLTPushFront):我们只需要将新创建的结构体(newnode)的下一个指针设置为我们链表的第一个结构体,并且将链表指针指向newnode即可。
尾删(SLTPopBack):首先,我们需要判断链表中的数据为1个或多个,当数据为单个时,我们直接删除即可,若数据为多个,我们需要进行找尾操作,然后将我们链表最后一位的前一位结构体中指向下一个结构体的指针指向NULL即可。完成这一步操作,我们设置两个指针,一个在前,一个在后,一前一后,当我们的前指针(tail)指向最后一位时,我们的后指针(prev)会指向最后一位的前一位,由此我们可以直接进行操作:
头删(SLTPopFront):我们将链表中的第二位的地址赋值给一个指针(tmp)用于保存地址,然后直接将链表释放,然后将tmp的值给予链表即可。
查找(SLTFind):我们将列表遍历一遍,当出现相同值时,返回地址
删除(SLTErase):我们设置两个指针,前指针(tail),后指针(prev),tail指向链表,prev指向NULL,我们使用循环遍历链表,当出现相同值时,将他的prev的next指针指向tail的下一位(此时tail为要删除值),然后释放tail即可。
位置前面插入(SLTInsert):我们需要先创建一下newnode将值赋予进去,然后使用while循环找到我们要插入的位置的前一位,将前一位的next指针指向newnode,newnode的next指针指向位置即可。
位置之后插入(SListInsertAfter):我们直接将当前位置(pos)的next指针指向newnode,newnode的next指向pos的下一个结构体即可:
位置之后删除(SListEraseAfter):与插入相似,将当前位置(pos)指向下一个数据的下一个数据,然后将pos的下一个数据删除即可
由此我们便完成了一个不带头单项不循环链表,在稍后我还会再开一篇文章写出带头双向循环链表,需要的小同学可以自取哦。
感谢您花费时间观看,欢迎各位大佬指点纠正!!!
代码如下:
//SList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLNDataType;
//Single List
typedef struct SListNode
{
SLNDataType val;
struct SListNode* next;
}SLNode;
SLNode* CreateNode(SLNDataType x);
void SLTPrint(SLNode* phead);
void SLTPushBack(SLNode** pphead , SLNDataType x);
void SLTDexstroy(SLNode** pphead);
void SLTPushFront(SLNode** pphead, SLNDataType x);
void SLTPopBack(SLNode** pphead);
void SLTPopFront(SLNode** pphead);
SLNode* SLTFind(SLNode* phead, SLNDataType x);
//在pos的前面插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);
//删除pos位置
void SLTErase(SLNode** pphead, SLNode* pos);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SLNode* pos, SLNDataType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SLNode* pos);
//SList.c
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
void SLTPrint(SLNode* phead)
{
SLNode* cur = phead;
while (cur!= NULL)
{
printf("%d->", cur->val);
cur = cur->next;
}
printf("NULL\n");
}
SLNode* CreateNode(SLNDataType x)
{
SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
return newnode;
}
void SLTPushBack(SLNode** pphead, SLNDataType x)
{
SLNode* newnode = CreateNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
//找尾
SLNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
} tail->next = newnode;
}
}
void SLTPushFront(SLNode** pphead, SLNDataType x)
{
SLNode* newnode = CreateNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SLTPopBack(SLNode** pphead)
{
assert(pphead);
//一个节点
//多个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//找尾
SLNode* tail = *pphead;
SLNode* prev = NULL;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
prev->next = NULL;
tail = NULL;
}
}
void SLTPopFront(SLNode** pphead)
{
assert(pphead);
//单个和多个
SLNode* tmp = (*pphead)->next;
free(*pphead);
*pphead = tmp;
}
SLNode* SLTFind(SLNode* phead, SLNDataType x)
{
assert(phead);
while (phead->val != x)
{
phead = phead->next;
}
return phead;
}
//在pos的前面插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{
assert(pphead);
SLNode* tail = *pphead;
SLNode* prev = CreateNode(x);
while (tail->next != pos)
{
tail = tail->next;
}
prev->next = pos;
tail->next = prev;
}
//删除pos位置
void SLTErase(SLNode** pphead, SLNode* pos)
{
SLNode* tail = *pphead;
SLNode* prev = NULL;
while (tail != pos)
{
prev = tail;
tail = tail->next;
}
prev->next = tail->next;
free(tail);
tail = NULL;
}
void SLTDexstroy(SLNode** pphead)
{
assert(pphead);
SLNode* tail = *pphead;
SLNode* prev = NULL;
while (tail != NULL)
{
prev = tail;
tail = tail->next;
free(prev);
}
prev = NULL;
}
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SLNode* pos, SLNDataType x)
{
assert(pos);
SLNode* tail = pos-> next;
SLNode* newnode = CreateNode(x);
pos->next = newnode;
newnode->next = tail;
}
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SLNode* pos)
{
SLNode* last = pos->next->next;
free(pos->next);
pos->next = last;
}
//test.c
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
//void Test1()
//{
// SLNode* plist = NULL;
// SLTPushBack(&plist, 1);
// SLTPushBack(&plist, 2);
// SLTPushBack(&plist, 3);
// SLTPushBack(&plist, 4);
// SLTPrint(plist);
//
// SLTPopBack(&plist);
// SLTPrint(plist);
// SLTPopBack(&plist);
// SLTPrint(plist);
// SLTPopBack(&plist);
// SLTPrint(plist);
//}
void TestSLT2()
{
SLNode* plist = NULL;
SLTPushFront(&plist, 1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
SLTPushFront(&plist, 4);
SLTPrint(plist);
SLNode* pos = SLTFind(plist, 3);
SLTInsert(&plist, pos, 5);
SLTErase(&plist, pos);
SLTPrint(plist);
SLTDexstroy(&plist);
}
void TestSLT3()
{
SLNode* plist = NULL;
SLTPushFront(&plist, 1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
SLTPushFront(&plist, 4);
SLTPrint(plist);
SLNode* pos = SLTFind(plist, 3);
SListEraseAfter(pos);
SLTPrint(plist);
SLTDexstroy(&plist);
}
int main()
{
TestSLT3 ();
return 0;
}