【数据结构】

单链表

什么是单链表

线性表的链式存储就是所谓的单链表

单链表的结构

单链表的特点

单链表没有个数限制,在使用的时候是通过动态分配内存的形式来开辟节点的空间的,开辟出来的新的节点和原来的单链表通过一个指针进行关联。

单链表节点的格式

在这里插入图片描述在这里插入图片描述

节点的结构体定义
#define datatype int
typedef struct node{
    datatype data;
    struct node *next;  
    //这个指针指向的是下一个节点,节点是结构体类型,所以指针也要定义成结构体指针
}linklist_t;

单链表的操作

单链表的创建
linklist_t *LinkListCreate(void)
{
    linklist_t *h;

    h = (linklist_t *)malloc(sizeof(*h));
    if(h==NULL){
        printf("malloc head memory error\n");
        return NULL;
    }

    //将数据赋值为0
    h->data = (datatype)0;
    //将指针域指向NULL
    h->next = NULL;

    return h;
}
单链表头插法

在这里插入图片描述

/*
*功能:单链表头插法插入节点
*参数:
*	@h:单链表的头
*   @data:被插入的数据
*返回值:成功返回0,失败返回-1
*/
int LinkListInsertHead(linklist_t *h,datatype data)
{
    linklist_t *tmp;
    //1.分配节点的内存,将data放到这个节点的数据域中
    tmp = (linklist_t *)malloc(sizeof(*tmp));
    if(tmp == NULL){
        printf("alloc node memory error\n");
        return -1;
    }
    tmp->data = data;

    //2.将这个节点通过头插法的方式插入到链表中即可
    //步骤1:让tmp的next指向h的后一个节点
    tmp->next = h->next;
    //步骤2:将h的next指针域指向tmp
    h->next = tmp;

    return 0;
}
单链表尾插法

在这里插入图片描述

int LinkListInsertTail(linklist_t* h, datatype data)
{
    linklist_t* tmp;
    // 1.分配节点的内存,将data放到这个节点的数据域中
    tmp = (linklist_t*)malloc(sizeof(*tmp));
    if (tmp == NULL) {
        printf("alloc node memory error\n");
        return -1;
    }
    tmp->data = data;

    // 2.让h走到最后一个节点的位置
    while (h->next) {
        h = h->next;
    }

    // 3.将这个节点通过头插法的方式插入到链表中即可
    // 步骤1:让tmp的next指向h的后一个节点
    tmp->next = h->next;
    // 步骤2:将h的next指针域指向tmp
    h->next = tmp;

    return 0;
}
单链表位置插入

在这里插入图片描述

// ①判断pos是否等于0,如果pos为零,直接将节点插入在当前节点的位置,退出
// ②如果pos不为0, 1.pos--  2.h = h->next。
// ③ 循环执行上述的①和②步骤,如果输入pos太大(不合法)链表到结尾了也要退出循环
int LinkListInsertByPos(linklist_t* h, int pos, datatype data)
{
    linklist_t* tmp;
    // 1.判断pos是否合法
    if (pos < 0) {
        printf("插入的位置不合法\n");
        return -1;
    }
    // 2.通过循环找出插入节点的位置
    while (h) {
        if (pos != 0) {
            pos--;
            h = h->next;
        } else {
            // 3.找到了插入节点的位置,将节点插入即可。
            tmp = (linklist_t*)malloc(sizeof(*tmp));
            if (tmp == NULL) {
                printf("malloc memory error\n");
                return -1;
            }
            tmp->data = data;
            tmp->next = h->next;
            h->next = tmp;
            return 0;
        }
    }
    printf("插入的位置不合法\n");
    return -1;
}
单链表的遍历

在这里插入图片描述

// 思想:判断h->next是否等于NULL,如果h->next为NULL说明链表没有要访问的成员了。
//    :如果h->next不是NULL,说明链表不为空,可以将它的数据域打印出来
//    :printf("data:%d\n",h->next->data);
//    :执行完上述的步骤之后将h->next赋值给h   h = h->next;
//    :在循环上述步骤
void LinkListShow(linklist_t *h)
{
    while(h->next){
        printf("-%d",h->next->data);
        h = h->next;
    }
    printf("-\n");
}
单链表的头删法

在这里插入图片描述

datatype LinkListDeleteHead(linklist_t* h)
{
    datatype data;
    linklist_t* tmp;
    // 1.判空
    if (LinkListIsEmpty(h)) {
        printf("链表是空,头删失败\n");
        return (datatype)-1;
    }

    // 2.让tmp指向h的下一个节点
    tmp = h->next;

    // 3.让h指向tmp的下一个节点
    h->next = h->next->next;

    // 4.释放tmp的内存
    data = tmp->data;
    if (tmp != NULL) {
        free(tmp);
        tmp = NULL;
    }

    return data;
}
单链表的尾删法

在这里插入图片描述

datatype LinkListDeleteTail(linklist_t* h)
{
    datatype data;
    linklist_t *tmp;
    // 1.判空
    if (LinkListIsEmpty(h)) {
        printf("链表是空,头删失败\n");
        return (datatype)-1;
    }
    //2.通过循环让h走到倒数第二个节点的位置
    while(h->next->next){
        h = h->next;
    }
    //3.让tmp指向h->next
    tmp = h->next;

    //4.让h->next指向空
    h->next = h->next->next;

    //5.释放tmp
    data = tmp->data;
    if(tmp != NULL){
        free(tmp);
        tmp = NULL;
    }

    return data;
}
单链表的位置删除

在这里插入图片描述

datatype LinkListDeletePos(linklist_t* h, int pos)
{
    datatype data;
    linklist_t* tmp;
    // 1.判空
    if (LinkListIsEmpty(h)) {
        printf("链表是空,位置删除失败\n");
        return (datatype)-1;
    }
    // 2.判断pos是否合法
    if (pos < 0) {
        printf("删除的位置不合法\n");
        return (datatype)-1;
    }

    // 3.通过循环找出删除节点的位置
    while (h->next) {
        if (pos > 0) {
            pos--;
            h = h->next;
        } else {
            // 4.找到了删除节点的位置,将节点删除即可。
            tmp = h->next;
            h->next = h->next->next;

            data = tmp->data;
            if(tmp != NULL){
                free(tmp);
                tmp = NULL;
            }
            return data;
        }
    }

    printf("删除的位置不合法\n");
    return (datatype)-1;
}
单链表的通过位置查询数据
datatype LinkListCheckDataByPos(linklist_t* h, int pos)
{
    datatype data;
    // 1.判空
    if (LinkListIsEmpty(h)) {
        printf("链表是空,位置查询失败\n");
        return (datatype)-1;
    }
    // 2.判断pos是否合法
    if (pos < 0) {
        printf("查询的位置不合法\n");
        return (datatype)-1;
    }
    // 3.通过循环找到查询的位置
    while (h->next) {
        if (pos != 0) {
            pos--;
            h = h->next;
        } else {
            // 查询到了位置
            data = h->next->data;
            return data;
        }
    }
    printf("查询的位置不合法\n");
    return (datatype)-1;
}
单链表的位置更新数据

int LinkListUpdateDataByPos(linklist_t* h, int pos, datatype data)
{
    // 1.判空
    if (LinkListIsEmpty(h)) {
        printf("链表是空,位置更新失败\n");
        return -1;
    }
    // 2.判断pos是否合法
    if (pos < 0) {
        printf("更新的位置不合法\n");
        return -1;
    }
    // 3.通过循环找到更新的位置
    while (h->next) {
        if (pos != 0) {
            pos--;
            h = h->next;
        } else {
            // 更新数据
            h->next->data = data;
            return 0;
        }
    }
    printf("更新的位置不合法\n");
    return -1;
}
单链表的逆序(进行一遍头插)

h-10-20-30-40-
​h- tmp-10-20-30-40
h-10 tmp-20-30-40
​h-20-10 tmp-30-40

void LinkListReverse(linklist_t* h)
{
    linklist_t *tmp,*s;
    //先让tmp指向h的下一个节点
    tmp = h->next;
    //将h->next赋值为NULL
    h->next = NULL;

    //如果tmp不为NULL,循环继续
    while(tmp){
        s = tmp;
        tmp = s->next;
        s->next = h->next;
        h->next = s;
    }
}
对单链表进行排序

在这里插入图片描述

void LinkListInsertSort(linklist_t* h)
{
    linklist_t *tmp, *s, *th = h;
    // 先让tmp指向h的下一个节点
    tmp = h->next;
    // 将h->next赋值为NULL
    h->next = NULL;

    // 如果tmp不为NULL,循环继续
    while (tmp) {
        //从tmp中取到开头的节点
        s = tmp;
        tmp = s->next;
  
        //如果h没有到结尾,并且s节点的数据大于h中的数据,就让h往后走
        while (h->next != NULL && s->data > h->next->data) {
            h = h->next;
        }
  
        //将s插入到当前的位置
        s->next = h->next;
        h->next = s;
        
        //恢复h到开头的位置
        h = th;
    }
}

单链表的整体代码

linklist.h

#ifndef __LINKLIST_H__
#define __LINKLIST_H__

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

#define datatype int
typedef struct node{
    datatype data;
    struct node *next;  
    //这个指针指向的是下一个节点,节点是结构体类型,所以指针也要定义成结构体指针
}linklist_t;

linklist_t *LinkListCreate(void);
int LinkListInsertHead(linklist_t *h,datatype data);
int LinkListInsertTail(linklist_t *h,datatype data);
int LinkListInsertByPos(linklist_t *h,int pos,datatype data);
void LinkListShow(linklist_t *h);
datatype LinkListDeleteHead(linklist_t* h);
datatype LinkListDeleteTail(linklist_t* h);
datatype LinkListDeletePos(linklist_t* h,int pos);
datatype LinkListCheckDataByPos(linklist_t* h,int pos);
int LinkListUpdateDataByPos(linklist_t* h,int pos,datatype data);
void LinkListReverse(linklist_t* h);
void LinkListInsertSort(linklist_t* h);
#endif

linklist.c

#include "linklist.h"

linklist_t* LinkListCreate(void)
{
    linklist_t* h;

    h = (linklist_t*)malloc(sizeof(*h));
    if (h == NULL) {
        printf("malloc head memory error\n");
        return NULL;
    }

    // 将数据赋值为0
    h->data = (datatype)0;
    // 将指针域指向NULL
    h->next = NULL;

    return h;
}
/*
 *功能:单链表头插法插入节点
 *参数:
 *	@h:单链表的头
 *   @data:被插入的数据
 *返回值:成功返回0,失败返回-1
 */
int LinkListInsertHead(linklist_t* h, datatype data)
{
    linklist_t* tmp;
    // 1.分配节点的内存,将data放到这个节点的数据域中
    tmp = (linklist_t*)malloc(sizeof(*tmp));
    if (tmp == NULL) {
        printf("alloc node memory error\n");
        return -1;
    }
    tmp->data = data;

    // 2.将这个节点通过头插法的方式插入到链表中即可
    // 步骤1:让tmp的next指向h的后一个节点
    tmp->next = h->next;
    // 步骤2:将h的next指针域指向tmp
    h->next = tmp;

    return 0;
}

// 思想:判断h->next是否等于NULL,如果h->next为NULL说明链表没有要访问的成员了。
//    :如果h->next不是NULL,说明链表不为空,可以将它的数据域打印出来
//    :printf("data:%d\n",h->next->data);
//    :执行完上述的步骤之后将h->next赋值给h   h = h->next;
//    :在循环上述步骤
void LinkListShow(linklist_t* h)
{
    while (h->next) {
        printf("-%d", h->next->data);
        h = h->next;
    }
    printf("-\n");
}

int LinkListInsertTail(linklist_t* h, datatype data)
{
    linklist_t* tmp;
    // 1.分配节点的内存,将data放到这个节点的数据域中
    tmp = (linklist_t*)malloc(sizeof(*tmp));
    if (tmp == NULL) {
        printf("alloc node memory error\n");
        return -1;
    }
    tmp->data = data;

    // 2.让h走到最后一个节点的位置
    while (h->next) {
        h = h->next;
    }

    // 3.将这个节点通过头插法的方式插入到链表中即可
    // 步骤1:让tmp的next指向h的后一个节点
    tmp->next = h->next;
    // 步骤2:将h的next指针域指向tmp
    h->next = tmp;

    return 0;
}

// ①判断pos是否等于0,如果pos为零,直接将节点插入在当前节点的位置,退出
// ②如果pos不为0, 1.pos--  2.h = h->next。
// ③ 循环执行上述的①和②步骤,如果输入pos太大(不合法)链表到结尾了也要退出循环
int LinkListInsertByPos(linklist_t* h, int pos, datatype data)
{
    linklist_t* tmp;
    // 1.判断pos是否合法
    if (pos < 0) {
        printf("插入的位置不合法\n");
        return -1;
    }

    // 2.通过循环找出插入节点的位置
    while (h) {
        if (pos != 0) {
            pos--;
            h = h->next;
        } else {
            // 3.找到了插入节点的位置,将节点插入即可。

            tmp = (linklist_t*)malloc(sizeof(*tmp));
            if (tmp == NULL) {
                printf("malloc memory error\n");
                return -1;
            }
            tmp->data = data;

            tmp->next = h->next;
            h->next = tmp;
            return 0;
        }
    }

    printf("插入的位置不合法\n");
    return -1;
}
// 如果链表是空这个函数返回1,否则返回0(非空)
int LinkListIsEmpty(linklist_t* h)
{
    return h->next == NULL ? 1 : 0;
}

datatype LinkListDeleteHead(linklist_t* h)
{
    datatype data;
    linklist_t* tmp;
    // 1.判空
    if (LinkListIsEmpty(h)) {
        printf("链表是空,头删失败\n");
        return (datatype)-1;
    }

    // 2.让tmp指向h的下一个节点
    tmp = h->next;

    // 3.让h指向tmp的下一个节点
    h->next = h->next->next;

    // 4.释放tmp的内存
    data = tmp->data;
    if (tmp != NULL) {
        free(tmp);
        tmp = NULL;
    }

    return data;
}

datatype LinkListDeleteTail(linklist_t* h)
{
    datatype data;
    linklist_t* tmp;
    // 1.判空
    if (LinkListIsEmpty(h)) {
        printf("链表是空,头删失败\n");
        return (datatype)-1;
    }
    // 2.通过循环让h走到倒数第二个节点的位置
    while (h->next->next) {
        h = h->next;
    }
    // 3.让tmp指向h->next
    tmp = h->next;

    // 4.让h->next指向空
    h->next = h->next->next;

    // 5.释放tmp
    data = tmp->data;
    if (tmp != NULL) {
        free(tmp);
        tmp = NULL;
    }

    return data;
}

datatype LinkListDeletePos(linklist_t* h, int pos)
{
    datatype data;
    linklist_t* tmp;
    // 1.判空
    if (LinkListIsEmpty(h)) {
        printf("链表是空,位置删除失败\n");
        return (datatype)-1;
    }
    // 2.判断pos是否合法
    if (pos < 0) {
        printf("删除的位置不合法\n");
        return (datatype)-1;
    }

    // 3.通过循环找出删除节点的位置
    while (h->next) {
        if (pos != 0) {
            pos--;
            h = h->next;
        } else {
            // 4.找到了删除节点的位置,将节点删除即可。
            tmp = h->next;
            h->next = h->next->next;

            data = tmp->data;
            if (tmp != NULL) {
                free(tmp);
                tmp = NULL;
            }
            return data;
        }
    }

    printf("删除的位置不合法\n");
    return (datatype)-1;
}

datatype LinkListCheckDataByPos(linklist_t* h, int pos)
{
    datatype data;
    // 1.判空
    if (LinkListIsEmpty(h)) {
        printf("链表是空,位置查询失败\n");
        return (datatype)-1;
    }
    // 2.判断pos是否合法
    if (pos < 0) {
        printf("查询的位置不合法\n");
        return (datatype)-1;
    }
    // 3.通过循环找到查询的位置
    while (h->next) {
        if (pos != 0) {
            pos--;
            h = h->next;
        } else {
            // 查询到了位置
            data = h->next->data;
            return data;
        }
    }
    printf("查询的位置不合法\n");
    return (datatype)-1;
}

int LinkListUpdateDataByPos(linklist_t* h, int pos, datatype data)
{
    // 1.判空
    if (LinkListIsEmpty(h)) {
        printf("链表是空,位置更新失败\n");
        return -1;
    }
    // 2.判断pos是否合法
    if (pos < 0) {
        printf("更新的位置不合法\n");
        return -1;
    }
    // 3.通过循环找到更新的位置
    while (h->next) {
        if (pos != 0) {
            pos--;
            h = h->next;
        } else {
            // 更新数据
            h->next->data = data;
            return 0;
        }
    }
    printf("更新的位置不合法\n");
    return -1;
}
void LinkListReverse(linklist_t* h)
{
    linklist_t *tmp, *s;
    // 先让tmp指向h的下一个节点
    tmp = h->next;
    // 将h->next赋值为NULL
    h->next = NULL;

    // 如果tmp不为NULL,循环继续
    while (tmp) {
        s = tmp;
        tmp = s->next;

        s->next = h->next;
        h->next = s;
    }
}

void LinkListInsertSort(linklist_t* h)
{
    linklist_t *tmp, *s, *th = h;
    // 先让tmp指向h的下一个节点
    tmp = h->next;
    // 将h->next赋值为NULL
    h->next = NULL;

    // 如果tmp不为NULL,循环继续
    while (tmp) {
        //从tmp中取到开头的节点
        s = tmp;
        tmp = s->next;
  
        //如果h没有到结尾,并且s节点的数据大于h中的数据,就让h往后走
        while (h->next != NULL && s->data > h->next->data) {
            h = h->next;
        }
  
        //将s插入到当前的位置
        s->next = h->next;
        h->next = s;
        
        //恢复h到开头的位置
        h = th;
    }
}

main.c

#include "linklist.h"

int main(int argc, char const* argv[])
{
    linklist_t* h;
    // 1.创建单链表
    h = LinkListCreate();
    if (h == NULL) {
        printf("create linklist error\n");
        return -1;
    }

    // 2.通过头插法插入数据
    LinkListInsertHead(h, 30);
    LinkListInsertHead(h, 20);
    LinkListInsertHead(h, 10);
    // 3.遍历链表中的成员
    LinkListShow(h);

    // 4.尾部插入成员
    //  LinkListInsertTail(h,60);
    //  LinkListInsertTail(h,70);
    //  LinkListInsertTail(h,80);
    //  LinkListShow(h);

    // 5.位置插入
    LinkListInsertByPos(h, 2, 555);
    LinkListShow(h);

    // 6.头删
    //  printf("delete head = %d\n",LinkListDeleteHead(h));
    //  printf("delete head = %d\n",LinkListDeleteHead(h));
    //  printf("delete head = %d\n",LinkListDeleteHead(h));
    //  LinkListShow(h);

    // // 7.尾删
    // printf("delete tail = %d\n", LinkListDeleteTail(h));
    // printf("delete tail = %d\n", LinkListDeleteTail(h));
    // printf("delete tail = %d\n", LinkListDeleteTail(h));
    // LinkListShow(h);
    // 8.位置删除
    // printf("delete pos = %d\n", LinkListDeletePos(h, 4));
    // LinkListShow(h);

    printf("Check pos = %d\n", LinkListCheckDataByPos(h, 2));

    LinkListUpdateDataByPos(h, 4, 999);
    LinkListShow(h);

    LinkListReverse(h);
    LinkListShow(h);

    LinkListInsertSort(h);
    LinkListShow(h);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值