C语言实现单链表

1、用于存放声明的头文件

#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include <stdio.h>
#include <assert.h>
#include <malloc.h>

typedef int DataType;

typedef struct SListNode
{
    DataType data;                    //链表中存放的数据
    struct SListNode *pNextNode;      //指向下一节点的指针
}SListNode, *PSListNode;


// 初始化单链表(对于无头结点单链表,该函数没有意义)
void InitList(PSListNode* pHead);
// 销毁单链表
void DestroyList(PSListNode* pHead);
// 尾插
void PushBack(PSListNode* pHead, DataType data);
// 尾出
void PopBack(PSListNode* pHead);
// 头插
void PushFront(PSListNode* pHead, DataType data);
// 头出
void PopFront(PSListNode* pHead);
// 在链表中查找元素data
PSListNode Find(PSListNode pHead, DataType data);
// 删除pos位置的结点(注意不能用那种替换形式)
void  Erase(PSListNode* pHead, PSListNode pos);
// 在链表的pos位置插入元素data
void  Insert(PSListNode* pHead, PSListNode pos, DataType data);
//打印链表存放的数据
void PrintList(PSListNode pHead);

2、用于实现声明的.c文件

#include "SListNode.h"

//这儿的参数是二级指针,因为要对结构体指针的内容进行修改,所以必须传该指针的地址(即一个二级指针)
//传一级指针的话相当于是值传递,不会对实参有改变
void InitList(PSListNode* pHead)
{
    assert(pHead);
    *pHead = NULL;
}

PSListNode ByeNode(DataType data)
{
    PSListNode pNewNode = (PSListNode)malloc(sizeof(struct SListNode));
    if (NULL != pNewNode)
    {
        pNewNode->data = data;
        //注意使开辟的新节点的指向为空
        pNewNode->pNextNode = NULL;
    }
    return pNewNode;
}

void PushBack(PSListNode* pHead, DataType data)
{
    PSListNode pNode = NULL;
    PSListNode pNewNode = NULL;
    assert(pHead);
    if (NULL == (*pHead))
    {
        //此时可以不对ByeNode函数是否成功开辟空间做检测,因为即使它没有开辟成功空间,
        //那么使头结点头结点等于NULL逻辑一样正确
        *pHead = ByeNode(data);
    }
    else
    {
        pNode = *pHead;
        //找到尾节点
        while (NULL != pNode->pNextNode)
        {
            //保存尾节点
            pNode = pNode->pNextNode;
        }
        pNewNode = ByeNode(data);
        //当开辟空间失败,说明向链表里插入了一个空指针
        pNode->pNextNode = pNewNode;
    }
}

void PopBack(PSListNode* pHead)
{
    PSListNode pPerNode = NULL;
    PSListNode pCurNode = NULL;
    assert(pHead);
    if (NULL == (*pHead))
    {
        printf("链表中没有数据节点!\n");
    }
    else
    {
        pCurNode = *pHead;
        //注意循环结束的条件
        while (NULL != pCurNode->pNextNode)
        {
            pPerNode = pCurNode;
            pCurNode = pCurNode->pNextNode;
        }
        //把要删除的结点从链表中断开(让该节点的上一个结点指向该结点的下一个结点)
        pPerNode->pNextNode = NULL;
        //删除该结点
        free(pCurNode);
        pCurNode = NULL;

    }
}

void PushFront(PSListNode* pHead, DataType data)
{
    assert(pHead);
    if (NULL == (*pHead))
    {
        //此时可以不对ByeNode函数是否成功开辟空间做检测,因为即使它没有开辟成功空间,
        //那么使头结点头结点等于NULL逻辑一样正确
        *pHead = ByeNode(data);
    }
    else
    {
        PSListNode pNewNode = ByeNode(data);
        if (NULL == pNewNode)
        {
            printf("开辟节点空间失败!\n");
        }
        else
        {
            //使指向头结点的指针指向新开辟的节点,新开辟的节点指向链表中原来的头结点
            pNewNode->pNextNode = *pHead;
            *pHead = pNewNode;
        }
    }
}

void PopFront(PSListNode* pHead)
{
    assert(pHead);
    if (NULL == (*pHead))
    {
        printf("链表中没有数据节点!\n");
    }
    else
    {
        PSListNode pNode = *pHead;
        //直接使头结点指向链表中第三个节点,即可做到头删的目的
        *pHead = (*pHead)->pNextNode;
        free(pNode);
        pNode = NULL;
    }
}

PSListNode Find(PSListNode pHead, DataType data)
{
    PSListNode pNode = pHead;
    while (NULL != pNode)
    {
        if (pNode->data == data)
        {
            return pNode;
        }
        else
        {

            pNode = pNode->pNextNode;
        }
    }
    //没有找到对应的结点
    return NULL;
}

void  Erase(PSListNode* pHead, PSListNode pos)
{
    //更简单的方法(pos指向的结点在链表中):
    //直接交换pos指向的节点和pos指向的节点的下一个结点的数据,然后使pos指向的节点的下一个结点为它的下下一个结点
    PSListNode pCurNode = *pHead;
    PSListNode pPerNode = *pHead;
    assert(pHead);
    if (NULL == pos)
    {
        return;
    }
    //要是传的参数没有在链表中,那么删除就显得没有意义了,因此对pos的检测(pos是否是链表中的某个节点)可有可无
    while ((pCurNode != pos) && (pCurNode != NULL))
    {
        pPerNode = pCurNode;
        pCurNode = pCurNode->pNextNode;
    }
    //使当前结点的上一个结点指向当前结点的下一个结点,然后释放当前结点的空间,即可做到删除当前结点
    pPerNode->pNextNode = pPerNode->pNextNode->pNextNode;
    free(pCurNode);
    pCurNode = NULL;
}

void  Insert(PSListNode* pHead, PSListNode pos, DataType data)
{
    //更简单的方法(pos指向的结点在链表中):
    //直接交换pos指向的节点和新插入的节点的的数据,然后使新插入的结点的下一个结点为pos指向的节点的下一个结点,
    //pos指向的节点的下一个结点为新插入的结点
    PSListNode pTempNode = *pHead;
    PSListNode pNode = *pHead;
    assert(pHead);
    //pTempNode!=NULL是为了避免pos是一个创建的孤结点,它没有插入到链表中
    while ((pTempNode != pos) && (pTempNode != NULL))
    {
        pNode = pTempNode;
        pTempNode = pTempNode->pNextNode;
    }
    if ((pTempNode == NULL) && (pos != NULL))
    {
        printf("链表中不存在该结点!\n");
        return;
    }
    else
    {
        PSListNode pNewNode = ByeNode(data);
        if (NULL == pNewNode)
        {
            printf("开辟空间失败!\n");
        }
        else
        {
            pNewNode->pNextNode = pNode->pNextNode;
            pNode->pNextNode = pNewNode;
        }
    }
}

void PrintList(PSListNode pHead)
{
    PSListNode pNode = pHead;
    //当pHead指向空时,说明此时链表中没有数据,那么不会进入打印数据的循环,而是等到函数结束返回
    while (NULL != pNode)
    {
        printf("%d  ", pNode->data);
        pNode = pNode->pNextNode;
    }
    printf("\n");
}

void DestroyList(PSListNode* pHead)
{
    PSListNode pCurNode = NULL;
    PSListNode pPerNode = *pHead;
    assert(pHead);
    while (NULL != pCurNode)
    {
        //每次销毁链表最前面那个结点的空间
        pPerNode = pCurNode->pNextNode;
        free(pCurNode);
        //不会出现野指针
        pCurNode = pPerNode;
    }
}

3、用于测试函数正确性的Test.c文件

#include "SListNode.h"

//Test PushBack() / PopBack()
void TestFun1()
{
    PSListNode pHead = NULL;
    InitList(&pHead);
    PushBack(&pHead, 0);
    PushBack(&pHead, 1);
    PushBack(&pHead, 2);
    PushBack(&pHead, 3);
    PrintList(pHead);
    PopBack(&pHead);
    PopBack(&pHead);
    PrintList(pHead);
}

//Test PushFront() / PopFront()
void TestFun2()
{
    PSListNode pHead = NULL;
    InitList(&pHead);
    PushFront(&pHead, 0);
    PushFront(&pHead, 1);
    PushFront(&pHead, 2);
    PushFront(&pHead, 3);
    PrintList(pHead);
    PopFront(&pHead);
    PopFront(&pHead);
    PrintList(pHead);
}

//Test Find() / Erase() / Insert()
void TestFun3()
{
    PSListNode pHead = NULL;
    PSListNode pRetNode = NULL;
    InitList(&pHead);
    PushBack(&pHead, 0);
    PushBack(&pHead, 1);
    PushBack(&pHead, 2);
    PushBack(&pHead, 3);
    PrintList(pHead);
    pRetNode = Find(pHead, 2);
    printf("%d\n", pRetNode->data);
    Erase(&pHead, Find(pHead, 1));
    PrintList(pHead);
    Insert(&pHead, Find(pHead, 2), 1);
    PrintList(pHead);
}

//Test DestroyList()
void TestFun4()
{
    PSListNode pHead = NULL;
    InitList(&pHead);
    PushBack(&pHead, 0);
    PushBack(&pHead, 1);
    PushBack(&pHead, 2);
    PushBack(&pHead, 3);
    DestroyList(&pHead);
    PrintList(pHead);
}

int main()
{
    //TestFun1();
    //TestFun2();
    //TestFun3();
    TestFun4();
    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值