使用纯C语言实现带头节点链表的基本操作

使用纯C语言实现带头节点链表的基本操作

最近准备软考,复习了一下链表的相关知识,写了一个Demo。

直接上代码:

  • 定义链表的节点
#include <stdio.h>

/**
结构体声明(链表的节点的数据域)
**/
typedef struct
{
    int n;
}Data, *pData;

/*
链表的节点声明
*/
typedef struct Node
{
    Data d;           //数据域
    struct Node *pNext;      //指针域
}Node, *pNode;               //为什么要这样写,和上面的结构体有什么区别,好处是什么

//这里 我用的枚举当做BOOL变量,可以使用stdBool.h头文件代替,也可以使用宏定义实现布尔值的定义
typedef enum {false = 0, true = 1}bool;
  • 创建节点,申请空间
#include <stdio.h>
#include <stdlib.h>

pNode createNode(Data d)
{
    pNode ptr = (pNode)malloc(sizeof(Node));
    ptr->d.n = d.n;
    ptr->pNext = NULL;   //很重要的一步
    return ptr;
}
  • 创建List,返回头指针
#include <stdio.h>
#include <stdlib.h>

pNode List_Create()
{
    //创建头结点,数据域的值赋值为-1
    Data d;
    d.n = -1;
    return createNode(d);
} 
  • 释放整个链表
#include <stdio.h>
#include <stdlib.h>

void List_Delete(pNode *pHead)
{
    List_DeleteAllNode(pHead);  //调用删除链表所有节点的函数
    free(*pHead);               //再删除头结点
    *pHead = NULL;
}
  • 释放所有节点,返回头结点,链表仍在
pNode List_DeleteAllNode(pNode *pHead)
{
    pNode ptr = (*pHead)->pNext;
    while (NULL != ptr)
    {
        pNode pTemp = ptr;
        ptr = ptr->pNext;
        free(pTemp);
    }
    (*pHead)->pNext = NULL;
    return *pHead;
}
  • 追加节点(尾插法)
    追加节点(尾插法), ptrNode是指向被插入节点的指针
int List_AppendTailNode(pNode *pHead, pNode ptrNode)
{
    pNode ptr = (*pHead)->pNext;

    //找尾节点
    while (NULL != ptr->pNext)
    {
        ptr = ptr->pNext;
    }

    ptr->pNext = ptrNode;
    return List_Lenth(pHead);
}
  • 插入节点(头插法)
bool List_AppendFirstNode(pNode *pHead, pNode ptrNode)
{
    ptrNode->pNext = (*pHead)->pNext;
    (*pHead)->pNext = ptrNode;
    return true;
}
  • 插入节点(根据索引)
int List_InsertNode(pNode *pHead, int index, pNode ptrNode)
{
    //判断传入的index是否合法
    if ( index > List_Lenth(pHead) - 1 )
    {
        return -1;
    }

    pNode ptr = (*pHead)->pNext;

    int i = -1;            //循环变量,index从零开始计数, 第一个节点Node索引应该为0

    while (NULL != ptr)
    {
        i++;               //计数

        if (i == index -1)   //找到索引所在Node的前一位
        {
            ptrNode->pNext = ptr->pNext->pNext;
            ptr->pNext = ptrNode;
            break;  //跳出循环
        }

        ptr = ptr->pNext;  //计位置
    }

    return index;
}
  • 删除某节点,根据Data
bool List_DeleteNode_ByData(pNode *pHead, Data d)
{
    pNode ptr = (*pHead)->pNext;
    pNode pPre = *pHead;
    bool ret = false;
    while (NULL != ptr)
    {
        if (d.n == ptr->d.n) //匹配成功
        {
            pPre->pNext = ptr->pNext;
            deleteNode(&ptr);
            ret = true;
            break;
        }
        ptr = ptr->pNext;
        pPre = pPre->pNext;
    }
    return ret; return false;
}
  • 替换节点
    先删除节点,再插入节点,思路很简单
void List_Replace(pNode *pHead, int index, pNode ptrNode)
{
    //先删除节点,再插入节点
    List_DeleteNode_ByIndex(pHead, index);
    List_InsertNode(pHead, index, ptrNode);
}
  • 删除某节点,根据节点
//删除某节点,根据节点
bool List_DeleteNode_ByNode(pNode *pHead, pNode ptrNode)
{
    pNode ptr = (*pHead)->pNext;
    pNode pPre = *pHead;
    bool ret = false;
    while (NULL != ptr)
    {
        if (ptrNode == ptr) //匹配成功
        {
            pPre->pNext = ptr->pNext;
            deleteNode(&ptr);
            ret = true;
            break;
        }
        ptr = ptr->pNext;
        pPre = pPre->pNext;
    }
    return ret;
}
  • 删除某节点,根据索引
//删除某节点,根据索引
bool List_DeleteNode_ByIndex(pNode *pHead, unsigned int index)
{

    if (index == 0)
    {
        return false;
    }

    pNode ptr = (*pHead)->pNext;
    pNode pPre = *pHead;
    unsigned int len = 0;
    bool ret = false;
    while (NULL != ptr)
    {
        if (len == index) //匹配到索引
        {
            pPre->pNext = ptr->pNext;
            ret = true;
            deleteNode(&ptr);
            break;
        }
        ptr = ptr->pNext;
        pPre = pPre->pNext;
    }
    return ret;
}
  • 查找某节点,根据索引
//查找某节点,根据索引返回该节点指针
pNode List_FindNode_ByIndex(pNode *pHead, unsigned int index)
{
    pNode ptr = (*pHead)->pNext;
    pNode p = NULL;
    unsigned int len = 0;
    while (NULL != ptr)
    {
        if (len == index)
        {
            p = ptr;
            break;
        }
        len++;
        ptr = ptr->pNext;
    }
    return p;
}

  • 查找某节点,根据节点指针
//查找某节点,根据节点指针返回该节点第一次出现的位置索引
int List_FindNode_ByNode(pNode *pHead, pNode ptrNode)
{
    pNode ptr = (*pHead)->pNext;
    unsigned int index = 0;
    while (NULL != ptr)
    {
        index++;
        if (ptr->d.n == ptrNode->d.n)
        {
            break;
        }
        ptr = ptr->pNext;
    }
    return index;
}
  • 链表逆序(递归实现)
//链表逆序,返回头指针(递归实现)
pNode List_Invert_In(pNode *pHead)
{
    if (*pHead == NULL || (*pHead)->pNext == NULL)       //链表为空直接返回,而H->next为空是递归基
    {
        return *pHead;
    }
    pNode newHead = List_Invert_In(&((*pHead)->pNext)); //一直循环到链尾 
    (*pHead)->pNext->pNext = *pHead;                       //翻转链表的指向
    (*pHead)->pNext = NULL;                          //记得赋值NULL,防止链表错乱
    return newHead;                          //新链表头永远指向的是原链表的链尾

}
  • 链表逆序(迭代实现)
pNode List_Invert(pNode *pHead)
{
    //只有一个节点或者没有节点直接返回头结点
    if ( NULL == (*pHead)->pNext || NULL == (*pHead)->pNext->pNext)
    {
        return *pHead;
    }

    //注意:形参是二级指针
    pNode pPre = *pHead;              //指向当前的Node的前一个Node
    pNode pCur = pPre->pNext;         //指向当前的Node
    pNode pNext = NULL;              //指向当前的Node的下一个Node

    while (NULL != pCur)
    {
        pNext = pCur->pNext;        //指向当前的Node的下一个Node
        if (*pHead == pPre)
        {
            //当前节点的上一个节点是头节点,当前节点指向空
            pCur->pNext = NULL;
        }
        else    //当前节点的上一个节点不是头节点
        {
            pCur->pNext = pPre;         //当前节点的指针域指向当前节点的上一个节点
        }
        pPre = pCur;
        pCur = pNext;               //当前指向的Node移到下一位
    }

    (*pHead)->pNext = pPre;
    return *pHead;
}
  • 链表的长度
unsigned int List_Lenth(pNode * pHead)
{
    pNode ptr = (*pHead)->pNext;
    unsigned int len = 0;
    while (NULL != ptr)
    {
        ptr = ptr->pNext;
        len++;
    }
    return len;
}
  • 注:
    在所有的函数参数列表中,只要用到了头结点的地方,我传参都是传的二级指针,有什么好处??
    • 以下是我的测试函数,有一部分函数没有测试:
#include "List.h"
#include <stdio.h>

void printList(pNode *pHead);

int main(void)
{
    printf("初始化:\n");
    pNode pHead = List_Create();
    int i = 10;
    while (i)
    {
        Data d;
        d.n = i;
        List_AppendFirstNode(&pHead, createNode(d));
        i--;
    }

    //打印链表
    printList(&pHead);


    printf("\n\n第一次逆序(迭代):\n");
    pHead = List_Invert(&pHead);
    //打印链表
    printList(&pHead);



    printf("\n\n第二次逆序(递归):\n");
    pHead = List_Invert_In(&pHead);   //这个函数有问题
    //打印链表
    printList(&pHead);

    printf("\n\n 删除链表所有节点前:%d\n", List_Lenth(&pHead));
    printList(&pHead);
    List_DeleteAllNode(&pHead);
    printf("\n\n 删除链表所有节点后:%d\n", List_Lenth(&pHead));



    printf("\n\n pHead存储的内存地址:%d\n", pHead);
    printf("\n\n 释放链表:\n");
    List_Delete(&pHead);
    printf("\n\n pHead存储的内存地址:%d\n", pHead);
    printf("\n\n 保存pHead的内存地址:%d\n", &pHead);


    getchar();

    return 0;
}

void printList(pNode *pHead)
{
    printf("\n\nStart Print:\n");
    int i = -1;
    pNode p = (*pHead)->pNext;
    while (p != NULL)
    {
        i++;
        printf("i = %d, D::n = %d\n", i, p->d.n);
        p = p->pNext;
    }
    printf("End Print:\n\n");
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值