链式结构线性表(链表)的实现

链式结构线性表(链表)

前面我们讲的线性表的顺序存储结构。 它是有缺点的, 最大的缺点就是插入和删除时
需要移动大量元素, 这显然就需要耗费时间。 能不能想办法解决呢?

线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素, 这组
存储单元可以是连续的, 也可以是不连续的。 这就意味着, 这些数据元素可以存在内
存未被占用的任意位置(如图所示) 。

构造一个这样的链式结构线性表:
表就不单单是只存储节点本身的信息,还要存储后一个节点的位置信息.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hv0hbePb-1691858206372)(../../../AppData/Roaming/Typora/typora-user-images/image-20230812215055052.png)]

头指针与头结点的异同

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ayCFRCdw-1691858206374)(../../../AppData/Roaming/Typora/typora-user-images/image-20230812215200728.png)]

链式结构线性表(链表)代码实现

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h> //添加bool
#include <string.h>
// #include "linked_list.h"

/**
 * 单链表
 */
typedef struct node
{
    int data;          // 存放该点的数据
    struct node *next; // 保存下一个节点的地址
} Node, LinkedList;

LinkedList *InitList();                               // 初始化,建立一个空的线性表
bool ListEmpty(LinkedList *L);                        // 判断线性表是不是空表
void ClearList(LinkedList *L);                        // 清空线性表
int GetList(LinkedList *L, int i, int e);             // 获取线性表L,位置i上的数据,返回给e;
int PrintList(LinkedList *L);                         // 打印List
bool IsPositionValid(LinkedList *L, int mode, int i); // 检查操作位置是否合法

/**
 *  增 删 改 查
 */
LinkedList *InsertElement(LinkedList *L, int i, int e); // 向List中的位置 i 插入一个元素
LinkedList *DelectElement(LinkedList *L, int i);        // 删除List中的位置 i 的元素
LinkedList *ChangeElement(LinkedList *L, int i, int e); // 修改List中的位置 i 的元素 为e
int LocateElement(LinkedList *L, int e);                // 在线性表在查找与e值相等的元素,并且返回元素下标

int main()
{
    LinkedList *List;
    List = InitList();
    for (int i = 0; i < 5; i++)
    {
        InsertElement(List, i, i);
    }

    // DelectElement(List, 5);
    // ChangeElement(List, 2, 100);
    LocateElement(List, 100);

    PrintList(List);

    return 0;
}

LinkedList *InitList()
{
    LinkedList *p = NULL;
    p = malloc(sizeof(LinkedList)); // 给链表设置一个头节点,便于链表的操作
    p->next = NULL;                 // 设置头节点指向的下一个节点为NULL(就是没有)
    return p;
}

// 清空线性表
void ClearList(LinkedList *L)
{
    Node *current = L;
    while (current != NULL) // 循环判断,free()释放每一个节点的内存。直到到达最后一个节点current == NULL
    {
        Node *next_node = current->next;
        free(current);
        current = next_node;
    }
    L->next = NULL; // 将头指针置为空
}

// 检查操作位置是否合法
bool IsPositionValid(LinkedList *L, int mode, int i)
{
    if (i < 0)
    {
        printf("操作位置非法: 位置不能小于0\n");
        return false;
    }

    int count = 0;
    Node *current = L->next;
    while (current != NULL)
    {
        count++;
        current = current->next;
    }

    /**
     * 为什么要设计该部分:
     * 因为这个操作(Insert,Delete...)是否合理,不同的情况(mode)下判断的条件是不同的
     * 例如:在插入操作中,假设原来的链表有5个数据(位置默认从0开始)。
     *      那么在位置5插入数据,这个位置是合法的--->(因为位置4有数据)该操作也就是在链表的末尾插入。
     *      如果是在位置5删除数据呢? 显然这个位置就是不合法的(因为位置5都没有数据)
     */
    switch (mode)
    {
    case 1:
        if (i > count)
        {
            printf("插入操作的位置非法: 位置超出链表插入的最大长度\n");
            return false;
        }
        break;

    case 2:
        if (i >= count)
        {
            printf("删除操作的位置非法: 位置超出链表的实际长度\n");
            return false;
        }
        break;
    case 3:
        if (i > count - 1)
        {
            printf("修改操作的位置非法: 位置超出链表的实际长度\n");
            return false;
        }
        break;
        
    default:
        break;
    }

    return true;
}

// 获取线性表L,位置i上的数据,返回给e;    默认头节点后的第一个点为位置0
int GetList(LinkedList *L, int i, int e)
{
    LinkedList *head = L;
    LinkedList *scan = L->next;

    // 先用scan指针扫描一边线性表,记录线性表节点的个数。判断查找的位置是否合理
    int count = 0;
    while (NULL != scan)
    {
        count++;
        scan = scan->next;
    }

    scan = L->next; // scan 指针复位

    if (i < 0 || i > (count - 1))
    {
        printf("获取的位置非法\n");
        return 0;
    }

    for (int j = 0; j <= i; j++)
    {
        scan = scan->next;
    }
    e = scan->data; // e保存节点的data

    return 0;
}

bool ListEmpty(LinkedList *L)
{
    LinkedList *scan = L; // 设置一个和扫描指针, 指向链表(也就是指向链表的头节点)

    if (NULL == scan->next)
    {
        // printf("该链表为一个空表\n");
        return true;
    }

    return false;
}

// 打印List
int PrintList(LinkedList *L)
{
    LinkedList *scan = L; // 设置一个和扫描指针, 指向链表(也就是指向链表的头节点)

    if (NULL == scan->next)
    {
        printf("该链表为一个空表\n");
        return 0;
    }
    else
    {
        scan = scan->next; // 头节点后面有元素,直接把扫描指针 指向下一个节点
    }

    while (scan)
    {
        printf("%d ", scan->data);
        scan = scan->next;
    }
    printf("\n");

    return 0;
}

// 向链表中的位置 i 插入一个元素   :默认 头节点的下一个节点(第一个节点)为位置0
LinkedList *InsertElement(LinkedList *L, int i, int e)
{
    if (!IsPositionValid(L, 1, i))
    {
        return L; // 插入位置非法,直接返回原链表
    }

    Node *new_node = malloc(sizeof(Node));
    new_node->data = e;

    Node *prev = L;          // 指向前一个节点的指针
    Node *current = L->next; // 指向当前节点的指针(从第一个元素开始)

    // 找到位置 i 前面的一个节点
    for (int j = 0; j < i && current != NULL; j++)
    {
        prev = current;
        current = current->next;
    }

    // 在前一个节点后面插入新节点
    prev->next = new_node;
    new_node->next = current;

    return L;
}

// 删除链表中的位置 i 的元素
LinkedList *DelectElement(LinkedList *L, int i)
{
    if (!IsPositionValid(L, 2, i))
    {
        return L; // 删除位置非法,直接返回原链表
    }

    LinkedList *prev = L;
    LinkedList *current = L->next;

    for (int j = 0; j < i && current != NULL; j++)
    {
        prev = current;
        current = current->next;
    }

    prev->next = current->next;
    free(current);

    return L;
}

// 修改链表中的位置 i 的元素 为e
LinkedList *ChangeElement(LinkedList *L, int i, int e)
{
    if (!IsPositionValid(L, 3, i))
    {
        return L; // 删除位置非法,直接返回原链表
    }

    LinkedList *current = L->next;

    for (int j = 0; j < i && current != NULL; j++)
    {
        current = current->next;
    }

    current->data = e;

    return L;
}

// 在链表中查找与e值相等的元素,并且返回元素下标
int LocateElement(LinkedList *L, int e)
{
    LinkedList *current = L->next;
    int count = 0;
    while (current)
    {
        if (current->data == e)
        {
            printf("查找到值与%d相等的元素,位置为%d\n", e, count);
            return 0;
        }
        count++;
        current = current->next;
    }

    printf("链表中未查找到相关的元素\n");
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值