数据结构学习记录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);
这是由于其定义时开辟的内存空间不同导致的
以上!
希望能给你一点帮助。
感谢
本文介绍数据结构学习中的双向链表,包括双向链表的基本概念、双向循环链表的优势和创建方法,以及柔性数组在双向链表中的应用。双向循环链表允许O(1)时间复杂度访问头尾节点,但会增加内存开销。同时,文章提到了柔性数组在结构体中的使用,以及内存分配和释放的注意事项。
800

被折叠的 条评论
为什么被折叠?



