顺序表的理解操作:点击打开链接
单链表:一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称存储单元为一个节点。(每个结点中只包含一个指针域)
链式的存储结构的特点:用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)
单链表的结构:
1.对于任意一个数据元素a(i)来说,储存本身的数据.(这个域叫数据域)
2.存储一个下一个(后继)数据元素的信息(pNext)(这个域叫指针域)
这两部分信息组成数据元素a(i)的存储映像,称为结点。
![](https://i-blog.csdnimg.cn/blog_migrate/27d29727dcc1bff6a2b5d3868b1e82b2.png)
单链表的结构:
typedef int DataType;
typedef struct SNode
{
DataType data; //保存本身的信息
struct SNode *_pNext;//保存下个节点的地址
}SNode, *PNode;
代码部分:
list.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int DataType;
typedef struct SNode
{
DataType data; //保存本身的信息
struct SNode *_pNext;//保存下个节点的地址
}SNode, *PNode;
void SListInit(PNode* pHead);// 链表的初始化
void SListPushBack(PNode* pHead, DataType data);// 尾插
void SListPopBack(PNode* pHead);// 尾删
void SListPushFront(PNode* pHead, DataType data);// 头插
void SListPopFront(PNode* pHead);// 头删
PNode SListFind(PNode pHead, DataType data);// 在链表中查找值为data的元素,找到后返回值为data的结点
void SListInsert(PNode* pHead, PNode pos, DataType data);// 在pos位置插入值为data的结点
void SListErase(PNode* pHead, PNode pos);// 删除pos位置的结点
int SListSize(PNode pHead);// 获取链表中节点总数
int SListEmpty(PNode pHead);// 判断链表是否为空
void SListDestroy(PNode* pHead);// 销毁链表
void PrintfList(PNode pHead);//打印链表
list.c
#include "list.h"
//初始化就是将链表头指针赋空,
//注意这里要传头指针的地址才能改变头指针的值
void SListInit(PNode* pHead)
{
assert(pHead);//判断链表是否存在
*pHead = NULL;
}
//创建新节点
PNode BuyNewNode(DataType data)
{
PNode ptr = NULL;
ptr = (PNode)malloc(sizeof(DataType)+sizeof(PNode));
if (NULL == ptr)
{
printf("BuyNewNode failed!!!\n");
return NULL;
}
//申请成功
ptr->data = data;
ptr->_pNext = NULL;
return ptr;
}
//打印链表
void PrintfList(PNode pHead)
{
PNode ptr = pHead;
if (pHead == NULL)
return;
while (ptr)
{
printf("%d-->", ptr->data);
ptr = ptr->_pNext;
}
printf("NULL\n");
}
//尾插
void SListPushBack(PNode* pHead, DataType data)
{
PNode pCur = NULL;
assert(pHead);//判断链表是否存在
if (*pHead == NULL)//判断链表是否为空
{
*pHead = BuyNewNode(data);//空就让头节点指向新节点
return;
}
//1.先找到链表最后一个节点
pCur = *pHead;
while (pCur->_pNext)
{
pCur = pCur->_pNext;//朝后走一步
}
pCur->_pNext = BuyNewNode(data);
}
//尾删
void SListPopBack(PNode* pHead)
{
assert(pHead);
PNode pPre = *pHead;
PNode pCur = *pHead;
if (*pHead == NULL)
{
printf("链表已空,删除失败!!!\n");
return;
}
//1.只有一个节点
if (pCur->_pNext == NULL)
{
free(*pHead);
*pHead = NULL;
return;
}
//2.有一个以上节点
while (pCur->_pNext)
{
pPre = pCur;//因为要找到倒数第二个节点,所以每走一步之前先保存
pCur = pCur->_pNext;
}
free(pPre->_pNext);
pPre->_pNext = NULL;
}
//头插
void SListPushFront(PNode* pHead, DataType data)
{
PNode ptr = NULL;
assert(pHead);
//1.链表为空
if (NULL == *pHead)
{
*pHead = BuyNewNode(data);
return;
}
//2.链表不为空
ptr = *pHead;//先保存第一个节点地址
*pHead = BuyNewNode(data);//头指针指向新节点
(*pHead)->_pNext = ptr;//新节点的_pNext指向原来的头节点
}
//头删
void SListPopFront(PNode* pHead)// 头删
{
assert(pHead);
PNode ptr = NULL;
if (NULL == pHead)
{
printf("链表已空,删除失败!!!\n");
return;
}
//1.只有一个节点
if ((*pHead)->_pNext == NULL)
{
free(*pHead);
*pHead = NULL;
return;
}
//2.有一个以上的节点
ptr = *pHead;//先保存头节点地址
*pHead = (*pHead)->_pNext;//头指针朝后走一步;
free(ptr);//释放原头节点空间
}
//查找为data的节点
PNode SListFind(PNode pHead, DataType data)
{
PNode ptr = pHead;
if (NULL == pHead)
{
printf("对不起,为空链表!!!\n");
return NULL;
}
while ((ptr) && (ptr->data != data))//ptr中的data不是要找的且不为空就进入循环
{
ptr = ptr->_pNext;
}
//1.ptr为空,证明找不到
if (NULL == ptr)
{
printf("对不起,找不到!!!\n");
return NULL;
}
else
return ptr;
}
int SListSize(PNode pHead)
{
int count = 0;
while (pHead)
{
pHead = pHead->_pNext;
count++;
}
return count;
}
void SListInsert(PNode* pHead, PNode pos, DataType data)
{
assert(pHead);
//1.链表为空
if (NULL == *pHead)
{
//1.如果链表为空,插入位置与头节点地址不相同,那么位置不合法
if (pos == *pHead)
{
*pHead = BuyNewNode(data);
return;
}
else
{
printf("位置不合法,插入失败!!!\n");
return;
}
}
//2.链表不为空
//(1)头插:pos与头节点地址相等
if (pos == *pHead)
SListPushFront(pHead, data);
//(2)pos与头节点位置不相等
else
{
PNode pPre = *pHead;
PNode pCur = *pHead;
while ((pCur) && (pCur->_pNext != pos))
{
pCur = pCur->_pNext;
}
if (pCur == NULL)
{
printf("对不起,找不到该节点!!!\n");
return;
}
else
{
pCur->_pNext = BuyNewNode(data);//插入新节点
pCur->_pNext->_pNext = pos;//新节点链接pos处的节点
pos = pCur->_pNext;//pos指向新节点
}
}
}
// 删除pos位置的结点
void SListErase(PNode* pHead, PNode pos)
{
assert(pHead);
PNode ptr = *pHead;
if (NULL == *pHead)
{
printf("链表已空,删除失败!!!\n");
return;
}
//1.只有一个节点
if ((*pHead)->_pNext == NULL)
{
if (*pHead == pos)
{
free(pos);
*pHead = NULL;
return;
}
else
{
printf("位置不合法,删除失败!!!\n");
return;
}
}
//2.有一个以上节点
//(1)如果pos是头节点
if (pos == *pHead)
{
*pHead = (*pHead)->_pNext;
free(pos);
return;
}
//(2)不是头结点
while ((ptr) && (ptr->_pNext != pos) && (pos))
{
ptr = ptr->_pNext;
}
if ((ptr == NULL) || (pos == NULL))
{
printf("节点不存在,删除失败!!!\n");
return;
}
else
{
ptr->_pNext = pos->_pNext;//链接起pos的下个节点
free(pos);
}
}
// 判断链表是否为空
int SListEmpty(PNode pHead)
{
if (pHead)
return 1;
return 0;
}
// 销毁链表
void SListDestroy(PNode* pHead)
{
assert(pHead);
PNode ptr = *pHead;
PNode pMsg = *pHead;
while (ptr)
{
ptr = ptr->_pNext;//ptr朝后走
free(pMsg);//释放pMsg指向的空间
if (ptr)
pMsg = ptr;//pMsg跟着ptr走
}
*pHead = NULL;
}
test.c
#include <windows.h>
void TestList()
{
SNode SList;
PNode pHead = &SList;
SListInit(&pHead);// 链表的初始化
//SListEmpty(pHead);// 判断链表是否为空
//SListInsert(&pHead, pHead, 40);
//PrintfList(pHead);
//SListPushFront(&pHead, 0);
//PrintfList(pHead);
SListPushBack(&pHead, 5);//尾插
SListPushBack(&pHead, 10);
SListPushBack(&pHead, 3);
PrintfList(pHead);
//SListPopBack(&pHead);//尾删
//PrintfList(pHead);
//SListPushFront(&pHead, 0);//头插
PrintfList(pHead);
SListPopFront(&pHead);//头删
//PrintfList(pHead);
在链表中查找值为data的元素,找到后返回值为data的结点
//SListFind(pHead, 3);
//SListSize(pHead);// 获取链表中节点总数
//SListInsert(&pHead, pHead->_pNext->_pNext, 20);// 在pos位置插入值为data的结点
//PrintfList(pHead);
//SListErase(&pHead, pHead->_pNext->_pNext);// 删除pos位置的结点
//PrintfList(pHead);
//SListDestroy(&pHead);// 销毁链表
}
int main()
{
TestList();
system("pause");
return 0;
}