自己参考了一些书和网上的代码,然后总结了一下。附加一些自己在途中遇见的错误。可以运行,可以通过。
代码实现
#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。要想改变主函数中的指针值,应该调用主函数中指针的指针,通过指针的指针来改变主函数中指针的指向的具体的地址值。改正方法一:在主函数中构造一个具体的结构体,在函数中通过指针将主函数中的结构体值改变
改正方法二:将函数的形参改为指针的指针,通过函数将链表对应的结点的地址传递给主函数的指针。
问题:由于已经链表的结点的地址传递出来了,就有从外界非法改动链表的可能,所以不建议采用
常见错误四:删除或者清空整个链表,没有的将对应的结点的内存释放
删除结点或者清空整个链表,记得回收对应的空间。否则会造成内存泄露,可用的内存空间越来越少。