数据结构--双链表

数据结构基础(4)


单链表:无法逆向检索,有时候不太方便

双链表:可进可退,存储密度更低一点点

双链表的初始化:

#include <stdbool.h>
#include <stdlib.h>

// 定义双链表节点结构体
typedef struct DNode {
    // 数据域,实际使用时需根据需求替换 ElemType(如 int、char 等)
    ElemType data; 
    struct DNode *prior;  // 指向前驱节点的指针
    struct DNode *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;  // 头结点的 next 为 NULL 则链表为空
    else return false;
}

// 测试函数
void testDLinkList() {
    // 初始化双链表
    DLinklist L;  
    InitDLinkList(L); 
    // 后续可补充对链表操作、测试的代码...
}

双链表的插入:

typedef struct DNode {
    // 数据域,实际使用时需根据需求替换 ElemType(如 int、char 等)
    ElemType data; 
    // 指向前驱节点的指针
    struct DNode *prior; 
    // 指向后继节点的指针
    struct DNode *next; 
} DNode; 

// 在 p 结点之后插入 s 结点
bool InsertNextDNode(DNode *p, DNode *s) {
    // 非法参数检查
    if (p == NULL || s == NULL) return false; 
    s->next = p->next;
    // 如果 p 结点有后继结点
    if (p->next != NULL) { 
        p->next->prior = s;
    }
    s->prior = p;
    p->next = s;
    return true;
}

修改指针时要注意顺序

双链表的删除:

// 假设已定义双链表节点结构体 DNode
typedef struct DNode {
    // 数据域,实际使用时需根据需求替换 ElemType(如 int、char 等)
    ElemType data; 
    // 指向前驱节点的指针
    struct DNode *prior; 
    // 指向后继节点的指针
    struct DNode *next; 
} DNode; 

// 删除 p 结点的后继结点
bool DeleteNextDNode(DNode *p) {
    // p 为 NULL,无法操作
    if (p == NULL) return false; 
    // 找到 p 的后继结点 q
    DNode *q = p->next; 
    // p 没有后继结点,无法删除
    if (q == NULL) return false; 
    p->next = q->next;
    // 如果 q 不是最后一个结点,调整后继结点的前驱
    if (q->next != NULL) { 
        q->next->prior = p;
    }
    // 释放结点 q 的空间
    free(q); 
    return true;
}

销毁一个双链表:

// 销毁双链表
void DestoryList(DLinklist &L) {
    // 循环释放各个数据结点
    while (L->next != NULL) { 
        DeleteNextDNode(L);
    }
    free(L);  // 释放头结点
    L = NULL;   // 头指针指向 NULL
}

双链表的遍历:

  • 后向遍历(从某个节点往后遍历,包含可能的头节点等起始点 )
while (p != NULL) {
    // 对结点 p 做相应处理,如打印等操作
    p = p->next;
}
  • 前向遍历(从某个节点往前遍历,包含可能的尾节点等起始点 )
while (p != NULL) {
    // 对结点 p 做相应处理,如打印等操作
    p = p->prior;
}
  • 前向遍历(跳过头节点,从第一个数据节点往前遍历 )
while (p->prior != NULL) {
    // 对结点 p 做相应处理,如打印等操作
    p = p->prior;
}

双链表不可随机存取,按位查找、按值查找操作都只能用遍历的方式实现。

时间复杂度:O(n)

循环单链表:

单链表:

表尾结点的next指针指向NULL

从一个结点出发只能找到后续的各个节点

循环单链表:

表尾结点的next指针指向头结点

从一个结点出发可以找到其他任何一个结点

// 定义循环单链表结点结构
typedef struct LNode {
    ElemType data;       // 数据域,存储数据元素
    struct LNode *next;  // 指针域,指向下一个结点
} LNode, *LinkList; 

// 初始化循环单链表
bool InitList(LinkList &L) {
    // 分配头结点内存
    L = (LNode *)malloc(sizeof(LNode)); 
    if (L == NULL)     // 内存分配失败
        return false; 
    L->next = L;   // 头结点 next 指向自身,形成循环
    return true;
}

// 判断循环单链表是否为空
bool Empty(LinkList L) {
    // 头结点 next 指向自身则为空表
    return L->next == L; 
}

// 判断结点 p 是否为循环单链表的表尾结点
bool isTail(LinkList L, LNode *p) {
    // 表尾结点的 next 指向头结点
    return p->next == L; 
}

当为空表时:就像是单手抱住空虚的自己

循环双链表:

双链表:

表头结点的prior指向NULL;表尾结点的next指向NULL

循环双链表:

表头结点的prior指向表尾结点;表尾结点的next指向头结点

// 定义循环双链表结点结构
typedef struct DNode {
    ElemType data;         // 数据域,存储数据元素
    struct DNode *prior;   // 前驱指针,指向前一个结点
    struct DNode *next;    // 后继指针,指向后一个结点
} DNode, *DLinklist; 

// 初始化空的循环双链表
bool InitDLinkList(DLinklist &L) {
    L = (DNode *)malloc(sizeof(DNode)); // 分配一个头结点
    if (L == NULL)  // 内存分配失败
        return false; 
    L->prior = L;   // 头结点的 prior 指向自身
    L->next = L;    // 头结点的 next 指向自身
    return true;
}

// 判断循环双链表是否为空
bool Empty(DLinklist L) {
    // 头结点 next 指向自身则为空表
    return L->next == L; 
}

// 判断结点 p 是否为循环双链表的表尾结点
bool isTail(DLinklist L, DNode *p) {
    // 表尾结点的 next 指向头结点
    return p->next == L; 
}

// 测试函数:初始化并调用循环双链表操作
void testDLinkList() {
    // 初始化循环双链表
    DLinklist L; 
    InitDLinkList(L); 
    // 后续可扩展:插入、删除、遍历等操作...
}

当为空表时:就像是双手抱住空虚的自己

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YA10JUN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值