C语言数据结构–双向链表的实现
双向链表对比单向链表增加了一个指向前一个节点的指针。
双向链表和单向链表节点的对比:
1、单向链表的节点
//节点
typedef struct node{
int val;
struct node* p_next;
}node_t;
//链表的结构体
typedef struct{
//头节点
node_t head;
//尾节点
node_t tail;
}link_t;
2 、 双向链表的节点
typedef struct node{
int val;
struct node* p_next;
struct node* p_prev;
}node_t;
//链表的结构体
typedef struct{
//头节点
node_t head;
//尾节点
node_t tail;
node_t* p_cur;
}link_t;
3 、双向链表的相关操作
①初始化函数
void link_init(link_t* p_link){
p_link->head.p_next = &p_link->tail;
p_link->tail.p_next = NULL;
p_link->tail.p_prev = &p_link->head;
p_link->head.p_prev = NULL;
//p_cur是否是空指针判断链表是否处于遍历过程中
p_link->p_cur = NULL;
}
②清理函数:主要是完成节点的释放工作,在该函数中定义了三个指针p_fist,p_mid,p_last,p_first始终指向头结点,将p_mid从链表中摘取出来,完成内存的释放工作。
void link_deinit(link_t* p_link){
node_t* p_first=NULL,*p_mid=NULL,*p_last=NULL;
//当链表的头节点向后不是指向尾节点
p_link->p_cur = NULL;
while(p_link->head.p_next != &p_link->tail){
p_first = &p_link->head;
p_mid = p_first->p_next;
p_last = p_mid->p_next;
//把p_mid指向的节点从链式物理结构中摘取出来
p_first->p_next = p_last;
p_last->p_prev = p_first;
free(p_mid);
p_mid = NULL;
}
}
③判断链表是否为空的函数:只要头结点指向尾节点,则链表就为空
int linkIsEmpty(const link_t* p_link){
//头节点指向尾节点时,链表为空
return p_link->head.p_next == &p_link->tail;
}
④获取链表中元素的个数的函数:遍历链表,只要p_mid没有到尾节点,就在元素的个数+1
//获取链表中的数字个数
int link_size(const link_t* p_link){
int count = 0;
const node_t* p_tmp = NULL,*p_first = NULL,*p_mid = NULL,*p_last = NULL;
for(p_tmp=&p_link->head;p_tmp!=&p_link->tail;p_tmp=p_tmp->p_next){
p_first = p_tmp;
p_mid = p_first->p_next;
p_last = p_mid->p_next;
if(p_mid != &p_link->tail)
count++;
}
return count;
}
⑤ 链表的插入操作:包括在链表的尾部插入数字,在链表的最前面插入数字,在链表的中间插入
在链表的尾部插入数字:由于是双向链表`,可以通过指向相邻位置的三个指针的第一个指针指向尾节点的前一个指针,中间的指针就指向尾节点,在第一个指针和中间指针之间插入节点即可。
//在最后面加入数字的函数(在链表中插入节点)
int link_append(link_t* p_link,int val){
node_t* p_node = NULL;
node_t* p_first = NULL,*p_mid = NULL,*p_last = NULL;
p_link->p_cur = NULL;
p_node = (node_t*)malloc(sizeof(node_t));
if(!p_node)
return 0;
p_node->val = val;
p_node->p_next = NULL;
p_node->p_prev = NULL;
p_first = p_link->tail.p_prev;
p_mid = p_first->p_next;
p_last = p_mid->p_next;
p_first->p_next = p_node;
p_node->p_next = p_mid;
p_node->p_prev = p_first;
p_mid->p_prev = p_node;
return 1;
}
在链表的最前面插入数字:本质上和在链表的尾部插入数据一样,找到插入的位置,将节点插入。
int link_add_head(link_t* p_link,int val){
node_t* p_node = NULL;
node_t* p_first = NULL,*p_mid = NULL,*p_last = NULL;
p_link->p_cur = NULL;
p_node = (node_t*)malloc(sizeof(node_t));
if(!p_node){
return 0;
}
p_node->val = val;
p_node->p_next = NULL;
p_node->p_prev = NULL;
p_first = &p_link->head;
p_mid = p_first->p_next;
p_last = p_mid->p_next;
p_first->p_next = p_node;
p_node->p_next = p_mid;
p_mid->p_prev = p_node;
p_node->p_prev = p_first;
return 1;
}
在链表的中间插入数字(按从小到大的顺序插入)
//按照从小到大的顺序插入在链表的中间
int link_insert(link_t* p_link,int val){
node_t* p_node = NULL;
node_t* p_tmp = NULL,*p_first = NULL,*p_mid = NULL,*p_last = NULL;
p_link->p_cur = NULL;
p_node = (node_t*)malloc(sizeof(node_t));
if(!p_node){
return 0;
}
p_node->val = val;
p_node->p_next = NULL;
p_node->p_prev = NULL;
for(p_tmp = &p_link->head;p_tmp!=&p_link->tail;p_tmp=p_tmp->p_next){
p_first = p_tmp;
p_mid = p_first->p_next;
p_last = p_mid->p_next;
if(p_mid == &p_link->tail || p_mid->val > p_node->val){
p_first->p_next = p_node;
p_node->p_next = p_mid;
p_mid->p_prev = p_node;
p_node->p_prev = p_first;
break;
}
}
return 1;
}
⑥ 链表的删除操作:
从尾部删除元素:
int link_remove_tail(link_t* p_link){
if(linkIsEmpty(p_link))
return 0;
node_t* p_first = NULL,*p_mid = NULL,*p_last = NULL;
p_link->p_cur = NULL;
p_last = &p_link->tail;
p_mid = p_last->p_prev;
p_first = p_mid->p_prev;
p_first->p_next = p_last;
p_last->p_prev = p_first;
free(p_mid);
p_mid = NULL;
return 1;
从头部删除元素:
int link_remove_head(link_t* p_link){
if(linkIsEmpty(p_link)){
return 0;
}
node_t* p_first = NULL,*p_mid = NULL,*p_last = NULL;
p_link->p_cur = NULL;
p_first = &p_link->head;
p_mid = p_first->p_next;
p_last = p_mid->p_next;
p_first->p_next = p_last;
p_last->p_prev = p_first;
free(p_mid);
p_mid = NULL;
return 1;
}
链表中删除某一个指定的数据
//删除某一个数字
int link_remove(link_t* p_link,int val){
node_t* p_tmp = NULL,*p_first = NULL,*p_mid = NULL,*p_last = NULL;
p_link->p_cur = NULL;
for(p_tmp = &p_link->head;p_tmp != &p_link->tail;p_tmp = p_tmp->p_next){
p_first = p_tmp;
p_mid = p_first->p_next;
p_last = p_mid->p_next;
if(p_mid !=&p_link->tail && p_mid->val == val){
p_first->p_next = p_last;
p_last->p_prev = p_first;
free(p_mid);
p_mid = NULL;
return 1;
}
if(p_mid == &p_link->tail){
printf("%d不存在\n!",val);
return 0;
}
⑦链表的查询操作
查询第一个元素的值:
void link_get_head(const link_t* p_link,int* val){
if(linkIsEmpty(p_link))
return;
*val = p_link->head.p_next->val;
}
查询最后一个元素的值
int link_get_tail(const link_t* p_link,int* val){
if(linkIsEmpty(p_link))
return 0;
*val = p_link->tail.p_prev->val;
return 1;
}
根据编号获取元素的值
int link_get(const link_t* p_link,int sn,int* val){
int cnt = 0;
const node_t* p_tmp = NULL,*p_first = NULL,*p_mid = NULL,*p_last = NULL;
for(p_tmp = &p_link->head;p_tmp!=&p_link->tail;p_tmp = p_tmp->p_next){
p_first = p_tmp;
p_mid = p_first->p_next;
p_last = p_mid->p_next;
//cnt就是p_mid指针的指向节点的编号
if(p_mid != &p_link->tail && (cnt==sn)){
*val = p_mid->val;
return 1;
}
cnt++;
}
return 0;
}
⑧链表的遍历
正向遍历:从头节点开始一次遍历到尾节点
//让链表进入从前向后遍历状态的函数
void link_begin(link_t* p_link){
p_link->p_cur = &p_link->head;//让链表指向头结点
}
//在从前向后的遍历过程中,获得下一个数字的函数
int link_next(link_t* p_link,int* val){
//链表没有处于遍历过程中
if(!p_link->p_cur)
return 0;
//找到这次要操作的节点并记录下来
p_link->p_cur = p_link->p_cur->p_next;
//这次要操作的节点时尾节点
if(p_link->p_cur == &p_link->tail){
p_link->p_cur = NULL;
return 0;
}
else{
*val = p_link->p_cur->val;
//表示成功获得数字
return 1;
}
}
void display(link_t* p_link){
link_begin(p_link);
int val;
while(1){
if(link_next(p_link,&val)){
printf("%d ",val);
}
else
break;
}
printf("\n");
}
反向遍历
//让链表进入从后向前遍历过程的函数
void link_rbegin(link_t* p_link){
p_link->p_cur = &p_link->tail;
}
//从后前面遍历的过程中获得前一个数字的函数
int link_prev(link_t* p_link,int* val){
if(!p_link->p_cur){
return 0;
}
p_link->p_cur = p_link->p_cur->p_prev;
if(p_link->p_cur == &p_link->head){
p_link->p_cur = NULL;
return 0;
}
else{
*val = p_link->p_cur->val;
return 1;
}
void rdisplay(link_t* p_link){
link_rbegin(p_link);
int val;
while(1){
if(link_prev(p_link,&val)){
printf("%d ",val);
}
else
break;
}
printf("\n");
}