当前位置:首页 > C语言
线性表之有头结点的单向链表实验
来源:靑龍一笑的博客  作者:靑龍一笑  发布时间:2022-10-05 11:22:22  点击量:160  评论:0

    有头结点的单向链表的实现如下:

1、linklist.h 文件

/*
 * 线性表的链式存储结构(有头结点的单向链表)
 */
#ifndef LINKLIST_H__
#define LINKLIST_H__

// 单向链表元素类型定义
typedef int datatype;

/*
 * 单向链表结构
 */
typedef struct node_st {
    datatype data; // 数据结点
    struct node_st *next; // 指向下一个结点的指针
}linklist;

/*
 * 初始化,创建单向链表
 * 返回结构体类型的指针
 */
linklist *linklist_create();

/*
 * 初始化,创建单向链表
 * 传入一个二级指针的方式
 */
void linklist_init(linklist **);

/*
 * 销毁单向链表
 * 释放单向链表所占内存
 */
void linklist_destroy(linklist *);

/*
 * 判断单向链表是否为空
 */
int linklist_is_empty(linklist *);

/*
 * 获取单向链表中元素的个数
 */
int linklist_length(linklist *);

/*
 * 往单向链表中指定位置插入数据
 */
int linklist_insert_by_pos(linklist *, int, datatype *);

/*
 * 往单向链表中插入数据
 */
int linklist_insert(linklist *, datatype *);

/*
 * 从单向链表中指定位置删除数据
 */
int linklist_delete_by_pos(linklist *, int, datatype *);

/*
 * 从单向链表中删除数据
 */
int linklist_delete(linklist *, datatype *);

/*
 * 遍历单向链表
 */
void linklist_display(linklist *);

#endif

2、linklist.c 文件

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

#include "linklist.h"

/*
 * 初始化,创建单向链表
 * 返回结构体类型的指针
 */
linklist *linklist_create() {
    linklist *head; // 头结点
    head = malloc(sizeof(*head));
    if (head == NULL) {
        return NULL;
    }

    // 刚初始化的单向链表的头结点没有指向的下一个结点
    head->next = NULL;
    return head;
}

/*
 * 初始化,创建单向链表
 * 传入一个二级指针的方式
 */
void linklist_init(linklist **head) {
    *head = malloc(sizeof(**head));
    if (*head == NULL) {
        return; // 无返回值
    }

    // 刚初始化的单向链表的头结点没有指向的下一个结点
    (*head)->next = NULL;
    return; // 无返回值
}

/*
 * 销毁单向链表
 * 释放单向链表所占内存
 */
void linklist_destroy(linklist *head) {
    linklist *node, *next; // 当前结点,下一个结点
    for (node = head->next; node != NULL; node=next) {
        // 由于是一个有头的单向链表,第一个结点是头结点所指向的下一个结点
        next = node->next; // 获取下一个结点
        free(node); // 释放当前结点
    }
    free(head); // 释放头结点
    return; // 无返回值
}

/*
 * 判断单向链表是否为空
 */
int linklist_is_empty(linklist *head) {
    if (head->next == NULL) {
        // 头结点没有任何指向,说明链表为空
        return 0;
    }

    // 非空
    return -1;
}

/*
 * 获取单向链表中元素的个数
 */
int linklist_length(linklist *head) {
    linklist *node; // 当前结点
    int length = 0; // 链表长度,即链表元素的个数

    // 确保链表存在,且不为空表
    if (head == NULL || head->next == NULL) {
        return -1;
    }

    // 既然不是空表,必然存在第一个结点
    node = head->next;

    while (node != NULL) {
        length++; // 长度加 1
        node = node->next; // 指向到下一个结点
    }

    // 返回链表长度
    return length;
}

/*
 * 往单向链表中指定位置插入数据
 */
int linklist_insert_by_pos(linklist *head, int pos, datatype *data) {
    int i = 0; // 循环索引值
    linklist *prev = head, *node = NULL; // 前一个结点,新结点

    if (pos < 0) {
        // 插入的位置不对
        return -1;
    }

    // 通过 while 循环,走到要插入的位置,并且得到前一个结点
    while (i < pos && prev != NULL) {
        prev = prev->next;
        i++;
    }

    if (prev) {
        // 存在前一个结点
        // 给新结点开辟内存空间
        node = malloc(sizeof(*node));
        if (node == NULL) {
            // 内存空间分配失败
            return -2;
        }
        // 给新结点赋值
        node->data = *data;
        // 新结点的下一个结点,指向到前一个结点的下一个结点
        node->next = prev->next;
        // 前一个结点指向到新结点
        prev->next = node;

        // 成功插入
        return 0;
    } else {
        // 前一个结点不存在
        // 插入的位置不对
        return -1;
    }
}

/*
 * 往单向链表中插入数据
 */
int linklist_insert(linklist *head, datatype *data) {
    linklist *prev = head, *node; // 前一个结点,新结点

    // 通过 while 循环,走到要插入的位置
    while (prev->next && prev->next->data < *data) {
        // 升序插入
        prev = prev->next;
    }

    // 给新结点开辟内存空间
    node = malloc(sizeof(*node));
    if (node == NULL) {
        // 内存空间分配失败
        return -1;
    }

    // 给新结点赋值
    node->data = *data;
    // 新结点的下一个结点,指向到前一个结点的下一个结点
    node->next = prev->next;
    // 前一个结点指向到新结点
    prev->next = node;

    // 成功插入
    return 0;
}

/*
 * 从单向链表中指定位置删除数据
 */
int linklist_delete_by_pos(linklist *head, int pos, datatype *data) {
    int i = 0; // 循环索引值
    linklist *prev = head, *node; // 前一个结点,要删除的结点
    *data = -1; // 要删除的数据

    if (pos < 0) {
        // 删除元素的位置不对
        return -1;
    }

    // 通过 while 循环,走到要删除的位置,并且得到前一个结点
    while (i < pos && prev != NULL) {
        prev = prev->next;
        i++;
    }

    if (prev) {
        // 存在前一个结点
        // 要删除的结点指向到前一个结点的下一个结点,即得到要删除的结点
        node = prev->next;
        // 获取要删除的数据
        *data = node->data;
        // 将前一个结点的下一个结点,指向到要删除结点的下一个结点
        prev->next = node->next;
        // 释放要删除结点所占内存
        free(node);
        // 将要删除结点指向为 NULL
        node = NULL;

        // 删除成功
        return 0;
    } else {
        // 前一个结点不存在
        // 删除元素的位置不对
        return -1;
    }
}

/*
 * 从单向链表中删除数据
 */
int linklist_delete(linklist *head, datatype *data) {
    linklist *prev = head, *node; // 前一个结点,要删除的结点

    // 通过 while 循环,走到要删除的位置
    while (prev->next && prev->next->data != *data) {
        prev = prev->next;
    }

    if (prev->next == NULL) {
        // 不存在要删除的数据
        return -1;
    } else {
        // 要删除的结点指向到前一个结点的下一个结点,即得到要删除的结点
        node = prev->next;
        // 将前一个结点的下一个结点,指向到要删除结点的下一个结点
        prev->next = node->next;
        // 释放要删除结点所占内存
        free(node);
        // 将要删除结点指向为 NULL
        node = NULL;

        // 删除成功
        return 0;
    }
}

/*
 * 遍历单向链表
 */
void linklist_display(linklist *head) {
    linklist *node = head->next; // 当前结点

    if (linklist_is_empty(head) == 0) {
        // 空链表
        return; // 无返回值
    }

    while (node != NULL) {
        printf("%d ", node->data); // 打印当前结点的数据
        node = node->next; // 指向到下一个结点
    }
    printf("\n");

    return; // 无返回值
}

3、main.c 文件

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

#include "linklist.h"

int main() {
    int i; // 循环索引值
    int ret; // 返回状态值
    int len; // 链表长度
    linklist *list; // 单向链表
    // 两条测试数据
    datatype data1[] = {12, 81, 23, 76, 34, 62};
    datatype data2[] = {73, 45, 15, 61, 99, 56};
    // 指定要删除的数据
    int del_data = 76;
    // 保存要删除的数据
    datatype delete_data;

    /* 初始化单向链表 */
    list = linklist_create(); // 方法一
    //linklist_init(&list); // 方法二

    if (list == NULL) {
        fprintf(stderr, "初始化单向链表 list 失败!\n");
        exit(1);
    }

    /* 往单向链表中插入数据 */
    // 方法一:按升序插入(前提是新链表,或已经排序好的链表)
    for (i = 0; i < sizeof(data1)/sizeof(*data1); i++) {
        if ((ret = linklist_insert(list, &data1[i])) != 0) {
            if (ret == -1) {
                fprintf(stderr, "单向链表 list 数据插入的位置有误。\n");
            } else if (ret == -2) {
                fprintf(stderr, "内存空间分配失败。\n");
            } else {
                fprintf(stderr, "发生未知错误。\n");
            }
            exit(1);
        }
    }

    /* 遍历单向链表 */
    printf("单向链表 list 中的元素:");
    linklist_display(list);

    /* 获取单向链表中元素的个数 */
    len = linklist_length(list);
    printf("单向链表 list 中元素的个数:%d\n", len);

    /* 往单向链表中插入数据 */
    // 方法二:指定位置插入,这里按顺序依次插入
    for (i = 0; i < sizeof(data2)/sizeof(*data2); i++) {
        if ((ret = linklist_insert_by_pos(list, len + i, &data2[i])) != 0) {
            if (ret == -1) {
                fprintf(stderr, "单向链表 list 数据插入的位置有误。\n");
            } else if (ret == -2) {
                fprintf(stderr, "内存空间分配失败。\n");
            } else {
                fprintf(stderr, "发生未知错误。\n");
            }
            exit(1);
        }
    }

    /* 遍历单向链表 */
    printf("单向链表 list 中的元素:");
    linklist_display(list);

    /* 从单向链表中删除数据 */
    if ((ret = linklist_delete(list, &del_data)) != 0) {
        if (ret == -1) {
            printf("不存在要删除的数据。\n");
        } else {
            fprintf(stderr, "发生未知错误。\n");
            exit(1);
        }
    }

    ret = linklist_delete_by_pos(list, 2, &delete_data);
    if (ret == 0) {
        printf("数据 %d 已从单向链表 list 中删除。\n", delete_data);
    } else if (ret == -1) {
        fprintf(stderr, "从单向链表 list 中删除元素的位置有误。\n");
        exit(1);
    } else {
        fprintf(stderr, "发生未知错误。\n");
        exit(1);
    }

    /* 遍历单向链表 */
    printf("单向链表 list 中的元素:");
    linklist_display(list);

    /* 销毁单向链表 */
    linklist_destroy(list);

    exit(0);
}

4、Makefile 文件

OBJS=main.o linklist.o
CC=gcc
CFLAGS+=-c -Wall -g
linklist:$(OBJS)
    $(CC) $^ -o $@
%.o:%.c
    $(CC) $^ $(CFLAGS) -o $@
clean:
    $(RM) *.o linklist -r

    执行效果:

root@RicenOS:~# make
gcc main.c -c -Wall -g -o main.o
gcc linklist.c -c -Wall -g -o linklist.o
gcc main.o linklist.o -o linklist
root@RicenOS:~# ./linklist
单向链表 list 中的元素:12 23 34 62 76 81
单向链表 list 中元素的个数:6
单向链表 list 中的元素:12 23 34 62 76 81 73 45 15 61 99 56
数据 34 已从单向链表 list 中删除。
单向链表 list 中的元素:12 23 62 81 73 45 15 61 99 56
版权所有 © 2005-2023 靑龍一笑的博客  Powered by C.S.Ricen
Copyright © 2005-2023 by www.ricensoftwares.com.cn  All Rights Reserved.

欢迎光临本站,这里是靑龍一笑的博客。

因资金匮乏,本站已迁到国外的免费空间,可能导致本站的访问速度较慢,由此给您带来的不便,敬请谅解。

您可以通过下方的“支持本站建设”链接,给本站提供资金支持。

Free Web Hosting