本篇博客部分图片来自《黑马程序员数据结构资料》,如有侵权,请联系我。
链式存储的双向循环链表
双向链表
双向链表也叫双向表,是链表的一种,它由多个结点组成,每个结点都由一个数据域和两个指针域组成,数据域用 来存储数据,其中一个指针域用来指向其后继结点,另一个指针域用来指向前驱结点。链表的头结点的数据域不存 储数据,指向前驱结点的指针域和指向后继结点的指针域指向第一个真正存储数据的结点。
带头结点的双向循环链表
每个结构体都知道自己的前驱和后继
1、结构体以及链表的构建
struct llist_node_st
{
void *data;
struct llist_node_st *prev,*next;
};
typedef struct
{
int size;
struct llist_node_st head;
}LLIST;
//main.c中学生结构体
struct score_st
{
int id;
char name[NAMESIZE];
int math;
};
LLIST* llist_create(int size)
{
LLIST *new;
new = malloc(sizeof(*new));
if(new == NULL)
return NULL;
new->size = size;
new->head.data = NULL;
new->head.prev = new->head.next = &new->head;
return new;
}
1.2 变长结构体思想的创建(malloc)
struct llist_node_st
{
struct llist_node_st *prev,*next;
char data[1]; //地址对齐
};
struct llist_head_st
{
int size; //主函数里学生结构体的大小
struct llist_node_st head;
}LLIST;
LLIST* llist_create(int size)
{
LLIST *new;
new = malloc(sizeof(*new)); //struct llist_head_st
if(new == NULL)
return NULL;
new->size = size; //主函数里学生结构体的大小
new->head.prev = new->head.next = &new->head; //new的前驱和后继都指向头结点
return new; //返回一个LLIST * 类型的指针
}
1.3 双向链表的插入
用变长结构体思想:申请空间时多申请出一块学生结构体的空间
int llist_insert(LLIST *ptr, const void *data,int mode)
{ //ptr:头结点 data:要插入的数据 mode:插入模式(1->首部插入 2->尾部插入)
struct llist_node_st *newnode;
newnode = malloc(sizeof(*newnode) + ptr->size);
if(newnode == NULL)
return -1;
//将连续的size字节的data数据复制到newnode->data指向的地址为起点的内存中去.
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;
}
1.4 双向链表的查找
//main.c 功能:按id查找,如果找到了返回0;
int id_cmp(const void *key, const void *data)
{ //key:我要查找的id data:学生结构体指针 data->id:表示该学生的id
const int *k = key; //类型转换 k 为int*型
const struct score_st *d = data; //类型转换data为struct score_st *型
return *k - d->id; //return 0 表示有这个id
}
find_函数功能:按id查找数据,如果找到返回 llist_node_st * 结构体指针
struct llist_node_st *find_(LLIST *ptr,const void *key,llist_cmp *cmp)
{
struct llist_node_st *cur;
//遍历:当cur == &ptr->head时结束(head是变量名,需要取地址)
for(cur = ptr->head.next; cur != &ptr->head ; cur = cur->next)
{// cur = cur->next
if(cmp(key, cur->data) == 0)
break;
}
return cur;
}
//函数功能:按id查找数据,如果找到返回node->data指针
void *llist_find(LLIST *ptr,const void *key,llist_cmp *cmp)
{
struct llist_node_st *node;
node = find_(ptr,key,cmp);
if(node == &ptr->head) //如果返回的是头结点就证明没找到
return NULL;
else
return node->data;
}
1.5 双向链表的删除
操作时只需要获取待插入结点就可以进行操作。
//函数功能:按id进行查找,找到直接删除。
int llist_delete(LLIST *ptr, const void *key, llist_cmp *cmp)
{ //ptr:头结点 key:要删除的数据 cmp:比较函数
struct llist_node_st *node;
node = find_(ptr,key,cmp); //先找这个结点,如果没有返回-1.
if(node == &ptr->head)
return -1;
//node是要删除的结点
node->prev->next = node->next; //待删除结点前驱的后继指针指向待删除结点的后继
node->next->prev = node->prev; //待删除结点后继的前驱指针指向待删除结点的前驱
free(node); //释放已删除节点
return 0;
}
//函数功能:寻找这个结点,并且把这个结点的数据写到data域中,然后删除。
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; //待删除结点后继的前驱指针指向待删除结点的前驱
//将连续的size字节的node->data数据复制到新结点data指向的地址为起点的内存中去.
memcpy(data, node->data, ptr->size);
free(node);//释放已删除节点
return 0;
}
1.6 遍历打印llist_travel(LLIST *,llist_op *)
void print_s(void *data)
{
struct score_st *d = data; // struct score_st:学生结构体
printf("%d %s %d\n",d->id, d->name, d->math);
}
//typedef void llist_op(void *); print_s(void *data)
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); //print_s(cur->data)
}
1.7 销毁_llist_destroy(LLIST *)
void llist_destroy(LLIST *ptr)
{
struct llist_node_st *cur,*next;
//从第一个有效结点开始遍历,也就是头结点所指向的下一个结点
for(cur = ptr->head.next; cur != &ptr->head ; cur = next)
{
next = cur->next;
free(cur);
}
free(ptr); //最后释放头结点
}
1.8 主函数_main.c()
int main()
{
LLIST *handler;
struct score_st tmp;
int i;
handler = llist_create(sizeof(struct score_st));
if(handler == NULL)
{
printf("llist_create() failed.\n");
exit(1);
}
for(i = 0 ; i < 7 ; i++)
{
tmp.id = i;
tmp.math = 100-i;
snprintf(tmp.name, NAMESIZE,"STU%d",i);
llist_insert(handler, &tmp, LLIST_BACKWARD);
}
llist_travel(handler,print_s);
printf("\n\n");
int findid = 12;
char *findname = "STU40";
struct score_st *retp;
/*
//retp = llist_find(handler,&findid,id_cmp);
retp = llist_find(handler,findname,name_cmp);
if(retp)
print_s(retp);
else
printf("Can not find.\n");
*/
// llist_delete(handler,&findid,id_cmp);
if(llist_fetch(handler,&findid,id_cmp,&tmp) == 0)
print_s(&tmp);
printf("\n\n");
llist_travel(handler,print_s);
llist_destroy(handler);
exit(0);
}