数据结构(c++版):二叉树的实现

让我们走进"二叉树童话王国",用生活中的故事理解每一个代码概念!

📚 第一章:书架的魔法格子------节点结构体

cpp 复制代码
template<typename T>
struct treeNode
{
    T data;                // 格子里藏的宝物
    treeNode<T>* left;     // 左边的魔法门
    treeNode<T>* right;    // 右边的魔法门
    
    treeNode() :data(0), left(NULL), right(NULL) {};      // 空空的初始格子
    treeNode(T d) :data(d), left(NULL), right(NULL) {};   // 放入宝物的格子
};

🏰 城堡里的宝物格子

想象一座魔法城堡,每个房间都有一个特殊的宝物格子:

  • data:格子里藏的宝物(可能是金苹果、魔法书或水晶球)
  • left:左边的魔法门,通向比当前宝物"年龄小"的宝物房间
  • right:右边的魔法门,通向比当前宝物"年龄大"的宝物房间

🧙♂️ ​老巫师的两种魔法​:

  1. treeNode():创造一个空空的宝物格子(还没放入任何宝物)
  2. treeNode(T d):创造一个已经放入宝物d的格子

就像城堡管家说:"第7号宝物格子放着'金苹果',它左边的门通向3号房间,右边的门通向11号房间"

🏛️ 第二章:整个魔法城堡------二叉树类

cpp 复制代码
template<typename T>
class Tree
{
private: 
    treeNode<T>* nodes;    // 城堡所有房间的登记簿
    treeNode<T>* root;     // 城堡的主入口
    size_t nodesize;       // 城堡总房间数
    
    treeNode<T>* creat(T a[], int size, int nodeId, T nullNode);
    void visit(treeNode<T>* node);  // 参观一个宝物房间
    void preOrder(treeNode<T>* node);
    void inOrder(treeNode<T>* node);
    void postOrder(treeNode<T>* node);
    
public:
    Tree();
    Tree(int maxidex);
    ~Tree();
    
    treeNode<T>* Gertreenode(int Id);
    void creatTree(T a[], int size, T nullNode);
    
    void preorder();   // 探险路线1:先看当前,再看左右
    void inorder();    // 探险路线2:先看左,再看当前,最后右
    void postorder();  // 探险路线3:先看左右,最后看当前
};

🏯 魔法城堡的构造

  • nodes:城堡所有房间的登记簿(记录每个房间的位置和内容)
  • root:城堡的主入口大门(从这里开始探险)
  • nodesize:城堡总共有多少个房间

🗝️ 城堡的三种探险路线

  1. preorder:勇者路线(先看当前宝物,再探索左右)
  2. inorder:学者路线(按宝物年龄顺序探索)
  3. postorder:清洁工路线(从最远的房间开始打扫)

🏗️ 第三章:建造与拆除城堡------构造与析构

cpp 复制代码
// 默认建造能容纳100000个宝物的大城堡
template<typename T>
Tree<T>::Tree() { nodes = new treeNode<T>[100000]; }

// 按国王的要求建造特定大小的城堡
template<typename T>
Tree<T>::Tree(int maxindex) { nodes = new treeNode<T>[maxindex]; }

// 拆除城堡(当王国灭亡时)
template<typename T>
Tree<T>::~Tree() { delete[]nodes; }

👑 国王的建造令

  1. 默认城堡 :当国王没说具体大小时,建一个能放100000个宝物的大城堡(Tree()
  2. 定制城堡 :当国王说"我要一个能放maxindex个宝物的城堡"(Tree(int maxindex)
  3. 拆除城堡 :当王国灭亡时,拆除整个城堡释放土地(~Tree()

就像建筑大臣说:"陛下,按您的要求,我们建造了一个有15个宝物房间的城堡!"

🗺️ 第四章:布置城堡房间------创建树

cpp 复制代码
void Tree<T>::creatTree(T a[], int size, T nullNode) {
    root = creat(a, size, 1, nullNode); // 从1号主殿开始布置
}

treeNode<T>* Tree<T>::creat(T a[], int size, int nodeId, T nullNode) {
    if (nodeId >= size || a[nodeId] == nullNode) {
        return NULL; // 这个位置不设宝物房间
    }
    
    treeNode<T>* nownode = Gertreenode(nodeId); // 找到这个房间
    nownode->data = a[nodeId]; // 放入宝物
    
    // 布置左边的房间(编号是当前的两倍)
    nownode->left = creat(a, size, 2 * nodeId, nullNode);
    
    // 布置右边的房间(编号是当前的两倍加一)
    nownode->right = creat(a, size, 2 * nodeId+1, nullNode);
    
    return nownode; // 返回布置好的房间
}

🧭 城堡布局规则

  1. 主殿:1号房间是城堡的主入口(root)
  2. 左翼:每个房间n的左房间是2n号
  3. 右翼:每个房间n的右房间是2n+1号
  4. 空房间:如果设计图上标记nullNode(如'-'),就不布置这个房间

🏗️ ​布置过程​:

拜访顺序 ​:a → b → d → g → h → c → e → f → i

就像一位有礼貌的客人,每到一家都先问候最年长的主人,然后再去见他的孩子们。

第二把钥匙:🗝️ "年龄顺序"钥匙(中序遍历)

cpp 复制代码
void inOrder(treeNode<T>* node) {
    if (node) {
        inOrder(node->left);  // 先拜访所有左边的晚辈
        visit(node);          // 然后问候当前长辈
        inOrder(node->right); // 最后拜访右边的晚辈
    }
}

拜访故事 ​:

族长a说:"这把钥匙的访客很讲究顺序,他们从最年轻的开始拜访,最后才来见我这位最年长的。"

拜访路线:

拜访顺序 ​:g → d → h → b → a → e → c → i → f

就像整理家族相册,从最年轻的婴儿照片开始,慢慢看到最年长的祖辈照片。

第三把钥匙:🗝️ "晚辈优先"钥匙(后序遍历)

cpp 复制代码
void postOrder(treeNode<T>* node) {
    if (node) {
        postOrder(node->left);  // 先拜访所有左边的晚辈
        postOrder(node->right); // 然后拜访右边的晚辈
        visit(node);            // 最后问候当前长辈
    }
}

拜访故事 ​:

族长a笑着说:"这把钥匙的访客最有耐心,他们要把所有孩子都见完了,最后才来见我这位老祖宗。"

拜访路线:

拜访顺序 ​:g → h → d → b → e → i → f → c → a

就像发压岁钱,必须先给最小的孩子发完,最后才给最年长的长辈。

🌟 魔法钥匙的现实应用

🎄 圣诞节礼物配送(前序遍历)

圣诞老人用"族长优先"钥匙:

📚 图书馆整理书籍(中序遍历)

图书管理员用"年龄顺序"钥匙:

🧹 大扫除顺序(后序遍历)

清洁工用"晚辈优先"钥匙:

🎯 验证我们的魔法探险

cpp 复制代码
int main() {
    const char nullNode = '-';
    char a[16] = {
        '-','a','b','c','d','-','e','f',
        'g','h','-','-','i','-','-','-'
    };
    
    Tree<char> magicTree(16);
    magicTree.creatTree(a, 16, nullNode);
    
    cout << "族长优先钥匙: ";
    magicTree.preorder(); cout << endl;  // abdghcefi
    
    cout << "年龄顺序钥匙: ";
    magicTree.inorder(); cout << endl;   // gdhbaecif
    
    cout << "晚辈优先钥匙: ";
    magicTree.postorder(); cout << endl; // ghdbeifca
    
    return 0;
}

运行结果完全匹配我们的探险故事!​​ 🎉

💫 魔法森林的智慧

通过这次魔法森林的探险,我们学到了:

记住这个魔法口诀:

下次当你需要遍历一棵树时,就想想这座魔法森林,选择适合的钥匙来开启你的探险之旅!

  • 管家拿着设计图(a[])走遍城堡
  • 在1号主殿放入宝物'A'
  • 然后去2号(1×2)左殿放宝物'B'
  • 再去3号(1×2+1)右殿放宝物'C'
  • 遇到'-'就跳过不布置

🌳 二叉树探险记:三把神奇钥匙打开魔法森林 🌳

想象一下,你面前有一座神秘的魔法森林,里面住着9个会说话的字魔法精灵:a、b、c、d、e、f、g、h、i。它们住在一棵神奇的家族树屋里,每个精灵都有自己的房间,房间之间用魔法楼梯相连。

🏰 魔法树屋的建造蓝图

cpp 复制代码
char a[16] = {
    '-',   // 0号位置:地基,不建房
    'a',   // 1号:族长爷爷a的顶层套房
    'b',   // 2号:大儿子b的房间
    'c',   // 3号:二儿子c的房间  
    'd',   // 4号:孙子d的房间(b的孩子)
    '-',   // 5号:空房间(b的另一个孩子出国了)
    'e',   // 6号:孙子e的房间(c的孩子)
    'f',   // 7号:孙子f的房间(c的另一个孩子)
    'g',   // 8号:曾孙g的房间(d的孩子)
    'h',   // 9号:曾孙h的房间(d的另一个孩子)
    '-',   // 10号:空房间
    '-',   // 11号:空房间
    'i',   // 12号:曾孙i的房间(f的孩子)
    '-',   // 13号:空房间
    '-',   // 14号:空房间
    '-'    // 15号:空房间
};
cpp 复制代码
        族长a(顶层)
        /      \
     儿子b     儿子c
      /        /   \
   孙子d    孙子e  孙子f
    /   \           /
曾孙g  曾孙h     曾孙i

🔑 三把神奇的拜访钥匙

森林守护者给了我们三把不同的魔法钥匙,每把钥匙都对应一种特殊的拜访顺序:

第一把钥匙:🗝️ "族长优先"钥匙(前序遍历)

cpp 复制代码
void preOrder(treeNode<T>* node) {
    if(node) {
        visit(node);          // 先拜访当前房间的精灵
        preOrder(node->left);  // 然后探索左边的所有子孙
        preOrder(node->right); // 最后探索右边的所有子孙
    }
}

拜访故事 ​:

族长爷爷a说:"用这把钥匙的访客都很尊重长辈,他们总是先来拜访我!"

拜访路线:

  • 🎯 先敲族长a的门:"爷爷好!"
  • 📍 然后去左边找儿子b:"b叔叔好!"
  • 📍 b说:"先去见我左边的孩子d"
  • 📍 d说:"先见我左边的g"
  • 👶 见到g:"g小朋友好!"
  • 📍 回到d:"现在见我右边的h"
  • 👶 见到h:"h小朋友好!"
  • 🔄 回到族长a:"左边的都拜访完了,现在去右边"
  • 📍 找儿子c:"c叔叔好!"
  • 📍 c说:"先见我左边的e"
  • 👶 见到e:"e小朋友好!"
  • 📍 c说:"现在见我右边的f"
  • 📍 f说:"先见我左边的i"
  • 👶 见到i:"i小朋友好!"
  • 🔍 从族长a开始,但先不敲门
  • 📍 去找最左边的g(最小的曾孙)
  • 👶 敲门:"g小朋友好!"
  • ↩️ 回到g的爸爸d:"d叔叔好!"
  • 📍 d说:"现在去见我右边的h"
  • 👶 见到h:"h小朋友好!"
  • ↩️ 回到d的爸爸b:"b爷爷好!"
  • ↩️ 回到族长a:"族长爷爷好!"
  • 📍 a说:"现在去右边找e"
  • 👶 见到e:"e小朋友好!"
  • ↩️ 回到e的爸爸c:"c爷爷好!"
  • 📍 c说:"现在去见我右边的f"
  • 📍 f说:"先见我左边的i"
  • 👶 见到i:"i小朋友好!"
  • ↩️ 回到f:"f叔叔好!"
  • 🔍 从族长a开始,但忍着不敲门
  • 📍 先找到最左边的g
  • 👶 敲门:"g小朋友好!"
  • 📍 然后找g的兄弟h
  • 👶 敲门:"h小朋友好!"
  • ↩️ 回到g和h的爸爸d:"d叔叔好!"
  • ↩️ 回到d的爸爸b:"b爷爷好!"
  • 📍 现在去右边找e
  • 👶 敲门:"e小朋友好!"
  • 📍 然后找f的孩子i
  • 👶 敲门:"i小朋友好!"
  • ↩️ 回到i的爸爸f:"f叔叔好!"
  • ↩️ 回到f的爸爸c:"c爷爷好!"
  • 🎯 最后终于敲族长a的门:"族长爷爷好!我们见过所有孩子了!"
  • 先给族长送礼物,然后从左到右给孩子们送
  • 确保最重要的长辈先收到礼物
  • 从最旧的书开始整理,到最新的书
  • 自然形成有序排列
  • 从最里面的小房间开始打扫
  • 最后打扫大厅,这样不会重复弄脏
  • 不同的拜访顺序就像不同的社交礼仪
  • 前序遍历尊重长辈,适合重要消息传递
  • 中序遍历讲究顺序,适合整理和搜索
  • 后序遍历关爱晚辈,适合清理和删除操作
  • 前序:我在前,儿孙后
  • 中序:儿在前,我在中,孙在后
  • 后序:儿孙全拜访,最后见老子

(打扫主殿王冠)

🌟 第七章:二叉树王国的智慧

通过这个童话故事,我们学到了:

  1. 节点就像魔法房间:每个房间存放宝物,并有通向其他房间的门
  2. 建造规则:左房间编号是当前的2倍,右房间是2倍加1
  3. 三种探险方式
    • 勇者喜欢先看眼前(前序)
    • 学者喜欢按顺序查看(中序)
    • 清洁工从最底层开始(后序)

下次当你看到二叉树时,就想象这座魔法城堡,不同的遍历方式就像不同身份的人在城堡中探索!

源码及运行:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
template<typename T>
struct treeNode
{

	T data;
	treeNode<T>* left;
	treeNode<T>* right;
	treeNode() :data(0), left(NULL), right(NULL) {};
	treeNode(T d) :data(d), left(NULL), right(NULL) {};
	
};
template<typename T>

class Tree
{
private: treeNode<T>* nodes;//所有节点的节点值相当于一个数组分配在堆上
	   treeNode<T>* root;//根节点
	   size_t nodesize;//有多少节点

	   treeNode<T>* creat(T a[], int size, int nodeId, T nullNode);
	   void visit(treeNode<T>* node);//访问单个节点时需要做些事情
	   void preOrder(treeNode<T>* node);
	   void inOrder(treeNode<T>* node);
	   void postOrder(treeNode<T>* node);
public:
	Tree();
	Tree(int maxidex);
	~Tree();
	treeNode<T>* Gertreenode(int Id);
	void creatTree(T a[], int size, T nullNode);//传入一个顺序表储存的树从而生成二叉树, T nullNode要定义空树时用这个定义;
	void preorder();
	void inorder();
	void postorder();//因为给外部调用,从根节点开始所以不用传参
};
template<typename T>
Tree<T>::Tree()
{
	 nodes = new treeNode<T>[100000];
}
template<typename T>
Tree<T>::Tree(int maxindex)
{
	nodesize = maxindex;
	nodes = new treeNode<T>[nodesize];
}
template<typename T>
Tree<T>::~Tree()
{
	delete[]nodes;

}

template<typename T>
treeNode<T>* Tree<T>::Gertreenode(int Id)
{
	return &nodes[Id];
}
template<typename T>
void Tree<T>:: creatTree(T a[], int size, T nullNode)
{
	root = creat(a, size, 1, nullNode);
}
template<typename T>
treeNode<T>* Tree<T>:: creat(T a[], int size, int nodeId, T nullNode)
{
	if (nodeId >= size || a[nodeId] == nullNode)
	{
		return NULL;
	}
	treeNode<T>* nownode = Gertreenode(nodeId);
	nownode->data = a[nodeId];
	nownode->left = creat(a, size, 2 * nodeId, nullNode);
	nownode->right = creat(a, size, 2 * nodeId+1, nullNode);
	return nownode;
}
template<typename T>
void Tree<T>::visit(treeNode<T>* node)
{
	cout << node->data;
}
template<typename T>
void Tree<T>::preOrder(treeNode<T>* node)
{
	if(node)
	{
		visit(node);
		preOrder(node->left);
		preOrder(node->right);
	}

}
template<typename T>
void Tree<T>::inOrder(treeNode<T>* node)
{
	if (node)
	{
		
		inOrder(node->left);
        visit(node);
		inOrder(node->right);

	}
}
template<typename T>
void Tree<T>::postOrder(treeNode<T>* node)
{
	if (node)
	{
		
		postOrder(node->left);
		postOrder(node->right);
		visit(node);
	}
}
template<typename T>
void Tree<T>::preorder()
{
	preOrder(root);
}
template<typename T>
void Tree<T>::inorder()
{
	inOrder(root);
}
template<typename T>
void Tree<T>::postorder()
{
	postOrder(root);
}

int main()
{
	const char nullNpde = '-';
	char a[15] =
	{
		nullNpde,'a','b','c','d',
		nullNpde,'e','f','g','h',
		nullNpde,nullNpde,nullNpde,nullNpde,'i'
	};
	Tree<char>T(15);
	T.creatTree(a, 15, nullNpde);
	T.preorder(); cout << endl;
	T.inorder(); cout << endl;
	T.postorder(); cout << endl;
	return 0;
	
}
相关推荐
心态特好1 天前
解锁分布式唯一 ID:技术、实践与最佳方案
分布式·生活
weixin_lynhgworld1 天前
旧物新生,从二手回收小程序开启绿色生活
小程序·生活·旧物回收
互联网江湖3 天前
蓝桥杯出局,少儿编程的价值祛魅时刻?
人工智能·生活
wan5555cn3 天前
当代社会情绪分类及其改善方向深度解析
大数据·人工智能·笔记·深度学习·算法·生活
wan5555cn5 天前
国产电脑操作系统与硬盘兼容性现状分析:挑战与前景评估
人工智能·笔记·深度学习·机器学习·电脑·生活
leijiwen6 天前
从美团到朴朴、我店与远方好物:本地生活的四种演化路径与未来趋势
生活
正见TrueView6 天前
阿里美团京东从“三国杀”到“双雄会”:本地生活无限战争的终局猜想
dubbo·生活
“向阳的蛋”7 天前
2100AI智能生活(下)
ai·生活
中金快讯7 天前
第16届深圳国际移动电子展AI生活主题将带来哪些新体验?
生活