双链表详解:初始化与插入操作

需积分: 0 0 下载量 128 浏览量 更新于2024-08-05 收藏 4KB MD 举报
"5.双链表的简介.md" 在数据结构中,链表是一种重要的线性数据结构,其中的数据元素不是在物理位置上相邻,而是通过指针连接。双链表是链表的一种变体,它在每个节点中不仅包含指向下一个节点的指针,还包含一个指向前一个节点的指针,这使得双向操作变得可能。 ### 单链表与双链表的比较 #### 单链表 单链表每个节点只包含一个指针,通常称为`next`指针,用于指向序列中的下一个节点。由于只有一个方向的链接,单链表的插入和删除操作相对简单,但其主要缺点是无法直接从前一个节点访问当前节点,只能从头节点开始按顺序查找,这在需要反向遍历或随机访问时效率较低。 #### 双链表 双链表的每个节点包含两个指针,一个`next`指针指向下一个节点,另一个`prior`指针指向前一个节点。这种设计使得双链表在进行插入和删除操作时更加灵活,因为可以从前后两个方向进行。然而,双链表的存储空间需求比单链表高,因为每个节点需要额外存储一个指针,所以其存储密度较低。 ### 双链表的初始化 在C语言中,我们可以定义一个结构体来表示双链表的节点,如`DNode`,包括数据域`data`以及前驱`prior`和后继`next`两个指针。`DLinkList`是一个指向`DNode`类型的指针,通常用作链表的头指针。初始化双链表通常包括分配一个头结点并设置其`prior`和`next`指针为`NULL`。 ```c typedef struct DNode{ ElemType data; // 数据域 struct DNode* prior, *next; // 前驱和后继指针 } DNode, *DLinkList; // 初始化双链表 bool InitDLinkList(DLinkList& L){ L = (DNode*)malloc(sizeof(DNode)); // 分配一个头结点 if(L == NULL) // 内存不足,分配失败 return false; L->prior = NULL; // 头结点的prior永远指向NULL L->next = NULL; // 头结点之后暂时还没有结点 return true; } // 判断双链表是否为空(带头结点) bool Empty(DLinkList L){ if(L->next == NULL) return true; else return false; } ``` ### 双链表的插入操作 插入新节点到双链表中通常有两种情况: 1. 在指定节点`p`之后插入节点`s`。首先,更新`s`的`next`和`prior`指针,然后更新`p`的`next`指针和`p->next`的`prior`指针。 2. 需要注意的是,如果`p`是链表的最后一个节点,`p->next`为空,直接插入会导致空指针错误,因此在插入之前需要检查这种情况。 ```c // ① 在p结点之后插入s结点(考虑了p是尾结点的情况) bool InsertNextDNode(DNode* p, DNode* s){ if(p == NULL || s == NULL) // 非法参数 return false; if(p->next == NULL){ // 如果p是尾结点 p->next = s; s->prior = p; s->next = NULL; } else { s->next = p->next; s->prior = p; p->next->prior = s; p->next = s; } return true; } ``` ### 双链表的其他操作 除了插入,双链表还支持其他常见的操作,如删除节点、反向遍历、查找特定节点等。删除操作与插入类似,需要同时更新被删除节点及其前后节点的指针。反向遍历只需从尾节点开始向前移动即可。查找特定节点可以通过正向或反向遍历实现,具体取决于查找效率的需求。 双链表是一种灵活的数据结构,提供了对链表的双向访问能力,但需要更多的存储空间。在需要频繁进行反向操作或对性能要求较高的场景下,双链表是一个很好的选择。