十二、有头双向链表实现【增删改查】

有头双向链表

1. 有头双向链表头插添加实现

在这里插入图片描述

/*
添加新的 Node 到当前双向链表的开端

@param head           Linked Head 链表头指针
@param new_node Node  Node 结点指针 
@return 添加成功,返回 EXIT_SUCCESS 否则返回 EXIT_FAILURE
*/
int add_first(LinkedHead *head, Node *new_node)
{
    // 判断用户提供的链表头和结点数据是否为 NULL
    if (NULL == head || NULL == new_node)
    {
        printf("用户提供的参数有误!!");
        return EXIT_FAILURE;
    }
    
    if (head->size != 0)
    {
        // 当前链表中有其他结点
        // 【重点】建立新结点和原结点的连接关系
        // 新结点中的 next 存储原本第一个结点地址
        new_node->next = head->first;

        // 原本第一个结点的 prev 保存新节点地址
        head->first->prev = new_node;

        // 头结点 head 中的 first 存储 new_node 结点地址
        head->first = new_node;
    }
    else
    {
        // 当前链表中没有其他结点
        // 链表头中 first 存储当前 new_node 地址
        head->first = new_node;
        // 链表头中 last 存储当前 new_node 地址
        head->last = new_node;
    }

    head->size += 1;

    return EXIT_SUCCESS;
}
2. 有头双向链表最后一个元素删除实现

在这里插入图片描述

/*
删除当前链表中的最后一个结点

@param head LinkedHead 链表头指针
@return 删除成功,返回 EXIT_SUCCESS ,否则返回 EXIT_FAILURE
*/
int remove_last(LinkedHead *head)
{
    // 如果用户提供的链表头为 NULL ,无法进行任何的代码操作
    if (NULL == head)
    {
        printf("用户提供的参数有误! %s:%d\n",__FILE__, __LINE__);
        return EXIT_FAILURE;
    }

    //情况一: 当前链表中没有任何一个元素 0 == size ,终止函数运行
    if (0 == head->size)
    {
        return EXIT_FAILURE;
    }
    
    Node *free_node = head->last;

    // 情况三: 当前链表中有多个元素
    if (head->size > 1)
    {
        // 链表中存在元素
        // 首先利用临时变量,存储当前被删除元素的上一个元素
        Node *temp = head->last->prev;

        // 断开连接 原本最后一个结点的 prev 赋值为NULL
        head->last->prev = NULL;
        // 断开连接 原本最后一个结点的上一个结点的 next 赋值为 NULL
        temp->next = NULL;

        // 将 head 中的 last 指向 temp
        head->last = temp;
    }
    // 情况二: 当前链表中有一个元素
    else
    {
        // 链表中有且只有一个元素
        // 链表头中的 frist 和 last 同时赋值为 NULL
        head->first = NULL;
        head->last = NULL;
    }
    // 删除操作执行完毕,需要对 size -= 1
    head->size -= 1;

    return free_node_memory(free_node);  
}
3. 有头双向链表第一个元素删除实现

在这里插入图片描述

/*
删除当前链表中的第一个结点

@param head LinkedHead 链表头指针
@return 删除成功,返回 EXIT_SUCCESS ,否则返回 EXIT_FAILURE
*/
int remove_first(LinkedHead *head)
{
    // 如果用户提供的链表头为 NULL 无法进行任何的代码操作
    if (NULL == head)
    {
        printf("用户提供的参数有误! %s:%d\n",__FILE__, __LINE__);
        return EXIT_FAILURE;
    }
    // 情况一: 当前链表中没有任何一个元素 0 == size ,终止函数运行。
    if (0 == head->size)
    {
        return EXIT_FAILURE;
    }

    Node *free_node = head->first;

    // 情况三: 当前链表中有多个元素
    if (head->size > 1)
    {
        // 链表中存在多个元素
        // 首先利用临时变量,存储当前被删除的下一个元素
        Node *temp = head->first->next;

        // 断开连接 原本第一个结点的 next 赋值为 NULL
        head->first->next = NULL;
        // 断开连接 第一个结点的 prev 赋值为 NULL
        temp->prev = NULL;

        // 将 head 中 first 指向 temp
        head->first = temp;
    }
    // 情况二: 当前链表中有一个元素
    else
    {
        // 链表中有且只有一个元素
        // 链表中的 first 和 last 同时赋值为 NULL
        head->first = NULL;
        head->last = NULL;
    }
    
    // 删除操作执行完毕,需要对 size -= 1
    head->size -= 1;
    return free_node_memory(free_node);
    
}
4. 有头双向链表删除指定下标位置地址添加操作

在这里插入图片描述

/*
在链表中,指定下标位置添加新的 Node 结点

@param head     LinkedHead 链表头指针
@param index    指定添加的下标位置,链表下标参考数组形式,从 0 开始到 size - 1
@param new_node 添加的新结点
@return 添加成功,返回 EXIT_SUCCESS ,否则返回 EXIT_FAILURE
*/
int add(LinkedHead *head, int index, Node *new_node)
{
    // 给定的两个指针数据判断是否为 NULL
    if (NULL == head || NULL == new_node)
    {
        printf("提供的链表或者结点为 NULL, %s:%d\n",__FILE__, __LINE__);
        return EXIT_FAILURE;
    }
    
    // 判断提供的下标数据是否合法
    if (index < 0 || index > head->size)
    {
        printf("提供的下标越界, %s:%d\n", __FILE__, __LINE__);
        return add_first(head, new_node);
    }
    else if (index == head->size)
    {
        // 下标数位 size 采用的添加方式是添加到整个链表末尾
        return add_last(head, new_node);
    }

    // 第二个模块:找到目标下标位置对应的结点地址
    Node *target = find_node(head, index);

    // 第三个模式:目标位置添加新结点
    // 核心步骤:new_node 和当前 target 结点以及 target 上一个结点连接
    new_node->next = target;
    new_node->prev = target->prev;

    // target 上一个结点和当前 new_node 连接
    new_node->prev->next = new_node;

    // 当前 new_node 成为 target 的上一个结点
    target->prev = new_node;

    // 有效元素个顺 += 1
    head->size += 1;

    return EXIT_SUCCESS;
}
5. 有头双向链表删除指定下标元素

在这里插入图片描述

/*
在当前链表中,删除指定下标位置结点

@param head LinkedHead 链表头指针
@param index 指定删除数据的下标位置,链表下标参考数组形式,从 0 开始到 size - 1 结束
@return 删除成功,返回 EXIT_SUCCESS ,否则返回 EXIT_FAILURE
*/
int remove_data(LinkedHead *head, int index)
{
    if (NULL == head)
    {
        printf("提供的链表为 NULL, %s:%d\n", __FILE__, __LINE__);
        return EXIT_FAILURE;
    }

    if (index < 0 || index > head->size -1)
    {
        printf("下标不合法!, %s:%d\n", __FILE__, __LINE__);
        return EXIT_FAILURE;
    }
    
    Node *target = find_node(head, index);

    if (NULL == target)
    {
        return EXIT_FAILURE;
    }
    
    // 删除结点的下一个结点的 prev 赋值为当前删除结点的 prevent
    target->next->prev = target->prev;

    // 删除结点的上一个结点的 next 赋值为当前删除结点的 next
    target->prev->next = target->next;

    // target 数据进行清理和释放操作
    target->next = NULL;
    target->prev = NULL;

    // 有效元素 -1
    head->size -= 1;

    return free_node_memory(target);
}
6. 辅助函数
6.1 结点释放函数
/*
【辅助函数】释放删除操作中需要进行内存释放的结点
注意:
    需要释放 Student 类型空间

@param node 需要被释放的结点地址
@return 释放成功,返回 EXIT_SUCCESS ,否则返回 EXIT_FAILURE
*/
int free_node_memory(Node *node)
{
    if (NULL == node)
    {
        printf("结点数据为 NULL %s:%d\n", __FILE__, __LINE__);
        return EXIT_FAILURE;
    }

    // 1. 释放 Student 空间
    free(node->stu);
    // 2. node 中的 stu 赋值为 NULL
    node->stu = NULL;
    // 3. 释放 Node 空间
    free(node);


    return EXIT_SUCCESS;
}
6.1.2 指定下标结点地址获取
/*
【辅助函数】根据用户指定的下标位置,找到目标结点地址,如果没有找到,返回 NULL

@param head LinkedHead 链表头指针
@param index 指定下标
@return 搜索成功返回目标 Node 结点地址,否则返回 NULL
*/
Node *find_node(LinkedHead *head, int index)
{
    if (NULL == head)
    {
        printf("提供的链表为 NULL, %s:%d\n", __FILE__, __LINE__);
        return NULL;
    }
    
    if (index < 0 || index > head->size -1)
    {
        printf("下标不合法!, %s:%d\n", __FILE__, __LINE__);
        return NULL;
    }

    Node *target = NULL;

    if (index < head->size / 2)
    {
        // 从链表中的第一个结点开始搜索目标 index 下标对应的结点地址
        target = head->first->next;
        int count = 1;

        // count 作为下标计数器,如果 count == index 找到目标元素
        while (count != index)
        {
            // 未找到目标元素,target 赋值为当前结点的下一个结点。
            target = target->next;
            count++;
        }
    }
    else
    {
        // 从链表中的最后一个结点开始搜索目标 index 下标对应的结点地址
        target = head->last;
        int count = head->size -1;

        while (count != index)
        {
            target = target->prev;
            count--;
        }
    }
    
    return target; 
}
  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值