数据结构day04

一、双向循环链表(核心重点)

1. 基本结构

双向循环链表的每个节点包含数据域两个指针域

c

运行

复制代码
struct doublelist {
    int data;               // 数据域:存放有效数据
    struct doublelist *next;// 后继指针:指向后一个节点
    struct doublelist *prev;// 前驱指针:指向前一个节点
};
  • 头节点:不存有效数据,仅作为链表入口;
  • 循环特性:头节点的prev指向尾节点,尾节点的next指向头节点;
  • 双向特性:任意节点可通过prev/next向前 / 向后遍历。

2. 核心操作(通俗解释)

(1)初始化头节点

c

运行

复制代码
struct doublelist *list_init() {
    struct doublelist *head = malloc(sizeof(struct doublelist));
    head->next = head; // 头节点next指向自己
    head->prev = head; // 头节点prev指向自己
    return head;
}
  • 作用:创建链表的 "入口",初始时链表只有头节点,自己指向自己。

(2)尾插数据(往链表最后加节点)

  1. 找到尾节点:从头节点开始,一直找next指向头节点的节点;
  2. 新建节点:给新节点赋值,初始化prev/next
  3. 调整指针:
    • 原尾节点的next指向新节点;
    • 新节点的prev指向原尾节点;
    • 新节点的next指向头节点;
    • 头节点的prev指向新节点。

(3)删除节点(按数据删)

  • 核心逻辑:找到要删的节点后,让它的 "前一个节点" 指向 "后一个节点","后一个节点" 指向 "前一个节点",最后释放节点内存;
  • 简化点:双向循环链表删除中间节点尾节点 的逻辑可统一(尾节点的next是头节点,无需额外判空)。

c

运行

复制代码
// 简化版删除逻辑(核心)
p->prev->next = p->next; // 前节点跳过当前节点
p->next->prev = p->prev; // 后节点跳过当前节点
free(p); // 释放内存

(4)修改数据

遍历链表,找到目标数据的节点,直接修改节点的data值即可:

c

运行

复制代码
void list_update(int newdata, int olddata, struct doublelist *head) {
    struct doublelist *p = head;
    while (p->next != head) {
        p = p->next;
        if (p->data == olddata) {
            p->data = newdata; // 直接替换数据
        }
    }
}

(5)销毁链表(清空所有节点)

  • 思路:每次删头节点的下一个节点,直到头节点的next指向自己;
  • 最后一定要释放头节点内存,避免内存泄漏。

c

运行

复制代码
void list_destory(struct doublelist *head) {
    struct doublelist *p = NULL;
    while (head->next != head) {
        p = head->next;          // 取头节点的下一个节点
        head->next = p->next;    // 头节点跳过当前节点
        p->next->prev = head;    // 后节点指向头节点
        free(p);                 // 释放当前节点
    }
    free(head); // 释放头节点
}

3. 关键注意点

  1. 指针调整顺序:先改 "前 / 后节点的指向",再释放节点,避免指针丢失;
  2. 循环终止条件:所有遍历都以p->next != head为结束(因为尾节点next指向头节点);
  3. 内存释放:删除 / 销毁节点时,必须用free()释放,否则会内存泄漏。

二、字符串处理(辅助知识点)

1. strtok(切割字符串)

  • 作用:按指定分隔符拆分字符串;
  • 用法:
    • 第一次调用:传要切割的字符串 + 分隔符;
    • 后续调用:第一个参数传NULL,继续切割剩余部分。

c

运行

复制代码
char date[20] = "2026-1-6";
char *p = strtok(date, "-"); // 第一次切:p = "2026"
p = strtok(NULL, "-");       // 第二次切:p = "1"
p = strtok(NULL, "-");       // 第三次切:p = "6"

2. sprintf(拼接字符串)

  • 作用:把不同类型数据(int、double、字符串)按格式拼接到一个字符串里;
  • 用法:sprintf(目标数组, 格式字符串, 待拼接数据...)

c

运行

复制代码
int n1=666;
char n2[20]="新年快乐";
double n3=99.9;
char buf[200]={0};
sprintf(buf,"祝大家2026%s,考试成绩%lf,一路%d",n2,n3,n1);
// buf最终内容:"祝大家2026新年快乐,考试成绩99.900000,一路666"

三、核心总结

  1. 双向循环链表:记住 "双向(prev/next)+ 循环(头尾相连)",操作核心是调整指针指向,删除 / 销毁时必释放内存;
  2. 字符串操作:strtok 按分隔符切,sprintf 按格式拼,记住 "第一次传字符串,后续传 NULL"(strtok)、"格式符匹配数据类型"(sprintf);
  3. 调试技巧:画链表指针图!比如删除节点时,先画原指针关系,再画修改后的关系,代码就不容易错。
相关推荐
历程里程碑5 分钟前
Linux20 : IO
linux·c语言·开发语言·数据结构·c++·算法
DeeplyMind18 分钟前
第七章:数据结构大比拼
数据结构·计算机科学·少儿编程·少儿科技读物
元亓亓亓19 分钟前
考研408--数据结构--day8--遍历序列&线索二叉树
数据结构·考研·408·线索二叉树
xiaoxue..25 分钟前
合并两个升序链表 与 合并k个升序链表
java·javascript·数据结构·链表·面试
驭渊的小故事1 小时前
简单模板笔记
数据结构·笔记·算法
VT.馒头2 小时前
【力扣】2727. 判断对象是否为空
javascript·数据结构·算法·leetcode·职场和发展
历程里程碑3 小时前
Linux 库
java·linux·运维·服务器·数据结构·c++·算法
Sheep Shaun3 小时前
如何让一个进程诞生、工作、终止并等待回收?——探索Linux进程控制与Shell的诞生
linux·服务器·数据结构·c++·算法·shell·进程控制
Pluchon3 小时前
硅基计划4.0 简单模拟实现AVL树&红黑树
java·数据结构·算法
小龙报3 小时前
【51单片机】从 0 到 1 玩转 51 蜂鸣器:分清有源无源,轻松驱动它奏响新年旋律
c语言·数据结构·c++·stm32·单片机·嵌入式硬件·51单片机