头结点无数据,头结点的指针域next指向第一个元素(此时last = 0)
头指针初始指向头结点
通过遍历移动头指针来寻找结点
LinkList.c文件
包含头文件LinkList.h
函数定义
单链表初始化函数 | LL LLInit(); |
单链表长度函数 | int LLLength(LL H); |
单链表插入函数 | int LLInsert(LL H , int post , LLNDataType data); |
单链表打印函数 | void LLPrint(LL H); |
单链表查找函数 | int LLFind(LL H, LLNDataType data); |
单链表修改函数 | int LLModify(LL H, int post, LLNDataType data); |
单链表删除函数(按位置) | int LLDelete(LL H, int post); |
单链表删除函数(按内容) | void LLDeleteData(LL H,LLNDataType data); |
单链表清空函数 | void LLClear(LL H); |
单链表插入操作图解:
通过循环遍历让头指针H指向要插入位置(post)的前一个位置(post-1)
按图示顺序先定义一个PNew指针来保存要插入的新结点
PNew->next = NULL;
PNew->data = data; //将要插入的数据存放到PNew的数据域PNew->data
将新结点的指针域next指向要被插入位置上的结点post
PNew->next = H->next;
然后将post-1结点与新结点相连
H->next = PNew;
#include "LinkList.h"
LL LLInit()
{
LL H = (LL)malloc(sizeof(LLN)); //定义一个指向单链表类型的结构体指针,并为其开辟空间
if (NULL == H) //判断是否成功开辟空间
{
printf("LLInit failed\n"); //打印失败信息
return NULL; //返回空指针(返回值要与函数定义的返回值保持一致)
}
H->next = NULL; // 成功开辟空间后,将头结点的指针域赋空
return H; //返回头指针
}
int LLLength(LL H) //定义单链表长度函数,返回int类型的长度
{
int len = 0; //定义长度len并初始化为0
while (H->next) //等价于while(H->next!=NULL) 头结点的next指向下标为0的第一个元素
{
H = H->next; //将头指针向后移动
len++; //每次移动后长度计数加1,循环结束后len中得到单链表的长度
}
return len; //返回单链表的长度
}
int LLInsert(LL H, int post, LLNDataType data) //定义单链表元素插入函数,三个形参 H是要传入的结构体指针(头指针) 元素插入位置post 要插入的数据data
{
if (post < 0 || post > LLLength(H)) //判断插入位置是否存在(因为可以在最后一个元素之后进行插入,所以post>LLLength(H)插入位置可以是最后一个下标之后的位置,也就是下标等于长度 )
{
printf("LLInsert failed , post is illegal\n"); //如果插入位置无效,打印错误信息
return -1; //返回值int类型 -1表示非正常结束
}
for (int i = 0; i < post; i++) //因为单链表内存位置不连续,所以在找插入位置post时要通过for循环进行遍历
H = H->next; //头指针向后遍历
LL PNew = (LL)malloc(sizeof(LLN)); //开辟一个单链表结点PNew来存放要插入的数据,通过malloc实现(开辟堆空间要注意类型匹配问题,通过强制类型转换解决)
if (NULL == PNew) //判断PNew是否开辟成功
{
printf("PNew malloc failed\n"); //如果开辟失败,打印错误信息
return -1; //返回值 -1
}
PNew->next = NULL;
PNew->data = data; //对创建的新结点进行赋值
PNew->next = H->next; //将新建的结点与单链表插入点进行连接(先连后再连前)因为其后部分链表无法找到,断开后丢失
H->next = PNew; //将新建结点与插入点的直接前驱进行连接(通过头指针)
return 0; //返回值 0 表示正常结束
}
void LLPrint(LL H) //定义一个单链表打印(显示)函数 传递形参(传入单链表H)
{
while (H->next) //等价于while(H->next!=NULL) 头结点的next指向下标为0的第一个元素
{
H = H->next; //头指针向后遍历
printf("%d ", H->data); //逐个打印单链表元素
}
putchar(10); //换行
}
int LLFind(LL H, LLNDataType data) // 定义单链表查找函数(传递单链表和要查找的数据)
{
int post = 0; //记录查找的初始位置post = 0
while (H->next != NULL) //通过while循环控制指针在单链表范围内(含头结点的单链表)
{
H = H->next; //头指针向后遍历
if (data == H->data) //如果找到数据(此时头指针在数据所在结点位置)
return post; //返回位置信息(下标)
post++; //下标随着遍历自增
}
return -1; //查询不到要找的元素
}
int LLModify(LL H, int post, LLNDataType data) //定义单链表修改函数(按位置进行修改)形参为单链表头指针,要修改位置下标,更改后的数据
{
if (post < 0 || post >= LLLength(H))
{
printf("LLModify failed , post is illegal\n");
return -1;
}
for (int i = 0; i <= post; i++)
H = H->next;
H->data = data;
return 0;
}
int LLDelete(LL H, int post) //按位置删除单链表结点
{
if (post < 0 || post >= LLLength(H))
{
printf("LLInsert failed , post is illegal\n");
return -1;
}
for (int i = 0; i < post; i++)
H = H->next;
LL PDel = H->next;
H->next = H->next->next;
free(PDel);
return 0;
}
void LLDeleteData(LL H, LLNDataType data) //按数据内容进行删除 删除从前向后遇到的第一个符合条件的元素
{
LL PDel = NULL; //定义一个单链表指针并赋空
while (H->next != NULL) //循环遍历
{
if (H->next->data == data) //如果找到要删除的内容
{
PDel = H->next; //用PDel接收要删除的结点
H->next = H->next->next; //将删除后的单链表前后连接
free(PDel); //释放PDel(单链表指针占用的空间)
PDel = NULL; //PDel赋空 避免出现野指针
}
else
{
H = H->next; //继续向后遍历,删除表内所有data元素
}
}
}
void LLClear(LL H) //单链表清空函数(保留头结点,销毁需要删除头结点)
{
LL PDel = NULL; //定义一个接收删除结点的单链表指针
while (H->next != NULL) //循环遍历
{
PDel = H->next;
H->next = H->next->next;
free(PDel); //循环删除每个结点
PDel = NULL; //指针赋空避免出现野指针
}
}
头文件LinkList.h
使用结构体对有头单链表进行定义,存放函数声明并使用 #ifndef 防止重复包含
#ifndef _LINKLIST_H_
#define _LINKLIST_H_
#include<stdio.h>
#include<stdlib.h>
typedef int LLNDataType;
typedef struct LinkListNode
{
LLNDataType data;
struct LinkListNode *next;
}LLN, *LL;
LL LLInit();
int LLLength(LL H);
int LLInsert(LL H , int post , LLNDataType data);
void LLPrint(LL H);
int LLFind(LL H, LLNDataType data);
int LLModify(LL H, int post, LLNDataType data);
int LLDelete(LL H, int post);
void LLDeleteData(LL H,LLNDataType data);
void LLClear(LL H);
#endif
主函数test.c
测试功能: | 函数调用 |
1.创建一个空的有头单向链表 | LL H = LLInit(); |
2.向单链表的0~4号位置分别插入10、11、12、13、14并打印链表验证 | LLInsert(H,0,10); LLInsert(H,2,12); |
3.打印链表长度 | printf("单链表长度是 %d\n",LLLength(H)); |
4.查找12在单链表的位置并输出到终端 | printf("元素12在单链表中的位置(下标)是:%d",LLFind(H,12)); |
5.修改0号位置数据为14并打印链表验证 | LLModify(H,0,14); |
6.删除1号位置数据并打印链表验证 | LLDelete(H,1); |
7.删除所有数据为14的结点并打印验证 | LLDeleteData(H,14); |
8. 清空链表并打印验证 | LLClear(H); |
#include"LinkList.h"
int main(int argc, char const *argv[])
{
LL H = LLInit();
printf("单链表初始长度是 %d\n",LLLength(H));
LLInsert(H,0,10);
LLInsert(H,1,11);
LLInsert(H,2,12);
LLInsert(H,3,13);
LLInsert(H,4,14);
printf("单链表长度是 %d\n",LLLength(H));
printf("单链表为:\n");
LLPrint(H);
printf("元素12在单链表中的位置(下标)是:%d",LLFind(H,12));
putchar(10);
LLModify(H,0,14);
printf("单链表更改为:\n");
LLPrint(H);
LLDelete(H,1);
printf("单链表删除1号位置之后为:\n");
LLPrint(H);
printf("单链表删除元素'14'后为:\n");
LLDeleteData(H,14);
LLPrint(H);
LLClear(H);
printf("清空后的单链表长度为%d\n",LLLength(H));
free(H);
return 0;
}
打印结果:
makefile文件
CC=gcc
OBJS=test.o LinkList.o
CFLAGS=-c -g -Wall
test:$(OBJS)
$(CC) $(OBJS) -o $@
%.o:%.c
$(CC) $(CFLAGS) $< -o $@
.PHONY:clean
clean:
$(RM) *.o test
# test:test.c LinkList.c
# gcc $^ -o $@
# clean:
# rm test