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

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

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

代码实现

#include

#include

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。要想改变主函数中的指针值,应该调用主函数中指针的指针,通过指针的指针来改变主函数中指针的指向的具体的地址值。改正方法一:在主函数中构造一个具体的结构体,在函数中通过指针将主函数中的结构体值改变

改正方法二:将函数的形参改为指针的指针,通过函数将链表对应的结点的地址传递给主函数的指针。

问题:由于已经链表的结点的地址传递出来了,就有从外界非法改动链表的可能,所以不建议采用

常见错误四:删除或者清空整个链表,没有的将对应的结点的内存释放

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

相关推荐

2022十大初音未来游戏排名推荐
365网络科技有限公司是做什么的

2022十大初音未来游戏排名推荐

📅 07-11 👁️ 8405
申万菱信新能源汽车(001156)诊断报告
日博365官网手机版

申万菱信新能源汽车(001156)诊断报告

📅 07-07 👁️ 5037
什么是现金宝?
365网络科技有限公司是做什么的

什么是现金宝?

📅 07-07 👁️ 3069
中兴基带软件开发如何
日博365官网手机版

中兴基带软件开发如何

📅 06-27 👁️ 9774
天彩写真机怎么样?
日博365官网手机版

天彩写真机怎么样?

📅 07-07 👁️ 6598