##链表:
用一组任意的存储单元来存放线性表的节点,这组存储单元可以是连续的,也可以是非连续的,甚至是零散分布在内存的任何位置上。
typedef int DataType;
typedef struct Node
{
DataType _data;
struct Node* _next;
}Node, *PNode;
节点包括两个域:
数据域:用来存储节点的值;
指针域:用来存储数据元素的直接后继的地址(或位置)信息。
单链表:只有一个next指针域!
由于线性表中的第一个节点无前驱,所以应设一个头指针H指向第一个节点;
由于线性表最后一个节点没有直接后继,则指定单链表的最后一个节点的指针域为“空”(NULL)。
##无头单链表
// Node.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <assert.h>
typedef int DataType;
typedef struct Node
{
DataType _data;
struct Node* _next;
}Node, *PNode;
// 尾插
void SListPushBack(PNode* pHead, DataType data);
// 尾删
void SListPopBack(PNode* pHead);
// 头插
void SListPushFront(PNode* pHead, DataType data);
// 头删
void SListPopFront(PNode* pHead);
// 在链表中查找值为data的元素,找到后返回值为data的结点
PNode SListFind(PNode pHead, DataType data);
// 在pos位置插入值为data的结点
void SListInsert(PNode* pHead, PNode pos, DataType data);
// 删除pos位置的结点
void SListErase(PNode* pHead, PNode pos);
// 判断链表是否为空
int SListEmpty(PNode pHead);
// 销毁链表
void SListDestroy(PNode* pHead);
// 链表遍历
void PrintNode(PNode pHead);
#include "Node.h"
// 创建新节点函数
PNode BuyNewNode(DataType data)
{
PNode _new = (PNode)malloc(sizeof(Node));
if (NULL == _new)
{
printf("内存申请失败\n");
return NULL;
}
_new->_data = data;
_new->_next = NULL;
return _new;
}
// 尾插操作函数
void SListPushBack(PNode* pHead, DataType data)
{
PNode _new = NULL;
// 参数检验
assert(pHead);
_new = BuyNewNode(data);
if (NULL == _new)
{
return;
}
else
{
// 申请到了新节点
if (NULL == (*pHead))
{
// 如果单链表是空的话,让头指针直接指向新节点
*pHead = _new;
}
else
{
// 链表非空
PNode pCur = NULL;
pCur = (*pHead);
// 找链表的尾
while (!(pCur->_next == NULL))
{
pCur = pCur->_next;
}
// 链接起来
pCur->_next = _new;
}
}
}
// 尾删操作函数
void SListPopBack(PNode* pHead)
{
PNode pCur = NULL; //
PNode pPre = NULL; // 用来保存当前节点前一个节点
// 参数检验
assert(pHead);
pCur = *pHead;
pPre = pCur;
// 找单链表的尾,并用pPre保存前一个节点
while (!(pCur->_next == NULL))
{
pPre = pCur;
pCur = pCur->_next;
}
pPre->_next = NULL; // 找到尾之后直接让pPre->_next赋空
free(pCur); // 释放掉从单链表摘下的节点
pCur = NULL; // 避免野指针
}
// 头插操作函数
void SListPushFront(PNode* pHead, DataType data)
{
PNode _new = NULL;
// 参数检验
assert(pHead);
_new = BuyNewNode(data);
if (NULL == _new)
{
return;
}
else
{
// 成功申请到新节点
if (*pHead == NULL)
{
// 空链表 直接让头指针指向新节点
*pHead = BuyNewNode(data);
return;
}
// 链表非空 进行头插
_new = BuyNewNode(data);
_new->_next = *pHead;
*pHead = _new;// 修改头指针
}
}
// 头删操作函数
void SListPopFront(PNode* pHead)
{
PNode pCur = NULL;
// 参数检验
assert(pHead);
pCur = *pHead;
if(NULL == pCur)
{
printf("链表为空\n");
return;
}
// 单链表非空
*pHead = pCur->_next; // 让头指针指向头的_next域
free(pCur); // 释放pCur
pCur = NULL; // 避免野指针
}
// 在链表中查找值为data的元素,找到后返回值为data的结点
PNode SListFind(PNode pHead, DataType data)
{
PNode pCur = NULL;
// 参数检验
assert(pHead);
pCur = pHead;
// 遍历单链表 查找值为data的元素
while (pCur != NULL)
{
if (pCur->_data == data)
{
return pCur; // 找到了返回
}
pCur = pCur->_next; // pCur后移
}
return NULL; // 找不到返回NULL
}
// 在pos位置插入值为data的结点
void SListInsert(PNode* pHead, PNode pos, DataType data)
{
PNode pPre = NULL;
PNode pCur = NULL;
PNode _new = NULL;
// 参数检验
assert(pHead);
pCur = *pHead;
pPre = pCur;
if (pCur->_next == NULL)
{
// 链表为空
if (pCur == pos)
{
// 头插
_new = BuyNewNode(data);
if (_new == NULL)
{
return;
}
else
{
pHead = &_new;
}
}
else
{
// 链表位置不合法
printf("pos位置不合法\n");
return;
}
}
// 链表不为空
while (pCur != NULL)
{
if (pos == pCur)
{
_new = BuyNewNode(data);
if (_new == NULL)
{
return;
}
else
{
_new->_next = pPre->_next;
pPre->_next = _new;
}
return;
}
else
{
pPre = pCur;
pCur = pCur->_next;
}
}
// 已经遍历完了单链表 仍没找到pos位置
printf("pos位置不合法\n");
return;
}
// 删除pos位置的结点
void SListErase(PNode* pHead, PNode pos)
{
PNode pCur = NULL;
PNode pPre = NULL;
// 参数检验
assert(pHead);
pCur = *pHead;
pPre = pCur;
if (pCur == NULL)
{
printf("链表为空!!!\n");
return;
}
while (pCur != NULL)
{
if (pos == pCur)
{
pPre->_next = pCur->_next;
free(pCur);
pCur = NULL;
return;
}
else
{
pPre = pCur;
pCur = pCur->_next;
}
}
// 已遍历单链表
printf("pos位置不合法\n");
return;
}
// 判断链表是否为空 为空返回1 不为空返回0
int SListEmpty(PNode pHead)
{
if (pHead == NULL)
{
return 1;
}
return 0;
}
// 销毁链表
void SListDestroy(PNode* pHead)
{
PNode p = NULL;
PNode q = NULL;
// 参数检验
assert(pHead);
p = *pHead;
while (p != 0)
{
// 不是链尾时
q = p->_next; // 让q指向头节点的后继节点
free(p);
p = q; // 让p和q都指向后继节点
}
*pHead = NULL; // 将头指针赋空
}
// 链表遍历
void PrintNode(PNode pHead)
{
PNode pCur = NULL;
// 参数检验
assert(pHead);
pCur = pHead;
printf("单链表中所有元素:");
while (pCur != NULL)
{
printf("%d->", pCur->_data);
pCur = pCur->_next;
}
printf("NULL\n");
}
1.测试单链表尾插、尾删操作,遍历单链表
#include "Node.h"
void TestNodePushBack_PopBack()
{
PNode pHead = NULL; // 头指针,并初始化
SListPushBack(&pHead, 1); // 尾插元素
SListPushBack(&pHead, 2);
SListPushBack(&pHead, 3);
SListPushBack(&pHead, 4);
PrintNode(pHead); // 遍历打印单链表
SListPopBack(&pHead); // 尾删一个元素
PrintNode(pHead); // 遍历打印单链表
SListDestroy(&pHead); // 销毁单链表
}
// 主函数
int main()
{
TestNodePushBack_PopBack(); // 调用测试函数
system("pause");
return 0;
}
测试结果:
2.测试单链表头插、头删操作,遍历单链表
#include "Node.h"
void TestNodePushFront_PopFront()
{
PNode pHead = NULL; // 头指针,并初始化
SListPushFront(&pHead, 4); // 头插元素
SListPushFront(&pHead, 3);
SListPushFront(&pHead, 2);
SListPushFront(&pHead, 1);
PrintNode(pHead); // 遍历打印单链表
SListPopFront(&pHead); // 头删一个元素
PrintNode(pHead); // 遍历打印单链表
SListDestroy(&pHead); // 销毁单链表
}
// 主函数
int main()
{
TestNodePushFront_PopFront();
system("pause");
return 0;
}
测试结果:
3.测试单链表查找、指定位置插入、指定位置删除操作
#include "Node.h"
void TestNodeFind_Insert_Erase()
{
PNode pHead = NULL;
PNode ret_Find = NULL;
SListPushBack(&pHead, 1);
SListPushBack(&pHead, 2);
SListPushBack(&pHead, 4);
PrintNode(pHead);
ret_Find = SListFind(pHead, 4);
SListInsert(&pHead, ret_Find, 3);
PrintNode(pHead);
SListErase(&pHead, ret_Find);
PrintNode(pHead);
SListDestroy(&pHead); // 销毁单链表
}
// 主函数
int main()
{
TestNodeFind_Insert_Erase(); // 调用测试函数
system("pause");
return 0;
}
测试结果:
4. 最后说一下单链表的销毁,为了避免内存泄漏,在使用完单链表之后一点要销毁。
请各位大佬点评,有错的地方一定要指正,谢谢