链表:一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称为存储单元为一个节点。
单链表的存储结构
typedef int DataType;
typedef struct ListNode
{
DataType _data; //当前节点中所保存的元素
struct ListNode* _pNext; //指向链表中下一个结点
}SLinkList, SListNode;
链表的分类
单链表的基本操作
void PrintSLinkList(SLinkList* pHead);//打印
void InitSLinkList(SLinkList** ppHead);//初始化
void DestroySLinkList(SLinkList** ppHead);//销毁
void PushBack(SLinkList** ppHead, DataType data);//尾部插入
void PopBack(SLinkList** ppHead);//尾部删除
void PushFront(SLinkList** ppHead, DataType data);//头部插入
void PopFront(SLinkList** ppHead);//头部删除
SLinkList* Find(SLinkList* pHead, DataType data);//查找元素的位置
void Delete(SLinkList** ppHead, SLinkList* pos);//指定位置删除元素
void Revome(SLinkList** ppHead, DataType data);//删除第一个出现的指定元素
void RemoveAll(SLinkList** ppHead, DataType data);//删除出现的所有元素
int GetLinkListLength(SLinkList* pHead);//计算单链表的长度
void BubbleSort(SLinkList** ppHead);//冒泡排序
0.打印
//打印
void PrintSLinkList(SLinkList* pHead)
{
SLinkList* cur = pHead;
while (cur)
{
printf("%d -> ", cur->_data);
cur = cur->_pNext;
}
printf("NULL\n");
}
1.初始化
初始化就是让指向单链表的指针指向空,因为要改变指针的志向所以需要传指针的地址
//初始化
void InitSLinkList(SLinkList** ppHead)
{
assert(ppHead);
*ppHead = NULL;
}
2.销毁
因为单链表都是用malloc开辟的内存空间,所以每次使用结束都要将其释放掉,所以就有一个销毁函数,将所有的结点释放掉,并让指向链表的指针指向空。
//销毁
void DestroySLinkList(SLinkList** ppHead)
{
SLinkList* cur = *ppHead;
SLinkList* del = NULL;
assert(ppHead);
while (cur)
{
del = cur;
cur = cur->_pNext;
free(del);
del = NULL;
}
*ppHead = NULL;
}
节点的函数
由于后面的插入会用malloc开辟空间,制作节点,所以在这里提前将我封装的函数写下来
SLinkList* BuyNode(DataType data)
{
SLinkList* p = (SLinkList*)malloc(sizeof(SLinkList));
//malloc开辟的空间一定要判断成功还是失败
if (p == NULL)
{
perror("BuyNode::malloc");
return NULL;
}
//空间开辟成功,将赋给节点中的数据域,将指针域赋为空指针
p->_data = data;
p->_pNext = NULL;
//返回节点的地址
return p;
}
3.尾部插入
1.空链表:直接连接,不需要遍历
2.非空链表:找到最后一个节点,连接
尾部插入,顾名思义就要知道它的尾部在哪里,所以,我们要遍历取找它的尾部,单恋表的最后一个元素没有指向任何元素,所以它指向小孩一个节点的指针指向空,找到后插入。单链表有时是一个空表,需要改变指针的值,所以需要传指针的地址。
//尾部插入
void PushBack(SLinkList** ppHead, DataType data)
{
SLinkList* NewNode = NULL;
SLinkList* cur = *ppHead;
assert(ppHead);
NewNode = BuyNode(data);
//1.空链表:直接连接
if (*ppHead == NULL)
{
*ppHead = NewNode;
}
//2.非空链表
else
{
//a.找最后一个结点
while (cur->_pNext)
{
cur = cur->_pNext;
}
//b.连接
cur->_pNext = NewNode;
}
}
4.尾部删除
1.空链表:不能删除,直接结束
2.只有一个节点:删除,将头指针置空
3.两个及以上的节点:找到最后一个节点并将倒数第二个节点置空
//尾部删除
void PopBack(SLinkList** ppHead)
{
SLinkList* cur = *ppHead;
assert(ppHead);
//1.空链表:不能删除,直接结束
if (*ppHead == NULL)
{
printf("单链表为空,不能删除!!!\n");
return;
}
//2.只有一个元素:删除,并将头结点置空
else if ((*ppHead)->_pNext == NULL)
{
free((*ppHead)->_pNext);
*ppHead = NULL;
}
//3.两个及以上的元素:找最后一个元素,删除
else
{
while (cur->_pNext->_pNext != NULL)
{
cur = cur->_pNext;
}
free(cur->_pNext);
cur->_pNext = NULL;
}
}
5.头部插入
单链表的头部插入都需要改变指向链表指针的指向,所以不分情况可以直接操作
//头部插入
void PushFront(SLinkList** ppHead, DataType data)
{
SLinkList* NewNode = NULL;
assert(ppHead);
NewNode = BuyNode(data);
NewNode->_pNext = *ppHead;
*ppHead = NewNode;
}
6.头部删除
单链表为空,不能随其进行删除操作,所以需要分别其为空链表还是非空链表
//头部删除
void PopFront(SLinkList** ppHead)
{
SLinkList* cur = *ppHead;
assert(ppHead);
//1.空链表,不能删除
if (*ppHead == NULL)
{
printf("单链表为空,不能删除!!!\n");
return;
}
//2.非空链表
*ppHead = cur->_pNext;
free(cur);
cur = NULL;
}
7.查找元素的位置
从单链表的第一个节点开始,判断当前节点的数据域的值是否与给定的值相等。若相等,则返回该节点的地址,否则继续比较下一个节点,直到链表结束。若节点不存在,则返回空。
//查找元素的位置
SLinkList* Find(SLinkList* pHead, DataType data)
{
SLinkList* cur = pHead;
while (cur != NULL)
{
if (cur->_data == data)
break;
cur = cur->_pNext;
}
return cur; //有可能返回空值或所查元素的地址
}
8.指定位置删除元素
遍历单链表,由于要删除指定位置的元素,需要将前一个节点的next指针域指向被删除节点的下一个节点。
//指定位置删除元素
void Delete(SLinkList** ppHead, SLinkList* pos)
{
SLinkList* cur = *ppHead;
SLinkList* del = NULL;
assert(ppHead);
//空链表
if (*ppHead == NULL)
return;
//要删除位置元素为第一个节点
if (*ppHead == pos)
PopFront(ppHead);
while (cur != NULL && cur->_pNext != pos)
{
cur = cur->_pNext;
}
if (cur != NULL)
{
cur->_pNext = cur->_pNext->_pNext;
free(pos);
pos = NULL;
}
}
9.删除第一个出现的元素
找到第一个指定元素出现的元素的前一个节点,指向被删除元素的下一个节点。因为是使用malloc开辟的空间,所以要用free()释放掉。
//删除第一个出现的指定元素
void Revome(SLinkList** ppHead, DataType data)
{
SLinkList* cur = *ppHead;
SLinkList* pre = NULL;
assert(ppHead);
//链表为空
if (*ppHead == NULL)
PopFront(ppHead);
//链表非空
while (cur != NULL)
{
if (cur->_data == data)
{
//首节点为所删元素
if (pre == NULL)
PopFront(ppHead); //头删法
else
{
pre->_pNext = cur->_pNext;
free(cur);
cur = NULL;
}
return;
}
pre = cur;
cur = cur->_pNext;
}
}
10.删除出现的所有元素
//删除出现的所有元素
void RemoveAll(SLinkList** ppHead, DataType data)
{
SLinkList* cur = *ppHead;
SLinkList* pre = NULL;
SLinkList* del = NULL;
assert(ppHead);
//空链表
if (*ppHead == NULL)
return;
//非空链表
while (cur != NULL)
{
if (cur->_data == data)
{
//要删除的元素是第一个节点
if (pre == NULL)
{
del = cur;
cur = cur->_pNext;
*ppHead = cur;
}
else
{
//用del指定需要被删除的元素,将被删除元素从链表上取下来
del = cur;
cur = cur->_pNext;
pre->_pNext = cur;
}
//删除需要删除的结点
free(del);
del = NULL;
}
else
{
pre = cur;
cur = cur->_pNext;
}
}
}
11.计算单链表的长度
利用count,对链表的结点进行计数。
//计算单链表的长度
int GetLinkListLength(SLinkList* pHead)
{
SLinkList* cur = pHead;
int len = 0;
//空链表
if (pHead == NULL)
return 0;
//非空链表
while (cur != NULL)
{
len++;
cur = cur->_pNext;
}
return len;
}
11.冒泡排序
外循环为链表的长度-1次,设置一个尾结点,外循环每执行一次,尾结点向前移动一步。
//冒泡排序
void BubbleSort(SLinkList** ppHead)
{
SLinkList* pTail = NULL; //标记:未排序元素的最后一个元素
SLinkList* pPre = NULL;
SLinkList* pCur = NULL;
assert(ppHead);
//趟数
while (pTail != *ppHead)
{
pPre = *ppHead;
pCur = *ppHead;
//单趟
while (pCur != pTail)
{
//升序
if (pPre->_data > pCur->_data)
{
DataType tmp = pPre->_data;
pPre->_data = pCur->_data;
pCur->_data = tmp;
}
pPre = pCur;
pCur = pCur->_pNext;
}
pTail = pPre;
}
}
12.选择排序
void SelectsortLinkList(SLinkList *pHead)
{
SLinkList *max = pHead; //最大值指针
SLinkList *tail = pHead;
SLinkList *cur = NULL;
DataType node = 0;
int length = GetLinkListLength(pHead) - 1;
//尾结点,tail指向空
while (tail != NULL)
{
tail = tail->_pNext;
}
while (length--)
{
max = pHead;
cur = pHead->_pNext;
while (cur->_pNext != tail)
{
//找到链表的最大值
if (cur->_data > max->_data)
{
max = cur;
}
cur = cur->_pNext;
}
//比较链表尾结点是不是最大值
if (cur->_data > max->_data)
{
max = cur;
}
if (max != cur)
{
//交换
node = max->_data;
max->_data = cur->_data;
cur->_data = node;
}
tail = cur;
}
}