在之前一篇文章中介绍了单向链表的实现,可以看出单向链表实现过程中,只能够逐次向下继续查找,指针一直指向下一个节点,而本文中的双向链表,在结构体定义的时候多了一个指向上一节点的指针,因此,无论是在插入还是删除来说,从后往前和从前往后遍历都极大的提高了便利性。下面来介绍双向链表的具体实现。
/* 双向链表的结构 */
typedef struct llist_st
{
int data;
struct llist_st * prev;
struct llist_st * next;
}llist;
创建双向链表的时候,同单向链表一样,需要一个头指针来标志链表的信息。
llist* llist_create()
{
llist* me = malloc(sizeof(*me));
if (me == NULL)
return NULL;
me->prev = NULL;
me->next = NULL;
me->data = 0;
return me;
}
在单向链表的插入中,找到待插入节点的前驱结点,再对那个节点进行插入操作。
void llist_insert(llist *l,int *data)
{if (l == NULL)
return;
llist*p=l,*q;
/*链表中仅仅只有头节点时的插入方式*/
if (p->next == NULL)
{
q = malloc(sizeof(*q));
if (q == NULL)
return ;
q->data = *data;
q->next = p->next;
p->next = q;
q->prev = p;
}
else
/*链表中有其他头节点时的插入方式*/
{
q = malloc(sizeof(*q));
if (q == NULL)
return ;
q->data = *data;
p->next->prev = q;
q->prev = p;
q->next = p->next;
p->next = q;
}
}
void llist_display(llist *l,int style)
{
/*顺序遍历*/
llist*p = l;
while (p->next !=NULL)
{
p = p->next;
printf("%d ",p->data);
}
/*倒序遍历*/
while (p->prev->prev!=NULL)
{
printf("%d ",p->data);
p = p->prev;
}
printf("%d\n",p->data);
return;
}
void llist_delete(llist * l,int *data)
{
llist*p = l;
while (p->next&&p->data!=*data)
{
p = p->next;
}
p->prev->next = p->next;
p->next->prev = p->prev;
free(p);
return;
}
void llist_destory(llist * l)
{
llist*p,*q;
for (p=l->next;p;q=p)
{
free(q);
p=p->next;
}
free(l);
return;
}
双向循环链表在双向链表的基础上是实现了数据的首尾相连。
结构体定义
struct llist_node_st
{
struct llist_node_st *prev;
struct llist_node_st *next;
/*变长结构体的实现,做一个占位符,使用结构体类型的变量来引用*/
char data[1];
};
头结点结构体的定义
typedef struct llist_head
{
定义大小
int size;
struct llist_node_st head;
}
LLIST* llist_create(int initsize)
{
LLIST *new;//双向链表头结点
new = malloc(sizeof(*new));
if (new == NULL)
return NULL;
new->size = initsize;
new->head.prev = &new->head;
new->head.next = &new->head;
return new;
}
int llist_insert(LLIST *ptr,const void *data,int mode)
{
struct llist_node_st *newnode;
newnode = malloc(sizeof(*newnode)+ptr->size);
if (newnode == NULL)
return -1;
memcpy(newnode->data,data,ptr->size);
/*首部插入*/
if (mode == LLIST_FORWARD)
{
newnode->prev = &ptr->head;
newnode->next = ptr->head.next;
}
/*尾部插入*/
else if(mode == LLIST_BACKWARD)
{
newnode->prev = ptr->head.prev;
newnode->next = &ptr->head;
}
else
return -3;
newnode->prev->next = newnode;
newnode->next->prev = newnode;
return 0;
}
为了保证数据的通用性,使用回调函数,让用户传递需要遍历的数据。
//抽象出一个函数类型
typedef void llist_op(const void *);
void llist_travel(LLIST *ptr,llist_op *op)
{
struct llist_node_st *cur;
for (cur = ptr->head.next;cur!=&ptr->head;cur=cur->next)
{
op(cur->data);
}
}
//打印函数的示范
void printf_s(const void *record)
{
//类型强制转换
const struct score_st *r = record;
printf("""%d %s %d %d \n",r->id,r->name,r->math,r->chinese);
};
find_函数需要用户自主实现函数指针:
static struct llist_node_st *find_(LLIST *ptr,const void *key,llist_cmp*cmp)
{
struct llist_node_st *cur;
for (cur = ptr->head.next;cur!= &ptr->head;cur=cur->next)
{
if(cmp(key,cur->data)==0)
break;
}
return cur;
}
//实现链表删除功能
int llist_delete(LLIST *ptr,const void *key,llist_cmp *cmp)
{
struct llist_node_st *node;
node = find_(ptr,key,cmp);
if (node == &ptr->head)
return -1;
/*对返回要删除的链表进行操作*/
node->prev->next = node->next;
node->next->prev = node->prev;
free(node);
return 0;
}
int llist_fetch(LLIST *ptr,const void *key,llist_cmp *cmp,void *data)
{
struct llist_node_st *node;
node = find_(ptr,key,cmp);
if (node == &ptr->head)
return -1;
node->prev->next = node->next;
node->next->prev = node->prev;
if (data != NULL)
memcpy(data,node->data,ptr->size);
free(node);
}
void llist_destory(LLIST *ptr)
{
struct llist_node_st *cur,*next;
//从头节点开始遍历,头节点不能删除
for (cur = ptr->head.next;cur != &ptr->head;cur=cur->next)
{
//首先next保存当前节点的下一个节点
next = cur->next;
//释放当前节点
free(cur);
}
free(ptr);
}