数据结构与算法 第五天双向循环链表指针操作和实例
双向链表优势:删除某个数据非常方便,不像数组需要一个一个遍历
第一章 双向链表图解【有图有真相】
第二章 双向链表代码编写【宏函数】
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
typedef struct link_node{
int data;//存数据
struct link_node *prev, *next;//上下指针
}node_t;
//不安全的遍历
//后遍历
#define list_for_each(head, pos) for(pos=head->next; pos!=head; pos=pos->next)
//前遍历
#define list_for_each_tail(head, pos) for(pos=hread->prev; pos!=head; pos=pos->prev)
//安全的遍历
#define list_for_each_safe(head, pos, n) for(pos=head->next, n=pos->next; pos!=head; pos=n, n=n->next)
#define list_for_each_safe_tail(head, pos, n) for(pos=head->prev, n=pos->prev; pos!=head; pos=n, n=n->prev)
/*
函数功能:
新建节点
参数:
无
返回值:
成功返回申请的节点内存,失败返回NULL
*/
node_t *request_list_node(void)
{
node_t *new_node;
new_node = malloc(sizeof(node_t));//申请内存
if(new_node == NULL)//判断内存是否申请失败
{
perror("申请节点失败");
return NULL;
}
//初始化操作
new_node->prev = new_node;//让新申请的节点的上一个位置指向自己
new_node->next = new_node;//让新申请的节点的下一个位置指向自己
return new_node;
}
/*
函数功能:【切记双向 双向 双向】
将insert_node这个节点插入到refer_node这个节点之后的位置
参数:
refer_node:是链表中的任意一个节点
insert_node:需要插入的节点
返回值:
无
*/
void insert_node_list(node_t *refer_node, node_t *insert_node)
{
//举例:第一次插入的时候,以此类推
insert_node->prev = refer_node;//插入节点的上一个指针指向头节点
insert_node->next = refer_node->next;//插入节点的下一个指针指向头节点指针以前指向的内容
refer_node->next->prev = insert_node;//头节点的下一个指针指向的那个结构体的上一个指针指向插入的这个节点
refer_node->next = insert_node;//头节点下一个指针指向插入节点
}//第一个插入的放在了最后的形式
/*
函数功能:
将insert_node这个节点插入到refer_node这个节点之前的位置
参数:
refer_node:是链表中的任意一个节点
insert_node:需要插入的节点
返回值:
无
*/
static inline void insert_node_to_list_tail(node_t *refer_node, node_t *insert_node)
{
insert_node->prev = refer_node->prev;
insert_node->next = refer_node;
refer_node->prev->next = insert_node;
refer_node->prev = insert_node;
}//头节点为标准,第一个插入的值放在了最前面
//遍历数组
void display_list_node(node_t *list_head)
{
node_t *pos;
printf("表格已有数据:");
//不安全的遍历 【从头节点的下一个节点依次往后遍历】
list_for_each(list_head, pos)
{
printf("%d ", pos->data);
}
printf("\n");
}
/*
函数功能:
将rm_node这个节点移除出所在的表格
参数:
rm_node:删除出表格的节点地址
返回值:
无
*/
static inline void remove_list_node(node_t *rm_node)
{
//这是相比其他链表很大的优势,不用一个一个找,根据删除的节点,直接删除
//删除节点的上一个指针的下一个指针指向删除节点的下一个
rm_node->prev->next = rm_node->next;
//删除节点的下一个指针的上一个指针指向删除指针的上一个
rm_node->next->prev = rm_node->prev;
}
/*
函数功能:
将rm_node这个节点移除出所在的表格,并且释放其节点内存
参数:
rm_node:删除出表格的节点地址
返回值:
无
*/
void destroy_list_node(node_t *rm_node)
{
remove_list_node(rm_node);
free(rm_node);
}
/*
函数功能:
判断head这个表格是否为空
返回值:
为空返回真,没有空的话返回假
*/
bool is_empty(node_t *head)
{
return head->next == head;
}
/*
函数功能:
搜索指定节点
*/
/*
函数功能:
销毁整条链表
参数:
list_head:需要销毁的链表头节点
返回值:
无
*/
void destroy_link_list(node_t *list_head)
{
node_t *pos;
node_t *n;
//安全的遍历方式,可以任意的pos节点
//for(pos=list_head, n=pos->next; pos!=list_head; pos = n, n=n->next)
list_for_each_safe(list_head, pos, n)
{
printf("free %d \n", pos->data);
free(pos);
}
//上面遍历结束,代表头节点里面的prev和next指针都指向了自己
free(list_head);//头节点也释放
}
int main(void)
{
int input_value;
node_t *list_head, *new_node;//申明两个链表节点的指针变量,其中list_head用来存放链表的头节点,new_node用来临时存放一下新申请的节点
//新建链表头节点
list_head = request_list_node();//【头节其实就是一个标准】
while(1)
{
scanf("%d", &input_value);
if(input_value > 0)
{
//新建节点
new_node = request_list_node();//新申请一个节点
new_node->data = input_value;//将数据存放进去这个节点当中
//将这个新的节点插入到list_head所对应的表格中
insert_node_to_list_tail(list_head, new_node);
}
else if(input_value < 0)
{
//删除指定节点
remove_list_node(-input_value);
//搜索指定节点
break;
}
else ;
//遍历表格
display_list_node(list_head);
}
//销毁链表
destroy_link_list(list_head);
return 0;
}