一.链表的概述

二.逻辑图

三.代码详解
//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;
} 
运行结果如图:





















