数据结构学习——双向链表

本文介绍数据结构学习中的双向链表,包括双向链表的基本概念、双向循环链表的优势和创建方法,以及柔性数组在双向链表中的应用。双向循环链表允许O(1)时间复杂度访问头尾节点,但会增加内存开销。同时,文章提到了柔性数组在结构体中的使用,以及内存分配和释放的注意事项。

数据结构学习记录DAY4:双向链表

双向链表

双向链表简介:

如果认识单向链表,那么双向链表的定义其实非常之简单,只需要在单向链表的基础上,增加一个指向前结点的指针,即——对于任意一个结点,都有一个指向前一个结点和后一个结点的指针。以下为一个基础双向链表的定义。

struct Dnode{
	int elem;
	struct Dnode *prev;
	struct Dnode *next;
}

对于单纯的双向链表来说,头结点不存储数据,头结点的前驱指针(prev)指向NULL,后置指针(next)指向下一个结点,如果没有则指向空。

那么对于不单纯的双向链表来说又如何呢?

所谓不单纯的双向链表,其实就是指的双向循环链表。对其我是这样理解的:对于任意一个拥有头结点以外结点的双向链表,令其最后一个元素的尾指针指向头结点,而头结点的前驱指针指向尾结点,如此就形成了一个循环。
这样做有何益处呢?首先,对于单向链表来说,访问尾结点的时间复杂度为O(n),但由于双向循环链表有指向尾结点的指针,因此访问的时间复杂度变为O(1),可以缩减对于元素的访问时间,尤其是头尾两个结点。其次,双向链表可以方便地通过任意一个结点的位置访问到其他的结点,不必受方向的限制。
弊端自然是增加了指针域,内存利用率再次降低,当然空间换时间的买卖由使用者决定值不值。

双向循环链表

头结点 的prev指向最后一个结点,最后一个结点的next指向头结点。

  • 在头部和尾部进行插入和删除的时间复杂度都为O(1)
  • 能直接访问链表的最后一个结点

双向循环链表的创建

#define SUCCESS 0//此处注意后面也要用
#define FAILURE -1
typedef struct Dnode * DL;
#define SIZE_DNODE sizeof(*DL);

DL create_dlinklist(void)
{
    DL list = (DL)malloc(SIZE_DNODE);
    if(NULL != list)
    {
        list->prev = list->next = list;
    }
    return list;
}

双向循环链表的插入

static struct Dnode* create_ndoe(struct Dnode *prev, struct Dnode *next, int elem)
{
	struct Dnode *node = (struct Dnode*)malloc(SIZE_DNODE);
	if(NULL != node)
	{
		node->elem = elem;
		node->prev = prev;
		node->next = next;
	}
	return node;
}
//插入到pos后面的位置
static int insert_after(struct Dnode* node, ElemType elem)
{
    struct Dnode* insnode = create_node(node, node->next, elem);
    if(NULL == insnode)
    {
        return FAILURE;
    }
    node->next->prev = insnode;
    node->next = insnode;
    return SUCCESS;
}
//插入到pos前面的位置
static int insert_before(struct Dnode* node, ElemType elem)
{
    struct Dnode* insnode = create_node(node->prev, node->next, elem);
    if(NULL == insnode)
    {
        return FAILURE;
    }
    node->prev->next = insnode;
    node->prev = insnode;
    return SUCCESS;
}
//正式创建
//前插
int push_front_dlinkedlist(DL list, ElemType elem)
{
    return insert_after(list, elem);
}
//后插
int push_back_dlinkedlist(DL list, ElemType elem)
{
    return insert_before(list, elem);
}
//任意位置插入
int insert_dlinkedlist(DL list, size_t pos, ElemType elem)
{
     assert(NULL != list);
     if(0 == pos)
     {
        return FAILURE;
     }
    struct Dnode *node = get_node(list, pos);
    if(NULL == node)
    {
        return FAILURE;
    }
    return insert_before(node, elem);

}

双向链表的结点删除


int delete_dnode(struct Dnode* node, ElemType *pe)
{
    if(NULL != pe)
    {
        *pe = node->elem;
    }
    node->next->prev = node->prev;
    node->prev->next = node->next;
    free(node);
    return *pe;
}
//头删
int pop_front_dlinkedlist(DL list, ElemType *elem)
{
    assert(NULL != list);
    if(list->next == list)
    {
        return FAILURE;
    }
    return delete_dnode(list->next, elem);
}
//尾删
int pop_back_dlinkedlist(DL list, ElemType *elem)
{
    assert(NULL != list);
    if(list->next == list)
    {
        return FAILURE;
    }
    return delete_dnode(list->prev, elem);
}
//任意位置删
int remove_dlinkedlist(DL list, size_t pos, ElemType *elem)
{
    assert(NULL != list);
    if(0 == pos)
    {
        return FAILURE;
    }
    struct Dnode * node = get_node(list, pos);
    if(NULL == node || list == node)
    {
        return FAILURE;
    }
    return delete_dnode(node, elem);
}

双向链表结点修改

//按位置修改
int update_dlinkedlist(DL list, size_t pos, ElemType newelem)
{
    assert(NULL != list);
    if(0 == pos)
    {
        return FAILURE;
    }
    struct Dnode *node = get_node(list, pos);
    if(NULL == node || list == node)
    {
        return FAILURE;
    }
    node->elem = newelem;
    return SUCCESS;

}
//按元素修改
int update_elem_dlinkedlist(DL list, ElemType oldelem, ElemType newelem, size_t n)
{
    assert(NULL != list);
    struct Dnode *node = list->next;
    size_t cnt = 0;
    for(;node != list; node = node->next)
    {
        if(node->elem == oldelem)
        {
            if(n == 0)
            {
                node->elem = newelem;
            }
            else
            {
                if(--n == 0)
                {
                    node->elem = newelem;
                    return 1;
                }
            }
        }
    }
    return cnt;
    
}
//按条件修改
int update_condition_dlinkedlist(DL list, bool (*condition)(ElemType), ElemType newelem, size_t n)
{
    assert(NULL != list && condition != NULL);
    struct Dnode *node = list->next;
    size_t cnt = 0;
    for(;node != list; node = node->next)
    {
        if(condition(node->elem))
        {
            if(n == 0)
            {
                node->elem = newelem;
            }
            else
            {
                if(--n == 0)
                {
                    node->elem = newelem;
                    return 1;
                }
            }
        }
    }
    return cnt;
}

双向链表查找

//值查找
int find_dlinkedlist(DL list, ElemType elem, size_t n)
{
    assert(NULL != list);
    if(n == 0)
    {
        return 0;
    }
    struct Dnode *node = list->next;
    size_t pos = 0;
    for(;node != list; node = node->next)
    {
        ++pos;
        if(node->elem == elem)
        {
            if(--n == 0)
            {
                return pos;
            }
        }
    }
    return 0;
}
//条件查找
int find_condition_dlinkedlist(DL list, bool (*condition)(ElemType), size_t n)
{
    assert(NULL != list && condition != NULL);
    if(n == 0)
    {
        return 0;
    }
    struct Dnode *node = list->next;
    size_t pos = 0;
    for(;node != list; node = node->next)
    {
        ++pos;
        if(condition(node->elem))
        {
            if(--n == 0)
            {
                return pos;
            }
        }
    }
    return 0;
}

今天还学了一个概念叫柔性数组
GNU/GCC 在标准的 C/C++ 基础上做了有实用性的扩展, 零长度数组(Arrays of Length Zero) 就是其中一个知名的扩展。零长度数组即柔性数组。多数情况下, 其应用在变长数组中, 其定义如下
柔性数组在结构体成员中,最后一个成员是一个数组,长度为0 或不明
struct Dnode{
struct Dnode *prev;
struct Dnode *next;
char elem[];
};

  • 申请内存空间:
    首先, 需为结构体分配一块内存空间;
    其次再为结构体中的成员变量分配内存空间.
    这样两次分配的内存是不连续的, 需要分别对其进行管理
    struct Dnode * node = malloc(sizeof(struct Dnode) + 32);
    同样的在释放时,需要这样分开释放
free(Dnode->elem);
free(Dnode);

这是由于其定义时开辟的内存空间不同导致的

以上!


希望能给你一点帮助。
感谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值