【花雕学编程】Arduino RTOS 之带回调的非递归 DFS 遍历

在这里插入图片描述
《Arduino 手册(思路与案例)》栏目介绍:
在电子制作与智能控制的应用领域,本栏目涵盖了丰富的内容,包括但不限于以下主题:Arduino BLDC、Arduino CNC、Arduino E-Ink、Arduino ESP32 SPP、Arduino FreeRTOS、Arduino FOC、Arduino GRBL、Arduino HTTP、Arduino HUB75、Arduino IoT Cloud、Arduino JSON、Arduino LCD、Arduino OLED、Arduino LVGL、Arduino PID、Arduino TFT,以及Arduino智能家居、智慧交通、月球基地、智慧校园和智慧农业等多个方面与领域。不仅探讨了这些技术的基础知识和应用领域,还提供了众多具体的参考案例,帮助读者更好地理解和运用Arduino平台进行创新项目。目前,本栏目已有近4000篇相关博客,旨在为广大电子爱好者和开发者提供全面的学习资源与实践指导。通过这些丰富的案例和思路,读者可以获取灵感,推动自己的创作与开发进程。
https://blog.csdn.net/weixin_41659040/category_12422453.html

在这里插入图片描述
Arduino RTOS 中的带回调的非递归 DFS 遍历

1、主要特点
非递归实现:
带回调的非递归深度优先遍历(DFS)通过栈数据结构模拟递归过程,避免了递归调用带来的栈溢出风险,适合在资源受限的嵌入式系统中使用。

回调机制:
在遍历过程中,用户可以传递一个回调函数,用于处理当前节点的数据。这种机制使得遍历过程中的节点处理更加灵活,用户可以根据需求实现不同的操作。

灵活性与可扩展性:
由于采用回调机制,用户可以轻松扩展遍历的功能,例如统计节点数量、查找特定值或执行复杂的节点处理逻辑。

状态管理:
通过显式的栈结构,用户可以更好地管理遍历状态,确保在遍历过程中可以实现动态调整和状态回退。

2、应用场景
树形数据结构遍历:
在需要遍历树形数据结构(如文件系统、XML数据解析等)的应用中,带回调的非递归 DFS 能够高效处理各种节点数据。

图形算法:
在图形处理、网络拓扑分析等领域,带回调的非递归 DFS 可用于遍历图的节点,执行特定的图算法(如寻找路径、连通分量等)。

游戏开发:
在游戏开发中,非递归 DFS 可用于遍历游戏对象的层次结构,实现碰撞检测、AI 行为决策等功能。

数据分析:
在数据分析中,带回调的 DFS 可用于遍历复杂的数据结构,进行数据挖掘和统计分析。

3、注意事项
栈溢出风险:
虽然非递归 DFS 避免了递归引起的栈溢出,但在使用显式栈时,仍需注意栈的大小,确保不会耗尽内存。

回调函数性能:
回调函数的执行效率直接影响遍历性能,需确保回调逻辑简洁高效,以避免影响整体遍历速度。

状态管理:
需要合理管理栈状态,确保在遍历过程中能够正确处理节点的访问状态,避免重复访问或遗漏节点。

错误处理:
遍历过程中可能会遇到异常情况(如空节点、循环引用等),需设计健壮的错误处理机制,以确保系统稳定运行。

性能监控:
在大规模数据处理时,需对遍历性能进行监控,确保在效率和资源使用之间取得平衡。

在这里插入图片描述
1、基础非递归 DFS 遍历

#include <Arduino.h>
#include <FreeRTOS.h>
#include <Stack.h>

typedef struct Node {
    int value;
    struct Node* left;
    struct Node* right;
} Node;

Node* createNode(int value) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->value = value;
    newNode->left = newNode->right = NULL;
    return newNode;
}

void dfsTraversal(Node* root, void (*callback)(int));

Node* root;

void setup() {
    Serial.begin(9600);
    
    // 创建简单的二叉树
    root = createNode(1);
    root->left = createNode(2);
    root->right = createNode(3);
    root->left->left = createNode(4);
    root->left->right = createNode(5);
    
    dfsTraversal(root, [](int value) {
        Serial.print("Visited Node: ");
        Serial.println(value);
    });
}

void loop() {
    // 主循环空着
}

void dfsTraversal(Node* root, void (*callback)(int)) {
    Stack<Node*> stack;
    if (root != NULL) {
        stack.push(root);
    }

    while (!stack.isEmpty()) {
        Node* current = stack.pop();
        callback(current->value);
        
        // 先右后左,以保证左子树优先
        if (current->right != NULL) {
            stack.push(current->right);
        }
        if (current->left != NULL) {
            stack.push(current->left);
        }
    }
}

2、 带状态的非递归 DFS 遍历

#include <Arduino.h>
#include <FreeRTOS.h>
#include <Stack.h>

typedef struct Node {
    int value;
    struct Node* left;
    struct Node* right;
} Node;

Node* createNode(int value) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->value = value;
    newNode->left = newNode->right = NULL;
    return newNode;
}

void dfsTraversal(Node* root, void (*callback)(int));

Node* root;

void setup() {
    Serial.begin(9600);
    
    root = createNode(1);
    root->left = createNode(2);
    root->right = createNode(3);
    root->left->left = createNode(4);
    root->left->right = createNode(5);
    
    dfsTraversal(root, [](int value) {
        Serial.print("Visited Node: ");
        Serial.println(value);
    });
}

void loop() {
    // 主循环空着
}

void dfsTraversal(Node* root, void (*callback)(int)) {
    Stack<Node*> stack;
    if (root != NULL) {
        stack.push(root);
    }

    while (!stack.isEmpty()) {
        Node* current = stack.pop();
        callback(current->value);
        
        // 先右后左,以保证左子树优先
        if (current->right != NULL) {
            stack.push(current->right);
        }
        if (current->left != NULL) {
            stack.push(current->left);
        }
    }
}

3、多任务环境下的非递归 DFS 遍历

#include <Arduino.h>
#include <FreeRTOS.h>
#include <Stack.h>

typedef struct Node {
    int value;
    struct Node* left;
    struct Node* right;
} Node;

Node* createNode(int value) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->value = value;
    newNode->left = newNode->right = NULL;
    return newNode;
}

void dfsTraversal(Node* root, void (*callback)(int));

Node* root;

void setup() {
    Serial.begin(9600);
    
    root = createNode(1);
    root->left = createNode(2);
    root->right = createNode(3);
    root->left->left = createNode(4);
    root->left->right = createNode(5);
    
    xTaskCreate([](void* params) {
        dfsTraversal(root, [](int value) {
            Serial.print("Visited Node: ");
            Serial.println(value);
        });
        vTaskDelete(NULL);
    }, "DFS Task", 1000, NULL, 1, NULL);
}

void loop() {
    // 主循环空着
}

void dfsTraversal(Node* root, void (*callback)(int)) {
    Stack<Node*> stack;
    if (root != NULL) {
        stack.push(root);
    }

    while (!stack.isEmpty()) {
        Node* current = stack.pop();
        callback(current->value);
        
        // 先右后左,以保证左子树优先
        if (current->right != NULL) {
            stack.push(current->right);
        }
        if (current->left != NULL) {
            stack.push(current->left);
        }
    }
}

要点解读
非递归实现:

通过使用栈来管理节点,实现非递归的深度优先搜索(DFS)遍历。与递归实现相比,非递归方法避免了栈溢出问题,适合处理较深的树结构。
回调函数:

使用回调函数来处理访问的节点值,使得遍历过程更加灵活。用户可以根据需要自定义处理逻辑,而无需修改 DFS 遍历的核心实现。
任务调度:

在第三个示例中,使用 FreeRTOS 创建任务来执行 DFS 遍历。这种设计使得树遍历可以在多任务环境中运行,增强了系统的并发性。
动态内存管理:

使用动态内存分配创建树节点,需注意内存管理,避免内存泄漏。在实际应用中,及时释放不再使用的内存是非常重要的。
灵活性与扩展性:

该实现可以轻松扩展到不同类型的树结构或其他数据结构,只需调整节点的定义和回调函数的实现。这使得该 DFS 实现具有良好的适应性,适合多种场景。

在这里插入图片描述
4、文件系统目录遍历(模拟)

#include <Arduino.h>
#include <FreeRTOS.h>
#include <task.h>
#include <stack>
 
// 模拟文件系统节点
struct FSNode {
    String name;
    bool isDir;
    std::vector<FSNode*> children;
};
 
// 回调函数类型定义
typedef void (*DFSCallback)(FSNode* node, bool isEnter);
 
// 非递归DFS实现
void dfs_traverse(FSNode* root, DFSCallback callback) {
    std::stack<FSNode*> nodeStack;
    std::stack<bool> visitedStack;
    
    nodeStack.push(root);
    visitedStack.push(false);
 
    while (!nodeStack.empty()) {
        FSNode* current = nodeStack.top();
        bool visited = visitedStack.top();
 
        if (!visited) {
            // 进入节点回调
            callback(current, true);
            visitedStack.pop();
            visitedStack.push(true);
 
            // 如果是目录,逆序压栈保证顺序处理
            if (current->isDir) {
                for (int i = current->children.size() - 1; i >= 0; i--) {
                    nodeStack.push(current->children[i]);
                    visitedStack.push(false);
                }
            }
        } else {
            // 离开节点回调
            callback(current, false);
            nodeStack.pop();
            visitedStack.pop();
        }
    }
}
 
// 示例回调函数:打印目录结构
void print_fs_callback(FSNode* node, bool isEnter) {
    if (isEnter) {
        Serial.print("Enter: ");
        for (int i = 0; i < nodeStack.size() - 1; i++) Serial.print("  "); // 缩进
        Serial.println(node->name + (node->isDir ? "/" : ""));
    } else {
        Serial.print("Leave: ");
        for (int i = 0; i < nodeStack.size(); i++) Serial.print("  ");
        Serial.println(node->name);
    }
}
 
// RTOS任务
void task_dfs_demo(void *pvParameters) {
    // 构建模拟文件系统
    FSNode root{"root", true};
    FSNode dir1{"dir1", true};
    FSNode file1{"file1.txt", false};
    FSNode dir2{"dir2", true};
    FSNode file2{"file2.log", false};
    
    dir1.children.push_back(&file1);
    dir2.children.push_back(&file2);
    root.children.push_back(&dir1);
    root.children.push_back(&dir2);
 
    // 执行DFS遍历
    dfs_traverse(&root, print_fs_callback);
    
    vTaskDelete(NULL);
}
 
void setup() {
    Serial.begin(115200);
    xTaskCreate(task_dfs_demo, "DFSDemo", 4096, NULL, 1, NULL);
}
 
void loop() {}

场景说明:
模拟文件系统目录遍历,使用回调函数在进入/离开节点时执行自定义操作(如打印日志)。

5、设备树状态检查

#include <Arduino.h>
#include <FreeRTOS.h>
#include <task.h>
#include <stack>
 
// 设备节点结构
struct DeviceNode {
    String id;
    uint8_t status; // 0=offline, 1=online, 2=error
    std::vector<DeviceNode*> children;
};
 
// 回调函数类型
typedef void (*DeviceCallback)(DeviceNode* device, bool isPreOrder);
 
// 带状态检查的DFS
void dfs_check_devices(DeviceNode* root, DeviceCallback callback) {
    std::stack<std::pair<DeviceNode*, bool>> stack;
    stack.push({root, false});
 
    while (!stack.empty()) {
        auto& [node, visited] = stack.top();
 
        if (!visited) {
            // 前序回调:检查设备状态
            callback(node, true);
            stack.top().second = true;
 
            // 逆序压入子设备
            for (auto it = node->children.rbegin(); it != node->children.rend(); ++it) {
                stack.push({*it, false});
            }
        } else {
            // 后序回调:汇总子设备状态
            callback(node, false);
            stack.pop();
        }
    }
}
 
// 示例回调:状态监控
void device_monitor_callback(DeviceNode* device, bool isPreOrder) {
    if (isPreOrder) {
        if (device->status == 2) {
            Serial.print("[ERROR] Device ");
            Serial.print(device->id);
            Serial.println(" needs attention!");
        }
    } else {
        // 后序处理可计算子树状态
        int onlineCount = 0;
        for (auto child : device->children) {
            if (child->status == 1) onlineCount++;
        }
        Serial.print("Device ");
        Serial.print(device->id);
        Serial.print(": ");
        Serial.print(onlineCount);
        Serial.println(" children online");
    }
}
 
// RTOS任务
void task_device_check(void *pvParameters) {
    // 创建设备树
    DeviceNode gateway{"gateway", 1};
    DeviceNode sensor1{"temp_sensor", 1};
    DeviceNode sensor2{"humidity_sensor", 2}; // 故障设备
    DeviceNode actuator{"pump", 0};
    
    gateway.children.push_back(&sensor1);
    gateway.children.push_back(&sensor2);
    sensor1.children.push_back(&actuator);
 
    // 定期检查(每5秒)
    while (1) {
        Serial.println("\n=== Starting device check ===");
        dfs_check_devices(&gateway, device_monitor_callback);
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}
 
void setup() {
    Serial.begin(115200);
    xTaskCreate(task_device_check, "DeviceCheck", 4096, NULL, 1, NULL);
}
 
void loop() {}

场景说明:
遍历设备树结构,前序回调检查设备状态,后序回调汇总统计信息,适合物联网设备监控场景。

6、JSON配置解析

#include <Arduino.h>
#include <FreeRTOS.h>
#include <task.h>
#include <stack>
#include <ArduinoJson.h>
 
// JSON节点结构
struct JsonNode {
    String key;
    JsonVariant value;
    std::vector<JsonNode*> children;
};
 
// 回调类型定义
typedef void (*JsonCallback)(JsonNode* node, bool isVisit);
 
// JSON文档转树结构
JsonNode* json_to_tree(const String& jsonStr) {
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, jsonStr);
    JsonObject root = doc.as<JsonObject>();
    
    JsonNode* rootNode = new JsonNode{"root", JsonVariant(), {}};
    std::stack<std::pair<JsonObject, JsonNode*>> stack;
    stack.push({root, rootNode});
 
    while (!stack.empty()) {
        auto [obj, parent] = stack.top();
        stack.pop();
 
        for (JsonPair kv : obj) {
            JsonNode* node = new JsonNode{
                kv.key().c_str(),
                kv.value(),
                {}
            };
            parent->children.push_back(node);
 
            if (kv.value().is<JsonObject>()) {
                stack.push({kv.value().as<JsonObject>(), node});
            }
        }
    }
    return rootNode;
}
 
// 带回调的DFS遍历
void dfs_json(JsonNode* root, JsonCallback callback) {
    std::stack<std::pair<JsonNode*, bool>> stack;
    stack.push({root, false});
 
    while (!stack.empty()) {
        auto [node, visited] = stack.top();
        
        if (!visited) {
            callback(node, true); // 访问前
            stack.top().second = true;
 
            // 逆序压入子节点
            for (auto it = node->children.rbegin(); it != node->children.rend(); ++it) {
                stack.push({*it, false});
            }
        } else {
            callback(node, false); // 访问后
            stack.pop();
        }
    }
}
 
// 示例回调:配置验证
void config_validate_callback(JsonNode* node, bool isVisit) {
    if (isVisit) {
        // 前序处理:检查必填字段
        if (node->key == "threshold" && node->value.is<int>()) {
            int val = node->value.as<int>();
            if (val < 0 || val > 100) {
                Serial.print("[WARN] Invalid threshold: ");
                Serial.println(val);
            }
        }
    } else {
        // 后序处理:复杂验证
        if (node->key == "sensor_config") {
            bool hasType = false, hasPin = false;
            for (auto child : node->children) {
                if (child->key == "type") hasType = true;
                if (child->key == "pin") hasPin = true;
            }
            if (!(hasType && hasPin)) {
                Serial.println("[ERROR] Incomplete sensor config");
            }
        }
    }
}
 
// RTOS任务
void task_json_parser(void *pvParameters) {
    const char* jsonConfig = R"(
    {
        "threshold": 120,
        "sensor_config": {
            "type": "DHT22",
            "interval": 2000
        },
        "network": {
            "ssid": "MyAP",
            "password": null
        }
    }
    )";
 
    JsonNode* configTree = json_to_tree(jsonConfig);
    dfs_json(configTree, config_validate_callback);
 
    // 清理内存(实际应设计更安全的释放机制)
    // ...
 
    vTaskDelete(NULL);
}
 
void setup() {
    Serial.begin(115200);
    xTaskCreate(task_json_parser, "JsonParser", 8192, NULL, 1, NULL);
}
 
void loop() {}

场景说明:
解析JSON配置文件,前序回调进行简单验证,后序回调实现复杂结构检查,适合嵌入式配置管理。

要点解读
非递归实现关键
使用显式栈(std::stack)替代系统调用栈,避免递归深度限制
通过visited标记区分首次访问(前序)和回溯阶段(后序)
逆序压入子节点保证处理顺序与递归一致
回调机制设计
前序回调(isEnter/isPreOrder)适合资源预分配和初步检查
后序回调(isLeave)适合状态汇总和复杂验证
回调函数应保持轻量级,避免阻塞RTOS调度
内存管理策略
动态分配节点时需配套释放机制(案例三简化处理)
对于资源受限设备,建议使用对象池预分配节点
注意ArduinoJson文档的生命周期管理
RTOS集成要点
长时间运行的DFS应拆分为多个任务片段(本例未展示,可通过xTaskDelay分段)
共享数据结构(如设备树)需通过队列或互斥锁保护
任务堆栈大小需根据树深度调整(案例二使用4KB)
错误处理增强
栈操作前检查stack.empty()防止异常
回调函数中增加异常状态上报(如通过RTOS队列)
添加看门狗监控防止遍历任务阻塞(如configASSERT)

注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

驴友花雕

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

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

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

打赏作者

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

抵扣说明:

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

余额充值