1、前言
我们学完顺序表以后我们现在来学习链表,我们先学习最简单的单向链表,
2.链表
2.1特点
链式存储不是连续存储,大小不固定,其插入和删除很简单,但是其访问和查找比较难实现,在单向链表中每个节点都有前驱和后继(头结点只有后继,尾结点只有前驱)
2.2实现一个节点
我们肯定使用我们学过的结构体来实现我们的节点,我们要在结构体内定义数据域和指针域
typedef int data_type
typedef struct link
{
//数据域
data_type data;
//指针域
struct link *next;
}Link;
//接下来定义头部节点
Link *head=(Link *)malloc(sizeof(Link));
接下来我们直接写代码吧
2.2.1先来实现头结点的创建,我们先写库函数link.h
#ifndef _LINK_H
#define _LINK_H
//引入头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//创建结构体
//数据类型的定义
typedef int data_type
struct link
{
data_type data;
struct link *next;
}Link;
//定义错误枚举,在这里我把我们接下来可能出现的错误进行定义,以便我们可以快速查找错误
enum value
{
EMPTY_ERROR=-7,
FULL_ERROR,
POS_ERROR,
CREAT_ERROR,
NULL_ERROR,
MALLOC_ERROR,
ERROR,
OK
};
Link * my_node();//创建结点
#endif
2.2.2接下来写main.c
#include "../include/link.h"
int main()
{
Link *phead=NULL;
phead=my_head;
if(NULL==phead)
{
printf("申请空间失败\n");
return ERROR;
}
printf("申请成功\n");
return 0;
}
2.2.3编写 link.c
#include "../include/link.h"
//链表节点的建立
Link * my_node()
{
Link *pHead=(Link *)malloc(sizeof(Link));
if(NULL==pHead)
{
printf("malloc error");
return NULL;
}
memset(pHead,'\0',sizeof(Link));
return pHead;
}
2.3:插入数据
主函数
#include "../include/link.h"
int main()
{
Link *phead=NULL;
phead=my_node();
if(NULL==phead)
{
printf("申请空间失败\n");
return ERROR;
}
printf("申请成功\n");
//插入数据
int pos;
int data;
while(1)
{
printf("请输入插入的位置\n");
scanf("%d",&pos);
if(pos==-1)
{
break;
}
printf("请输入插入的数据\n");
scanf("%d",&data);
my_insert(pHead,pos,data);
}
return 0;
}
子函数
#include "../include/link.h"
Link * my_node()
{
Link *pHead=(Link *)malloc(sizeof(Link));
if(NULL==pHead)
{
printf("malloc error");
return NULL;
}
memset(pHead,'\0',sizeof(Link));
return pHead;
}
//插入函数
//在插入函数的中我们要分三个区域,分别为头部插入(在头结点之后插入)
//中间插入
//尾部插入
int my_insert(SeqLink *phead,int pos,int Newdata)
{
//入参检查
if(NULL==*phead)
{
printf("creat error:");
return CREAT_ERROR;
}
if(pos<0)
{
printf("pos error: ");
return POS_ERROR;
}
//为我们要插入的数据申请一个 结点
Link *pnew=my_node();
penw->data=newdata;
//接下来我们判断插入位置进行插入
switch(pos)
{
//首部插入
case 1:
{ //判断此时链表是否为空
if(NULL==phead->next)
{
phead->next=pnew;
}
else
{
pnew->next=phead->next;
phead->next=pnew;
}
break;
}
//尾部插入
case 0:
{
Link *ptail=phead->next;
if(NULL==ptail)
{
phead->next=pnew;
}
else
{
while(NULL != ptail->next)
{
ptail=ptail->next;
}
ptail->next=pnew;
break;
}
default:
{
Link *ppre=phead;
if(NULL==ppre->next)
{
ppre->next=pnew;
}
else
{
for(int i=0;(i<pos-1)&&(NULL != ppre);i++)
{
ppre=ppre->next;
}
if(NULL==ppre)
{
printf("pos error:");
return POS_ERROR;
}
else
{
pnew->next=ppre->next;
ppre->next=pnew;
}
}
}
}
rerurn OK;
}
2.4删除函数
#include "../include/link.h"
Link * my_node()
{
Link *pHead=(Link *)malloc(sizeof(Link));
if(NULL==pHead)
{
printf("malloc error");
return NULL;
}
memset(pHead,'\0',sizeof(Link));
return pHead;
}
//插入函数
//在插入函数的中我们要分三个区域,分别为头部插入(在头结点之后插入)
//中间插入
//尾部插入
int my_insert(SeqLink *phead,int pos,int Newdata)
{
//入参检查
if(NULL==*phead)
{
printf("creat error:");
return CREAT_ERROR;
}
if(pos<0)
{
printf("pos error: ");
return POS_ERROR;
}
//为我们要插入的数据申请一个 结点
Link *pnew=my_node();
penw->data=newdata;
//接下来我们判断插入位置进行插入
switch(pos)
{
//首部插入
case 1:
{ //判断此时链表是否为空
if(NULL==phead->next)
{
phead->next=pnew;
}
else
{
pnew->next=phead->next;
phead->next=pnew;
}
break;
}
//尾部插入
case 0:
{
Link *ptail=phead->next;
if(NULL==ptail)
{
phead->next=pnew;
}
else
{
while(NULL != ptail->next)
{
ptail=ptail->next;
}
ptail->next=pnew;
break;
}
default:
{
Link *ppre=phead;
if(NULL==ppre->next)
{
ppre->next=pnew;
}
else
{
for(int i=0;(i<pos-1)&&(NULL != ppre);i++)
{
ppre=ppre->next;
}
if(NULL==ppre)
{
printf("pos error:");
return POS_ERROR;
}
else
{
pnew->next=ppre->next;
ppre->next=pnew;
}
}
}
}
rerurn OK;
}
//删除数据
int my_delete(Link *pHead,int pos, data_type *pDelData)
{
if(NULL == pHead)
{
printf("creat is error!\n");
return CREAT_ERROR;
}
if(pos < 0)
{
printf("pos is error!\n");
return POS_ERROR;
}
if(NULL == pDelData)
{
printf("null error!\n");
return NULL_ERROR;
}
switch(pos)
{
case HEAD:
{
//头部删除
printf("I am Head!\n");
break;
}
case TAIL:
{
//尾部删除
printf("I am Tail!\n");
break;
}
default:
{
//中间删除
//判断是否为空链表
Link *pDelPre = pHead;
if(NULL == pHead->pNext)
{
printf("空链表! 不能删除操作!\n");
return EMPTY_ERROR;
}
else
{
//采用寻找pos-1的位置即可
int i;
for(i=0;(i<pos-1) && (NULL != pDelPre);i++)
{
pDelPre = pDelPre->pNext;
}
//判断引起for结束的原因
if(NULL == pDelPre)
{
printf("pos is error!\n");
return POS_ERROR;
}
//定义一个临时指针接替pDelPre->pNext
Link *pDel = pDelPre->pNext;
//保存被删除结点之后的所有结点
pDelPre->pNext = pDel->pNext;
//保存被删除的数据
*pDelData = pDel->data;
//释放
free(pDel);
pDel = NULL;
}
}
}
return OK;
}
2.5显示函数
int my_show(Link *phead)
{
//入参检查
if(NULL==phead)
{
printf("creak error:\n");
return CREAK_ERROR;
}
Link *pshow=phead->next;
if(NULL==pshow)
{
printf("空链表!\n");
return EMPTY_ERROR;
}
else
{
while(NULL != pshow)
{
printf("%3d",pshow->data);
pshow=pshow->next;
}
printf("\n");
printf("显示完毕\n");
}
return OK;
}
2.6销毁函数
int DestroyLink(Link *pHead)
{
if(NULL == pHead)
{
printf("creat is error!\n");
return CREAT_ERROR;
}
//判断是否为空链表
if(NULL == pHead->pNext)
{
//释放头结点的空间
free(pHead);
//该置空是不会引起实际参数的指向发生变化,形式参数的指向此时被改变了
pHead = NULL;
}
else
{
//不能去直接释放pHead,后续还有一些结点
//(1)先释放头结点之后的所有结点
Link *pDelHead = pHead->pNext;
Link *pDelTemp = NULL;
while(NULL != pDelHead)
{
pDelTemp = pDelHead;
//释放pDelTemp结点之前:先保存pDelTemp->pNext/pDelHead->pNext
//之后的所有结点
pDelHead = pDelHead->pNext;
//释放
free(pDelTemp);
pDelTemp = NULL;
}
//(2)最后再释放头结点
free(pHead);
//给形式参数pHead置空
pHead = NULL;
}
return OK;
}
3、最后
我在单向链表中为大家展示了如何创建节点,如何在插入数据,如何删除数据,如何展示数据,如何销毁链表,因为我们是在堆区开辟的空间最后要我们手动释放空间,剩下的还有如何查找数据,还有修改数据,这两个函数留给大家自己练手,不会的可以私信我,好了,我们的单向链表到这里就要结束了,下一篇文章,我将会为大家讲解双向链表,其大部分的知识点与单向链表差不多,大家学完单向链表再学习双向链表就比较简单了