数据结构之单链表C语言实现,以及其中的常见的错误总结和修改

自己参考了一些书和网上的代码,然后总结了一下。附加一些自己在途中遇见的错误。可以运行,可以通过。

代码实现

#include <stdio.h>
#include <stdlib.h>
typedef enum {
    
    false,true} Status;

//定义节点类的数据域
typedef struct
{
    
    
    char *name;
}ElemType;

//定义节点的构成:节点:数据域和指针域
typedef struct Node
{
    
    
    ElemType data;                  //节点域
    struct Node *Next;            //指针域
}Node;
typedef struct Node *LinkList;      //将指向结点结构体的指针命名为LinkList

//定义相关的方法
Status InitList(LinkList *L);
Status ListAdd(LinkList L,ElemType e);
void ShowList(LinkList L);
Status ListInsert(LinkList L,int i,ElemType e);
Status ListDeleteNo(LinkList L,int i);
Status GetElem(LinkList L, int i,LinkList *e);
int getlength(LinkList L);

int main()
{
    
    
    printf("构造链表: \n");
    LinkList test ;
    InitList(&test);
    ElemType one = {
    
    "郭龙"};
    ElemType two = {
    
    "陈宁"};
    ElemType three = {
    
    "石翔旭"};
    ElemType four = {
    
    "许可"};
    ElemType five = {
    
    "庄钦"};
    ElemType six = {
    
    "刘沛沛"};
    ListAdd(test,one);
    ListAdd(test,two);
    ListAdd(test,three);
    ListAdd(test,four);
    ListAdd(test,five);
    ListAdd(test,six);
    ShowList(test);

    printf("4号位插入节点:  \n");
    ElemType insert = {
    
    "吴天爬"};
    ListInsert(test,4,insert);
    ShowList(test);

    printf("6号删除结点:\n");
    ListDeleteNo(test,6);
    ShowList(test);

    printf("查找3号结点,并将之删除返回  \n  \n");
    LinkList goal;
    GetElem(test,2,&goal);
    printf("查找的目标节点:%s \n\n",goal->data.name);
    printf("查找之后的链表: \n");
    ShowList(test);


    printf("测试:获取链表的长度 \n \n");
    int length = 0;
    printf("链表的长度为:%d  \n",getlength(test));
    ShowList(test);

    return 0;
}

/*
    功能描述:初始化对应的链表,将传入的创建的链表进行初始化
*/
Status InitList(LinkList *L)
{
    
    
    *L = (Node *)malloc(sizeof(Node));

    //判定是否分配成功
    if(*L == NULL)
    {
    
    
        printf("申请空间失败");
        return false;
    }
    
    //初始化的链表,没有一个元素,将头指针的next置为空
    (*L)->Next = NULL;
    return true;
}

/*
    功能描述:在链表L的末位增加新的结点
*/
Status ListAdd(LinkList L,ElemType e)
{
    
    
    //根据传入的结点的值,申请空间组装成对应的结点
    Node *temp;
    temp = (Node *)malloc(sizeof(Node));
    if(temp == NULL)
    {
    
    
        printf("分配失败");
        return false;
    }
    temp->data = e;
    temp->Next = NULL;

    //遍历到链表的末位
    while(L->Next)
    {
    
    
        L = L->Next;
    }
    L->Next = temp;

    return true;

}

/*
    功能描述:在第i个结点处插入以e为数据的新的结点
*/
Status ListInsert(LinkList L,int i,ElemType e)
{
    
    
    //判定链表是否为空和输入是否合法
    if(!L->Next )
    {
    
    
        printf("链表为空");
        return false;
    }
    if(i < 1)
    {
    
    
        printf("输入非法");
        return false;
    }

    //遍历到对应的位置的前一个位置
    LinkList p = NULL;
    p = L->Next;
    int j = 1;
    while(p && j < i - 1)
    {
    
    
        p = p->Next;
        j ++;
    }

    //假设只有四个结点,要求插入的位置是第六个
    if(!p )
    {
    
    
        return false;
    }

    //构造结点,尾指针为空
    Node *temp = NULL;
    temp = (Node *)malloc(sizeof(Node));
    if(!temp)
    {
    
    
        printf("未申请到空间");
        return false;
    }
    temp->data = e;
    temp->Next = NULL;

    //连接起对应的指针
    temp->Next = p->Next;
    p->Next = temp;

    return true;
}

/*
    功能描述:根据传入的索引,将节点删除
*/
Status ListDeleteNo(LinkList L,int i)
{
    
    
    //判定链表是否为空
    if(!L->Next || i < 1)
    {
    
    
        return false;
    }

    //遍历到对应的结点的前一个结点
    LinkList p = NULL;
    p = L->Next;
    int j = 1;
    while(p && j < i - 1)
    {
    
    
        p = p->Next;
        j ++;
    }

    //判定特殊情况,如果输入的结点的序号大于拥有的结点数
    if(!p)
    {
    
    
        return false;
    }

    //删除结点
    LinkList q = NULL;
    q = p->Next;
    p->Next = p->Next->Next;
    free(q);
    return true;


}

/*
    功能描述:用e返回L中第i个数据元素的值
    返回值:0,输入错误;1,查找成功
*/
Status GetElem(LinkList L,int i,LinkList *e)
{
    
    
    //判定输入的索引是否正确  和链表是否为空
    if(i < 1 || !L->Next)
    {
    
    
        return false;
    }

    //遍历将p指向i对应结点
    int j = 1;
    L = L->Next;
    while(L && j < i)
    {
    
    
        L = L->Next;
        j ++;
    }

    //针对两种情况:查找越界,输入的索引大于有的结点数
    if(!L)
    {
    
    
        return false;
    }
    *e = L;     //将地址传给主函数,造成结点被非法修改

    return true;

}

/*
    功能描述:获取链表的长度,将值返回给外部的参数
*/
int getlength(LinkList L)
{
    
    
    int j = 0;
    L = L->Next;
    while(L)
    {
    
    
        L = L->Next;
        j ++;
    }
    return j;
}

/*
    功能描述:展示输出对应的链表
*/
void ShowList(LinkList L)
{
    
    
    L = L->Next;
    int i = 1;
    while(L)
    {
    
    

        printf("    结点%3d:%5s \n",i,L->data.name);

        i ++;
        L = L->Next;
    }
    printf("***********************************************************  \n \n");
}

运行效果

在这里插入图片描述

常见错误一:尾结点的Next指针并没有处理

在这里插入图片描述

在这里插入图片描述

  • 没有将加入的最后一个结点的指针域next置为NULL,陷入无限的死循环
常见的错误二:初始化函数InitList
/*
    功能描述:初始化对应的链表,将传入的创建的链表进行初始化
*/
Status InitList(LinkList L)
{
    
    
    L = (Node *)malloc(sizeof(Node));
    //已经修改了形参指针的值,并不是修改实参的地址,已经和实参没有任何的关系

    //判定是否分配成功
    if(L == NULL)
    {
    
    
        printf("申请空间失败");
        return false;
    }

    //初始化的链表,没有一个元素,将头指针的next置为空
    L->Next = NULL;
    return true;

}
  • 错误分析:传入的是在主函数声明的指向结点的指针,但是在函数中自己又申请了新的地址,改变了形参的值。已经和主函数声明的指针没有任何关系。所以,并没有初始化成功。
  • 将指针当做链表本身,以为改了指针就是改了链表本身。实际形参的指针的值,并没有改变实参。
常见错误三:混淆指针和指针的指针的含义
  • 在主函数中将一个指针初始化为NULL,通过值传递,传递给函数。在函数中对形参进行解引用操作,指向的仍旧是NULL,即使对NULL进行赋值还是NULL。
  • 要想改变主函数中的指针值,应该调用主函数中指针的指针,通过指针的指针来改变主函数中指针的指向的具体的地址值。
  • 改正方法一:在主函数中构造一个具体的结构体,在函数中通过指针将主函数中的结构体值改变

在这里插入图片描述

  • 改正方法二:将函数的形参改为指针的指针,通过函数将链表对应的结点的地址传递给主函数的指针。
    • 问题:由于已经链表的结点的地址传递出来了,就有从外界非法改动链表的可能,所以不建议采用
    • 在这里插入图片描述
常见错误四:删除或者清空整个链表,没有的将对应的结点的内存释放

在这里插入图片描述

  • 删除结点或者清空整个链表,记得回收对应的空间。否则会造成内存泄露,可用的内存空间越来越少。

猜你喜欢

转载自blog.csdn.net/Blackoutdragon/article/details/108226295