文章目录
有头双向链表
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;
}