一.链表的概述
二.逻辑图
三.代码详解
//1.定义关于链表的结构体
#include <iostream>
#include <stdlib.h>
#include <assert.h>
using namespace std;
typedef int SLTDateType;//适用于不同的数据类型
typedef struct SListNode
{
SLTDateType data;//数据
struct SListNode* next;//结构体指针,存的是下一个节点的地址
}SLTNode;//该结构体的别名
//2.打印链表,用于测试接口
void SListPrint(SLTNode* phead)//链表中的第一个节点
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->",cur->data);
cur = cur->next;//找到下一个节点的地址
}
cout << endl;
}
//3.创建节点,便于数据的插入
SLTNode* BuyListNode(SLTDateType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//新增一个节点
assert(newnode != NULL);
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//4.在尾部插入一个节点
void SListPushBack(SLTNode** pphead,SLTDateType x)//链表本身就是由一个一个指针组成的,要改变指针的内容,只能用指针的指针
{
//原来的尾部指向新的节点,新的节点指向空
//插入一个节点,需将链表扩容,增加一个节点
/*SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
newnode->data = x;
newnode->next = NULL;*/
SLTNode* newnode = BuyListNode(x);
//链表本身为空
if (*pphead == NULL)
{
*pphead = newnode;
}
//链表不为空
else
{
//找到尾节点
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
改变:
1)int--->int*
2)int*--->int**(二级指针)
//5.在头部插入一个节点
void SListPushFront(SLTNode** pphead,SLTDateType x)
{
SLTNode* newnode = BuyListNode(x);
//先把头节点的地址传给新节点,然后头节点就是空的,即新节点
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
newnode->next = *pphead;
*pphead = newnode;
}
}
//6.尾删
//当删除多个节点时,要将数组置为NULL
void SListPopBack(SListNode** pphead)
{
//法1:需要定义两个变量,若只定义一个变量会导致free后,出现野指针问题(tail在free时,已经将空间还给系统,tail=NULL无效;tail前一个空间的指针指向它,属于野指针)
SLTNode* tail = *pphead;
SLTNode* prev = NULL;
assert(*pphead != NULL);//链表为空,则不能进行尾删
if ((*pphead)->next == NULL)
{
//当链表中只有一个节点时
free(*pphead);
*pphead = NULL;
}
else
{
while (tail->next)
{
prev = tail;
tail = tail->next;
}
//直到tail->为空,才结束循环,即tail已经为链表中的最后一个节点
free(tail);
prev->next = NULL;
//法2:少定义一个变量(逻辑同上)
//while (tail->next->next)
//{
// tail = tail->next;
//}
//free(tail->next);
//tail->next = NULL;
}
}
//7.头删
void SListPopFront(SListNode** pphead)
{
//不能直接free掉头节点,头节点会被置成随机值,导致找不到下一个节点
//与尾删对比,头删不需要再定义一个新的变量,来指向前面的一个节点
assert(*pphead != NULL);//当链表不为空时
SListNode* next = (*pphead)->next;//定义一个指针来指向头节点的下一个地址
free(*pphead);
*pphead = next;//完成使头节点指向下一个节点的地址
}
//8.查找
SLTNode* SListFind(SLTNode* phead,SLTDateType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;//查询不到,返回空
}
//9.在pos位置前插入一个节点
void SListInsert(SLTNode** pphead,SLTNode* pos,SLTDateType x)//使用二级指针对链表进行修改
{
//单链表不适合在pos位置前插入数据,会有一定程度的效率损失,在pos后面插入,不需要传入plist
//可配合find函数进行插入
//创建一个新的节点
SLTNode* newnode = BuyListNode(x);
if (*pphead==pos)//否则在头部插入节点时,会报错,因为找不到该位置的前一个节点
{
newnode->next = *pphead;
*pphead = newnode;
}
else
{
//找到pos的前一个位置
SLTNode* posPrev = *pphead;
while (posPrev->next != pos)
{
posPrev = posPrev->next;
}
posPrev->next = newnode;
newnode->next = pos;
}
}
//10.在pos位置后插入一个节点
void SListInsertAfter(SLTNode* pos,SLTDateType x)
{
//要先改newnode后的那个节点,先改pos节点的话,会导致newnode找不到下一个节点的地址
//先创建出一个新的节点
SLTNode* newnode = BuyListNode(x);
newnode->next=pos->next;
pos->next = newnode;
}
//11.删除pos位置前的一个节点
void SListErase(SLTNode** pphead, SLTNode* pos)
{
if (*pphead == pos)//相当于头删
{
*pphead=pos->next;
free(pos);
}
else
{
SLTNode* prev = *pphead;
while (prev->next!=pos)
{
prev = prev->next;
}
prev->next=pos->next;
free(pos);
/*pos = NULL;*///无意义,可直接删除
}
}
//12.删除pos位置后的一个节点
void SListEraseAfter(SLTNode** pphead,SLTNode* pos)
{
//与删除pos前一个位置的节点对比,删除后一个位置时,不用找pos的前一个节点
SLTNode* next = pos->next;//记录要删除的节点
pos->next=next->next;
free(next);
}
//13.销毁链表
void SListDestory(SLTNode** pphead)
{
//不能free掉头节点,会导致找不到下一个节点的地址
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;//先设置一个变量用来保存cur所指的下一个节点
free(cur);
cur = next;
}
*pphead = NULL;//链表的头部要滞空,最后一个不用滞空,在函数里面会自动滞空
}
每写一个接口函数,就要进行一次测试,以下为所有测试函数:
void SListTest()//测试函数
{
SLTNode* plist = NULL;//初始化链表
cout << "尾部插入节点测试:" << endl;
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListPushBack(&plist, 5);
SListPrint(plist);
cout << "---------------" << endl;
cout << "头部插入节点测试:" << endl;
SListPushFront(&plist, 1);
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 4);
SListPushFront(&plist, 5);
SListPrint(plist);
cout << "---------------" << endl;
cout << "尾删测试:" << endl;
SListPopBack(&plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPrint(plist);
cout << "---------------" << endl;
cout << "头删测试:" << endl;
SListPopFront(&plist);
SListPrint(plist);
cout << "---------------" << endl;
//查找测试
//当链表中的多个2时(利用头插法实现模拟)
SListPushFront(&plist,2);
SListPushFront(&plist, 2);
SListPushFront(&plist, 2);
cout << "新的链表为:" << endl;
SListPrint(plist);
cout << "查找测试:" << endl;
SLTNode* pos = SListFind(plist, 2);
int i = 1;
while (pos)//当pos没有移动到最后一个节点时,继续查找
{
cout << "第" << i << "个节点" << ":" << pos->data << endl;
i++;
pos = SListFind(pos->next, 2);
}
//可利用查找函数对链表中的数值进行修改
cout << "---------------" << endl;
cout << "修改数据测试:" << endl;
pos = SListFind(plist,3);
if (pos)
{
pos->data = 29;
}
SListPrint(plist);
cout << "---------------" << endl;
cout << "删除pos位置后的节点测试:" << endl;
SListEraseAfter(&plist,pos);
SListPrint(plist);
cout << "---------------" << endl;
cout << "在pos位置前插入节点测试:" << endl;
SListInsert(&plist, pos, 34);
SListPrint(plist);
cout << "---------------" << endl;
cout << "在pos位置后插入节点测试:" << endl;
SListInsertAfter(pos,7);
SListPrint(plist);
cout << "---------------" << endl;
cout << "删除pos位置前的节点测试:" << endl;
SListErase(&plist,pos);
SListPrint(plist);
//cout << "---------------" << endl;
//cout << "销毁链表测试:" << endl;
//SListDestory(&plist);
//SListPrint(plist);
}
int main()
{
SListTest();
return 0;
}
运行结果如图: