单链表(线性表的链式存储)

1 概念

单链表:线性表的链式存储

线性表:数据之间是一对一的关系

链式存储:不需要在内存中开辟一块连续的空间,所以每一个数据不再是基本数据,而是优两部分组成,数据域和指针域,数据域保存数据,指针域保存下一个结点的地址

单链表:就是单向的链表,前者结点可以找到后者结点,但是后者无法找到前者

2 单链表的操作

2.1 定义结点结构体

//定义数据类型
typedef int DataType;

//定义结点结构体
typedef struct node{
    DataType data;  //数据域
    struct node *next; //指针域,为了能够操作后面结点,
                       //所以指针的类型定义为当前结构体类型
}linklist;

2.2 创建一个空的单链表

//创建一个空的单链表
//如果是有头结点的单链表,
//定义一个头结点,让数据域不赋值,指针域为空来表示当前链表为空
linklist *LinklistCreate()
{
    //定义一个头结点,在堆区开辟空间
    linklist *Head = (linklist *)malloc(sizeof(linklist));

    //初始化指针域标识为空
    Head->next = NULL;

    return Head;
}

2.3 头插法插入数据

//插入数据(头插法插入数据)
void LinklistInserHead(linklist *Head, DataType value)
{
    //开辟空间并赋值
    //(因为创建空单链表时只是开辟了头结点的空间,所以每次插入数据之前需要重新开辟空间)
    linklist *temp = (linklist *)malloc(sizeof(linklist));
    temp->data = value;
    temp->next = NULL;

    //将原本头结点后面结点的地址保存在新插入结点的指针域里面
    //头结点后面结点的地址:Head->next
    //新插入结点的指针域:temp->next
    temp->next = Head->next;

    //将新插入结点的地址保存在头结点的指针域里面
    //新插入结点的地址:temp
    //头结点的指针域:Head->next
    Head->next = temp;

    return ;
}

2.4 遍历单链表

//遍历单链表
void LinklistPrint(linklist *Head)
{
    //定义一个指针遍历保存第一个有数据的结点的地址
    linklist *p = Head->next;

    //循环遍历单链表,直到p保存结点的指针域为NULL时循环结束
    while(p->next != NULL)
    {
        //打印数据
        printf("%d ", p->data);

        //p指向下一个结点(p保存下一个结点的地址)
        p = p->next;
    }

    printf("%d\n", p->data);
}

2.5 尾插法插入数据

//尾插法插入数据
void LinklistInsertTail(linklist *Head, DataType value)
{
    //申请空间并赋值
    linklist *temp = (linklist *)malloc(sizeof(linklist));
    temp->data = value;
    temp->next = NULL;

    //找到最后一个结点
    linklist *p = Head;
    while(p->next != NULL)
    {
        p = p->next;
    }

    //将新插入的结点保存在最后一个结点的后面
    //将新插入的结点的地址保存在p指向的结点的指针域里面
    p->next = temp;
    //将新插入结点的指针域置为NULL
    temp->next = NULL;

    return ;
}

2.6 判断单链表是否为空

//判断单链表是否为空
//如果为空返回1,否则返回0
int LinklistIsEmpty(linklist *Head)
{
    return Head->next == NULL ? 1 : 0;
}

2.7 头删法删除数据(返回删除的数据)

//头删法删除数据(返回删除的数据)
DataType LinklistDeleteHead(linklist *Head)
{
    if(LinklistIsEmpty(Head) == 1)
    {
        printf("删除失败,链表为空\n");
        return (DataType)-1;
    }

    DataType value = Head->next->data;
    linklist *temp = Head->next;

    Head->next = Head->next->next;

    free(temp);
    temp = NULL;

    return value;
}

2.8 按照数据修改数据

//按照数据修改数据
void LinklistUpdate(linklist *Head, DataType Oldvalue, DataType Newvalue)
{
    if(LinklistIsEmpty(Head))
    {
        printf("链表为空\n");
        return ;
    }

    int flags = 0;

    //遍历链表对比每一个数据
    while(Head->next != NULL)
    {
        Head = Head->next;

        if(Head->data == Oldvalue)
        {
            Head->data = Newvalue;
            flags = 1;
        }
    }

    if(flags == 0)
    {
        printf("数据%d不存在,修改失败\n", Oldvalue);
    }
}

2.9 按照位置查找数据

//按照位置查找数据
DataType LinklistSearchData(linklist *Head, int pos)
{
    if(LinklistIsEmpty(Head))
    {
        printf("链表为空\n");
        return (DataType)-1;
    }

    if(pos < 1)
    {
        printf("位置有误\n");
        return (DataType)-1;
    }

    linklist *p = Head;
    int i;

    for(i = 1; i <= pos; i++)
    {
        if(p->next == NULL)
        {
            printf("位置有误\n");
            return (DataType)-1;
        }

        p = p->next;
    }

    return p->data;
}

2.10 按照数据查找位置

//按照数据查找位置
int LinklistSearchPos(linklist *Head, DataType value)
{
    if(LinklistIsEmpty(Head))
    {
        printf("链表为空\n");
        return -1;
    }

    linklist *p = Head;
    int pos = 0;
    while(p->next != NULL)
    {
        pos++;
        p = p->next;
        if(p->data == value)
        {
            return pos;
        }
    }

    printf("数据%d不存在,查找失败\n", value);
    return -1;
}

2.11 按照位置插入数据

//按照位置插入数据
void LinklistInsertByPos(linklist *Head, int pos, DataType value)
{
    if(pos < 1)
    {
        printf("位置有误\n");
        return ;
    }

    linklist *temp = (linklist *)malloc(sizeof(linklist));
    temp->data = value;
    temp->next = NULL;

    if(LinklistIsEmpty(Head) == 1)
    {
        temp->next = Head->next;
        Head->next = temp;
    }
    else
    {
        int i;
        for(i = 1; i < pos; i++)
        {
            if(Head->next == NULL)
            {
                printf("位置有误\n");
                return ;
            }

            Head = Head->next;
        }

        temp->next = Head->next;
        Head->next = temp;
    }
}

2.12 直接插入排序

//直接插入排序
void LinklistSortInsert(linklist *Head, DataType value)
{
    linklist *temp = (linklist *)malloc(sizeof(linklist));
    temp->data = value;
    temp->next = NULL;

    while(Head->next != NULL && Head->next->data < temp->data)
    {
        Head = Head->next;
    }

    temp->next = Head->next;
    Head->next = temp;
}

3 整体代码

linklist.h

#ifndef _LINKLIST_H_
#define _LINKLIST_H_

//一般.h里面一般放取别名、结构体、函数的声明

#include <stdio.h>
#include <stdlib.h>

//定义数据类型
typedef int DataType;

//定义结点结构体
typedef struct node{
    DataType data;  //数据域
    struct node *next; //指针域,为了能够操作后面结点,
                       //所以指针的类型定义为当前结构体类型
}linklist;

//创建一个空的单链表
extern linklist *LinklistCreate();
//插入数据(头插法插入数据)
extern void LinklistInserHead(linklist *Head, DataType value);
//遍历单链表
extern void LinklistPrint(linklist *Head);
//尾插法插入数据
extern void LinklistInsertTail(linklist *Head, DataType value);
//判断单链表是否为空
extern int LinklistIsEmpty(linklist *Head);
//头删法删除数据(返回删除的数据)
extern DataType LinklistDeleteHead(linklist *Head);
//按照数据修改数据
extern void LinklistUpdate(linklist *Head, DataType Oldvalue, DataType Newvalue);
//按照位置查找数据
extern DataType LinklistSearchData(linklist *Head, int pos);
//按照数据查找位置
extern int LinklistSearchPos(linklist *Head, DataType value);
//按照位置插入数据
extern void LinklistInsertByPos(linklist *Head, int pos, DataType value);
//直接插入排序
extern void LinklistSortInsert(linklist *Head, DataType value);

#endif

linklist.c

#include "linklist.h"

//.c主要放函数的定义

//创建一个空的单链表
//如果是有头结点的单链表,
//定义一个头结点,让数据域不赋值,指针域为空来表示当前链表为空
linklist *LinklistCreate()
{
    //定义一个头结点,在堆区开辟空间
    linklist *Head = (linklist *)malloc(sizeof(linklist));

    //初始化指针域标识为空
    Head->next = NULL;

    return Head;
}

//插入数据(头插法插入数据)
void LinklistInserHead(linklist *Head, DataType value)
{
    //开辟空间并赋值
    //(因为创建空单链表时只是开辟了头结点的空间,所以每次插入数据之前需要重新开辟空间)
    linklist *temp = (linklist *)malloc(sizeof(linklist));
    temp->data = value;
    temp->next = NULL;

    //将原本头结点后面结点的地址保存在新插入结点的指针域里面
    //头结点后面结点的地址:Head->next
    //新插入结点的指针域:temp->next
    temp->next = Head->next;

    //将新插入结点的地址保存在头结点的指针域里面
    //新插入结点的地址:temp
    //头结点的指针域:Head->next
    Head->next = temp;

    return ;
}

//遍历单链表
void LinklistPrint(linklist *Head)
{
    //定义一个指针遍历保存第一个有数据的结点的地址
    linklist *p = Head->next;

    //循环遍历单链表,直到p保存结点的指针域为NULL时循环结束
    while(p->next != NULL)
    {
        //打印数据
        printf("%d ", p->data);

        //p指向下一个结点(p保存下一个结点的地址)
        p = p->next;
    }

    printf("%d\n", p->data);
}

//尾插法插入数据
void LinklistInsertTail(linklist *Head, DataType value)
{
    //申请空间并赋值
    linklist *temp = (linklist *)malloc(sizeof(linklist));
    temp->data = value;
    temp->next = NULL;

    //找到最后一个结点
    linklist *p = Head;
    while(p->next != NULL)
    {
        p = p->next;
    }

    //将新插入的结点保存在最后一个结点的后面
    //将新插入的结点的地址保存在p指向的结点的指针域里面
    p->next = temp;
    //将新插入结点的指针域置为NULL
    temp->next = NULL;

    return ;
}

//判断单链表是否为空
//如果为空返回1,否则返回0
int LinklistIsEmpty(linklist *Head)
{
    return Head->next == NULL ? 1 : 0;
}

//头删法删除数据(返回删除的数据)
DataType LinklistDeleteHead(linklist *Head)
{
    if(LinklistIsEmpty(Head) == 1)
    {
        printf("删除失败,链表为空\n");
        return (DataType)-1;
    }

    DataType value = Head->next->data;
    linklist *temp = Head->next;

    Head->next = Head->next->next;

    free(temp);
    temp = NULL;

    return value;
}

//按照数据修改数据
void LinklistUpdate(linklist *Head, DataType Oldvalue, DataType Newvalue)
{
    if(LinklistIsEmpty(Head))
    {
        printf("链表为空\n");
        return ;
    }

    int flags = 0;

    //遍历链表对比每一个数据
    while(Head->next != NULL)
    {
        Head = Head->next;

        if(Head->data == Oldvalue)
        {
            Head->data = Newvalue;
            flags = 1;
        }
    }

    if(flags == 0)
    {
        printf("数据%d不存在,修改失败\n", Oldvalue);
    }
}

//按照位置查找数据
DataType LinklistSearchData(linklist *Head, int pos)
{
    if(LinklistIsEmpty(Head))
    {
        printf("链表为空\n");
        return (DataType)-1;
    }

    if(pos < 1)
    {
        printf("位置有误\n");
        return (DataType)-1;
    }

    linklist *p = Head;
    int i;

    for(i = 1; i <= pos; i++)
    {
        if(p->next == NULL)
        {
            printf("位置有误\n");
            return (DataType)-1;
        }

        p = p->next;
    }

    return p->data;
}

//按照数据查找位置
int LinklistSearchPos(linklist *Head, DataType value)
{
    if(LinklistIsEmpty(Head))
    {
        printf("链表为空\n");
        return -1;
    }

    linklist *p = Head;
    int pos = 0;
    while(p->next != NULL)
    {
        pos++;
        p = p->next;
        if(p->data == value)
        {
            return pos;
        }
    }

    printf("数据%d不存在,查找失败\n", value);
    return -1;
}

//按照位置插入数据
void LinklistInsertByPos(linklist *Head, int pos, DataType value)
{
    if(pos < 1)
    {
        printf("位置有误\n");
        return ;
    }

    linklist *temp = (linklist *)malloc(sizeof(linklist));
    temp->data = value;
    temp->next = NULL;

    if(LinklistIsEmpty(Head) == 1)
    {
        temp->next = Head->next;
        Head->next = temp;
    }
    else
    {
        int i;
        for(i = 1; i < pos; i++)
        {
            if(Head->next == NULL)
            {
                printf("位置有误\n");
                return ;
            }

            Head = Head->next;
        }

        temp->next = Head->next;
        Head->next = temp;
    }
}

//直接插入排序
void LinklistSortInsert(linklist *Head, DataType value)
{
    linklist *temp = (linklist *)malloc(sizeof(linklist));
    temp->data = value;
    temp->next = NULL;

    while(Head->next != NULL && Head->next->data < temp->data)
    {
        Head = Head->next;
    }

    temp->next = Head->next;
    Head->next = temp;
}
              

main.c

#include "linklist.h"

//分文件编程的意义在于多人协助代码管理

int main(int argc, char const *argv[])
{
    linklist *h1 = LinklistCreate();
    LinklistInserHead(h1, 100);
    LinklistInserHead(h1, 200);
    LinklistInserHead(h1, 300);
    LinklistInserHead(h1, 400);
    LinklistInserHead(h1, 500);
    LinklistInserHead(h1, 600);
    LinklistPrint(h1);

    LinklistInsertTail(h1, 666);
    LinklistInsertTail(h1, 777);
    LinklistInsertTail(h1, 888);
    LinklistPrint(h1);

    DataType del_val;
    del_val = LinklistDeleteHead(h1);
    printf("del_val = %d\n", del_val);
    LinklistPrint(h1);

    LinklistUpdate(h1, 100, 333);
    LinklistPrint(h1);

    DataType val;
    val = LinklistSearchData(h1, 4);
    printf("第4个数据是%d\n", val);

    int pos;
    pos = LinklistSearchPos(h1, 200);
    printf("数据200的位置是%d\n", pos);

    LinklistInsertByPos(h1, 4, 1000);
    LinklistPrint(h1);

    puts("***********************");
    linklist *h2 = LinklistCreate();
    LinklistInsertTail(h2, 30);
    LinklistInsertTail(h2, 60);
    LinklistInsertTail(h2, 80);
    LinklistPrint(h2);

    LinklistSortInsert(h2, 10);
    LinklistSortInsert(h2, 40);
    LinklistSortInsert(h2, 70);
    LinklistSortInsert(h2, 100);
    LinklistPrint(h2);
    
    return 0;
}

Makefile

main:main.o linklist.o
main.o:main.c
linklist.o:linklist.c

.PHONY:clean

clean:
    $(RM) *.o main a.out

4.扩展用法

实现链表的翻转

h1: 10->20->30->40->NULL

...

h1: 40->30->20->10->NULL

//实现链表的翻转
void LinklistReverse(linklist *Head)
{
    if(LinklistIsEmpty(Head))
    {
        printf("链表为空\n");
        return ;
    }

    //定义两个指针变量来执行链表的翻转
    linklist *p, *q;
    //让p指针保存头结点之后的第一个有数据的结点的地址
    p = Head->next;

    //头结点的指针域等于空来初始化头结点
    Head->next = NULL;

    //循环让q指针保存p指向的结点的首地址并通过头插法将其插法插入到头结点的后面
    while(p != NULL)
    {
        //q指针保存p指向的结点的地址
        q = p;
        //p指针后移保存下一个结点的地址
        p = p->next;

        //通过头插法将q指向的结点插入到头结点的后面
        q->next = Head->next;
        Head->next = q;
    }

    return ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小徐的记事本

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值