手搓尾插法树类代码详解:从实现到优化的全流程指南
一、简介和特点
尾插法树是一种基于链表实现树结构的手动编码方式,通过将节点的孩子节点以尾插方式插入链表,实现树形数据的存储与操作。本文代码示例采用C++模板类,允许泛型数据类型,核心特点包括:
1. 使用listnode作为子节点链表单元,treenode存储数据及子节点链表头指针;
2. tree类管理节点数组,支持动态分配内存,提供根节点设置、添加子节点、数据赋值及打印功能;
3. 尾插法插入子节点时,从链表末尾添加,避免头插法导致的遍历顺序颠倒,逻辑更直观。
二、优点
1. 逻辑清晰:尾插法按节点自然顺序添加,符合“父-子”层级关系,减少遍历时的反向调整;
2. 内存管理可控:通过节点数组预分配(如tree(int maxnodes)),避免频繁动态分配内存碎片;
3. 灵活性:模板类支持任意数据类型,适用于不同场景(如整数树、字符串树等);
4. 简洁性:相比递归构建或复杂指针操作,代码结构更易理解,适合新手学习树结构基础。
三、实现步骤
1. 定义节点结构
listnode:链表节点,存储指向treenode的指针,通过next指向下一个节点;
treenode:树节点,包含数据data和子节点链表头指针childrenhead。
2. 构建树类tree
初始化节点数组:nodes为treenode指针数组,通过构造函数传入最大节点数(默认10000);
根节点设置:通过setroot(int rootid)将指定索引的节点设为根;
子节点添加:addchild(int parentid, int sonid)通过父/子ID定位节点,调用addchild(treenode* node)尾插子节点到父节点链表。
3. 数据操作与遍历
assigndata(int id, T data):为指定ID节点赋值;
print():递归打印根节点及其所有子节点,通过链表遍历子树。
四、代码及注释
#include <iostream> using namespace std; // 链表节点模板,存储指向T类型节点的指针 template <typename T> struct listnode { T* data; // 指向树节点的地址 listnode* next = nullptr; // 指向下一个节点 // 构造:传入节点指针初始化data listnode(T* d) { data = d; } // 空构造(可能用于初始化头节点) listnode() {} }; // 树节点模板,存储数据和子节点链表头指针 template <typename T> struct treenode { T data; // 节点数据 listnode<treenode<T>*>* childrenhead = nullptr; // 子节点链表头 // 添加子节点:尾插法 void addchild(treenode<T>* node) { // 创建新子节点链表节点,指向传入的node listnode<treenode<T>*>* newchild = new listnode<treenode<T>*>(node); if (childrenhead == nullptr) { // 若链表为空,直接作为头节点 childrenhead = newchild; } else { // 否则从末尾插入 listnode<treenode<T>*>* tmp = childrenhead; while (tmp->next) { // 遍历到末尾 tmp = tmp->next; } tmp->next = newchild; // 尾插新节点 } } }; // 树类模板,管理节点数组和树操作 template <typename T> class tree { treenode<T>* nodes; // 节点数组指针 treenode<T>* root = nullptr; // 根节点 public: // 构造:默认分配10000个节点 tree() { nodes = new treenode<T>[10000]; } // 构造:传入最大节点数 tree(int maxnodes) { nodes = new treenode<T>[maxnodes]; } // 析构:释放节点数组 ~tree() { delete[] nodes; } // 设置根节点(通过ID索引) void setroot(int rootid) { root = &nodes[rootid]; } // 添加子节点(通过父/子ID) void addchild(int parentid, int sonid) { treenode<T>* parent = &nodes[parentid]; treenode<T>* son = &nodes[sonid]; parent->addchild(son); } // 为节点赋值 void assigndata(int id, T data) { treenode<T>* node = &nodes[id]; node->data = data; } // 递归打印节点及其子节点 void print(treenode<T>* node) { cout << node->data; // 打印当前数据 listnode<treenode<T>*>* tmp = node->childrenhead; while (tmp) { // 遍历子节点链表 print(tmp->data); // 递归打印子节点 tmp = tmp->next; } } // 打印整棵树(从根开始) void print() { print(root); } };
五、总结
本文通过尾插法树类的代码示例,展示了手动实现树结构的基础逻辑:利用链表维护子节点顺序,通过节点数组简化内存管理。该实现适合学习树结构原理,尤其适合新手理解“尾插”操作如何避免头插法的遍历问题。在实际应用中,可根据需求扩展(如添加删除节点、搜索等功能),但需注意内存释放细节(如节点删除时链表清理)。通过注释与步骤解析,读者可快速掌握从零构建树类的方法,为更复杂的算法设计打下基础。
原创内容 转载请注明出处