单链表(含头结点)实现增删查改功能

头结点无数据,头结点的指针域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,1,11);

 LLInsert(H,2,12);
 LLInsert(H,3,13);
 LLInsert(H,4,14);

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

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值