数据结构—双向链表(详解)

双向链表

在之前一篇文章中介绍了单向链表的实现,可以看出单向链表实现过程中,只能够逐次向下继续查找,指针一直指向下一个节点,而本文中的双向链表,在结构体定义的时候多了一个指向上一节点的指针,因此,无论是在插入还是删除来说,从后往前和从前往后遍历都极大的提高了便利性。下面来介绍双向链表的具体实现。

双向非循环链表的实现

1、链表结构体封装
/* 双向链表的结构 */

typedef struct llist_st
{
    int data;
    struct llist_st * prev;
    struct llist_st * next;
}llist;
2、链表的创建

创建双向链表的时候,同单向链表一样,需要一个头指针来标志链表的信息。

llist* llist_create()
{

    llist* me = malloc(sizeof(*me));
    if (me == NULL)
        return NULL;       
    me->prev = NULL;
    me->next = NULL;
    me->data = 0;
    return me;
}
3、链表的插入(头部插入法)

在单向链表的插入中,找到待插入节点的前驱结点,再对那个节点进行插入操作。

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;
    }
}
3、链表的遍历
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;
 }
4、链表元素的删除
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;
}


4、链表销毁

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;  
}
1、循环链表的创建
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;
}

2、循环链表的插入

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;
}
3、循环链表的遍历
为了保证数据的通用性,使用回调函数,让用户传递需要遍历的数据。
//抽象出一个函数类型
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);    
};
4、链表删除某个特定元素
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;
}
5、指定位置元素的获取
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);
}
6、循环链表的销毁

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);
}

你可能感兴趣的:(#,数据结构,数据结构,链表)