在学习数据结构中,紧接着顺序表接触的就是单链表,那么单链表是什么呢?
1.什么是单链表?
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。
2.结构?
链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。以“结点的序列”表示线性表称作线性链表(单链表)。单链表是链式存取的结构,为找第 i 个数据元素,必须先找到第 i-1 个数据元素。
把单链表的特征说清楚后,我们就开始实现单链表的功能了。最基础的功能是实现它的增加、删除、查找等,因为它的线性特征,在增加的时候会存在头插、尾插,同样删除也一样。
SListNode.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int DataType;
typedef struct SListNode
{
DataType data;
struct SListNode* next;
}SListNode;
void InitSList(SListNode*& pHead); //初始化
SListNode* BuyNode(DataType data); //开辟空间
void Print(SListNode*& pHead); //打印
void Destory(SListNode*& pHead); //销毁
void PushBack(SListNode*& pHead, DataType data); //尾插
void PopBack(SListNode*& pHead); //尾删
void PushFront(SListNode*& pHead, DataType data); //头插
void PopFront(SListNode*& pHead); //头删
void Insert(SListNode*& pos,DataType data);//在某个位置插入某个数据
void Erase(SListNode*& pHead,SListNode* pos);//删除某个节点的数据
void Remove(SListNode*& pHead,DataType data); //删除某个数据所在的节点
SListNode* Find(SListNode*& pHead,DataType data); //查找某个数据所在的位置
SListNode.c
#include"SListNode.h"
void InitSList(SListNode*& pHead) //初始化
{
pHead = NULL;
}
SListNode* BuyNode(DataType data) //开辟空间
{
SListNode* node = (SListNode*)malloc(sizeof(SListNode));
assert(node);
node->data = data;
node->next = NULL;
return node;
}
void Print(SListNode*& pHead) //打印
{
SListNode* cur = pHead;
if (cur == NULL)
printf("null\n");
while (cur)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
void PushBack(SListNode*& pHead, DataType data) //尾插
{
if (pHead == NULL)
pHead = BuyNode(data);
else
{
SListNode* cur = pHead;
while (cur->next)
{
cur = cur->next;
}
cur->next = BuyNode(data);
}
}
void PopBack(SListNode*& pHead) //尾删
{
assert(pHead);
if (pHead->next == NULL)
{
free(pHead);
pHead = NULL;
}
else
{
SListNode* cur = pHead;
SListNode* pcur = cur;
while(cur->next)
{
pcur = cur;
cur = cur->next;
}
free(cur);
cur = NULL;
pcur->next = NULL;
}
}
void PushFront(SListNode*& pHead, DataType data) //头插
{
if (pHead == NULL)
pHead=BuyNode(data);
else
{
SListNode* newHead = BuyNode(data);
newHead->next = pHead;
pHead = newHead;
}
}
void PopFront(SListNode*& pHead) //头删
{
if (pHead == NULL) //pHead为空
return;
else if (pHead->next == NULL) //pHead->next为空
{
free(pHead);
pHead = NULL;
}
else //正常的删除
{
SListNode* cur = pHead;
pHead = pHead->next;
free(cur);
}
}
void Insert(SListNode*& pos, DataType data) //在某个位置插入某个数据
{
if (NULL == pos)
return;
SListNode* newNode = BuyNode(data);
newNode->next = pos->next;
pos->next = newNode;
}
void Erase(SListNode*& pHead, SListNode* pos) //删除某个节点的数据
{
assert(pHead&&pos);
if (pos == pHead)
{
SListNode* tmp = pHead;
pHead = pHead->next;
free(tmp);
tmp = NULL;
}
else
{
SListNode* cur = pHead;
while (cur->next != pos)
{
cur = cur->next;
}
cur->next = pos->next;
free(pos);
pos = NULL;
}
}
void Remove(SListNode*& pHead, DataType data) //删除某个数据所在的节点
{
assert(pHead);
SListNode* pos = Find(pHead, data);
if (pos == pHead)
{
SListNode* tmp = pHead;
pHead = pHead->next;
free(tmp);
tmp = NULL;
}
else
{
SListNode* cur = pHead;
while (cur->next!=pos)
{
cur = cur->next;
}
cur->next = pos->next;
free(pos);
pos = NULL;
}
}
SListNode* Find(SListNode*& pHead, DataType data) //查找某个数据所在的位置
{
SListNode* cur = pHead;
assert(pHead);
while(cur)
{
if(cur->data == data)
return cur;
cur = cur->next;
}
return NULL;
}
void Destory(SListNode*& pHead) //销毁
{
assert(pHead);
free(pHead);
pHead = NULL;
}
测试函数
#include"SListNode.h"
void Test1() //尾插尾删
{
SListNode* Slistnode;
InitSList(Slistnode);
PushBack(Slistnode, 1);
PushBack(Slistnode, 2);
PushBack(Slistnode, 3);
PushBack(Slistnode, 4);
PushBack(Slistnode, 5);
Print(Slistnode);
PopBack(Slistnode);
PopBack(Slistnode);
PopBack(Slistnode);
PopBack(Slistnode);
Print(Slistnode);
}
void Test2() //头插头删
{
SListNode* Slistnode;
InitSList(Slistnode);
PushFront(Slistnode, 1);
PushFront(Slistnode, 2);
PushFront(Slistnode, 3);
PushFront(Slistnode, 4);
PushFront(Slistnode, 5);
Print(Slistnode);
PopFront(Slistnode);
PopFront(Slistnode);
PopFront(Slistnode);
PopFront(Slistnode);
Print(Slistnode);
}
void Test3() //任意位置的插入删除
{
SListNode* Slistnode;
InitSList(Slistnode);
PushFront(Slistnode, 1);
PushFront(Slistnode, 2);
PushFront(Slistnode, 3);
PushFront(Slistnode, 4);
PushFront(Slistnode, 5);
Print(Slistnode);
SListNode* pos1 = Find(Slistnode, 3);
Insert(pos1, 7);
Print(Slistnode);
SListNode* pos2 = Find(Slistnode, 4);
Erase(Slistnode, pos2);
Print(Slistnode);
Remove(Slistnode, 1);
Print(Slistnode);
Destory(Slistnode);
Print(Slistnode);
}
int main()
{
Test1();
Test2();
Test3();
system("pause");
}
总结:整体代码部分如上,在这里需要注意几点:
1.对链表进行插入删除时,一定要分析可能出现的情况,比如头插时可能三种情况,当链表为空;当链表指向的下一个为空;正常的头插。只有将情况分析清楚,你才能按照逻辑去写代码。
2.在实现链表的时候,代码都差不多,但很多细节需要我们去注意。该断言的时候可以用assert,不需要的部分进行判断即可。
3.在测试较多的功能时,可以分步分模块测试,哪里有问题也比较好调试。