假设:有一个n个元素的完全二叉树,为了使其成为满二叉树,补全没有孩子的节点是的除了叶节点所有节点都有两个孩子,即最低层皆为-1.

例1:

              1

         2          3

      4     5     -1    6

     -1  -1     -1  -1                -1   -1

补全的节点赋值为-1,表示当前无节点,需转向别的子树。

step 1:首先,对于一棵二叉树,需定义一个节点的类模板:

包括:节点键值、左子树指针、右子树指针

template <typename T>
class binaryTreeNode {
public:
T element;
binaryTreeNode<T>* leftChild;
binaryTreeNode<T>* rightChild; binaryTreeNode(){leftChild = NULL;rightChild = NULL;}
};

step 2:如何创建一棵二叉树呢?根据链表的特性,成员变量为:节点指针类型的mRoot根节点。除此之外,还包括常见的成员函数:如 获取树的当前规模、获取树的深度、打印或输出树及销毁二叉树等操作。

 template <typename T>
class binaryTree {
private:
binaryTreeNode<T>* mRoot;//树根 int getSize(binaryTreeNode<T>*);
int getHeight(binaryTreeNode<T>*); void preOrder(binaryTreeNode<T>*);
void inOrder(binaryTreeNode<T>*);
void postOrder(binaryTreeNode<T>*);
void distroy(binaryTreeNode<T>*&);
binaryTreeNode<T>* AddNode(const T& key,int direction,binaryTreeNode<T>*& root); public:
binaryTree();
virtual ~binaryTree();
binaryTreeNode<T>* Create();//递归的创建二叉树的节点
void AddNode(const T& key,int direction); int getSize();//递归得到树的节点数目
int getHeight();//递归得到树的高度 //递归遍历
void preOrder();//前序遍历
void inOrder();//中序遍历
void postOrder();//后序遍历 //删除二叉树
void distroy(); };

>>首先,怎么在二叉树中插入节点呢?
可以想到的是,插入新节点的时候需要标明 插得是左孩子还是右孩子which,在哪一个父节点下插入where,及插入的键值是多少what,简单的来说,就是WWW问题

 template <typename T>
binaryTreeNode<T>* binaryTree<T>::AddNode(const T& key,int direction,binaryTreeNode<T>*& root)
{
if(direction == )//左孩子
{
if(root->leftChild == NULL){//找到对应的叶节点插入
root->leftChild = new binaryTreeNode<T>(key);
}
else{
root->leftChild = AddNode(key, direction, root->leftChild);
}
} else//右孩子
{
if (root->rightChild == NULL) {//找到相应的叶节点插入
root->rightChild = new binaryTreeNode<T>(key);
}
else{
root->rightChild = AddNode(key, direction, root->rightChild);
}
} return root;
} template <typename T>
void binaryTree<T>::AddNode(const T& key,int direction)
{
AddNode(key, direction, mRoot);
}

了解了这个思路后,可以不用一个个插入节点,用输入流的方式直接创建一棵树,如下程序:

 template <typename T>
binaryTreeNode<T>* binaryTree<T>::Create(){ binaryTreeNode<T>* current = NULL; T val; cin >> val;//输入键值 if(val == -)//标识当前子树为空,转向下一节点
{
return NULL;
} else{//递归的创建左右子树
current = new binaryTreeNode<T>;
current->element = val;
current->leftChild = Create();
current->rightChild = Create();
return current;
}
}

可以发现,-1是一个过渡标识,标明当前从递归左子树 转向 递归右子树。而上述创建程序是一个前序遍历,所谓前序遍历是指:

  1.先访问父节点

  2.递归左子树

  3.递归右子树

时间复杂度是O(N),因为遍历了每一个节点。

>>创建了二叉树后,怎么销毁?其实只要一一删除每个节点即可,考虑到链表结构,我们不能使用下标去删除节点,只能一个个的访问,而二叉树典型的遍历方法有:前序遍历、中序遍历 及 后序遍历。在这里,我们使用后序遍历进行递归删除, 即自下而上的删除。

 /*二叉树的销毁操作:后序遍历删除

 1)不能使用该声明:void distroy(binaryTreeNode<T>* pNode);该声明会创建一个局部的临时对象来保存传递的指针
虽然实参指针和局部指针都执行同一块堆空间,delete局部指针也会删除二叉树结构所占用的堆内存
但是实参指针将出现无所指的状态,出现不可预料的错误
因此传递的是指针的引用,这样才能将实参指针置空。 2)使用递归方法释放节点 */ template <typename T>
void binaryTree<T>::distroy(binaryTreeNode<T>*& pNode)
{
if(pNode)
{
distroy(pNode->leftChild);
distroy(pNode->rightChild);
delete pNode;
pNode = NULL;
}
}
template <typename T>
void binaryTree<T>::distroy()
{
distroy(mRoot);
}

如要删除如上例1中的二叉树,删除过程依次为:4 5 2 6 3 1

>>对于获取树的深度,有一种方法是,获取左右子树的深度,比较子树深度大小,大的那个增1即为树的深度了。当然,也是递归实现。

 template <typename T>
int binaryTree<T>::getHeight()
{
return getHeight(mRoot);
}
/*
获取当前节点的深度
递归的方法首先要设置截止条件,在进行递归操作。
0.约束条件:节点为空
1.递归左子树,每次递归加1
2.递归右子树,每次递归加1
3.比较左右子树深度,更深的子树+1即为当前节点深度。
*/ template <typename T>
int binaryTree<T>::getHeight(binaryTreeNode<T>* node)
{
if(node == NULL)
return ;
else{
int depL = getHeight(node->leftChild);
int depR = getHeight(node->rightChild);
return (depL > depR) ? depL+ : depR+;
} }

>>同理,获取树的规模只要遍历整棵树即可,这里用递归实现。这里仅给出前序遍历,后序遍历和中序遍历类似则不再给出。

 template <typename T>
void binaryTree<T>::preOrder()
{
cout <<"前序遍历: ";
preOrder(mRoot);
cout << endl;
} /*
前序遍历:
1.由于是递归实现,所以要设置截止条件:当前节点为空
2.先访问父节点,再访问左节点,最后访问右孩子 */
template <typename T>
void binaryTree<T>::preOrder(binaryTreeNode<T>* node)
{
if(node == NULL)
return;
else{
cout << node->element <<' ';
preOrder(node->leftChild);
preOrder(node->rightChild);
}
}

对于例1的遍历结果,如下:

输入:1   - -  - -  -  - -
前序遍历:
中序遍历:
后序遍历:
树的高度为: 3
树的节点数目: 6

>>总之呢,创建二叉树的全过程都用到了递归,那么递归到底是什么呢?

从定义上来讲:递归作为一种算法,是让函数/子程序/过程在程序运行过程中调用自身的方法,能够把一个较为复杂的问题经过层层转换,得到一个与原问题相似但是规模大大减小的问题来求解。递归方法大大减少了代码的复杂度。

实现方法:首先递归必须设置一个终止条件,当满足终止条件时,则递归返回。除此之外,则递归调用自身。

二叉树的基本功能实现方法(C++)的更多相关文章

  1. 使用CSS禁止textarea调整大小功能的方法

    这篇文章主要介绍了使用CSS禁止textarea调整大小功能的方法,禁止可以调整textarea大小功能的方法很简单,使用CSS的resize属性即可,需要的朋友可以参考下 如果你使用谷歌浏览器或火狐 ...

  2. WIN8 、WIN7 下IIS7.5、IIS8 的rewrite 伪静态功能设置方法

    原文 WIN8 .WIN7 下IIS7.5.IIS8 的rewrite 伪静态功能设置方法 win7和win8系统都自带有iis的功能.关于IIS的安装,上一篇已经讲述,这里就不重复了. 下面说下在w ...

  3. JAVAEE——BOS物流项目09:业务受理需求分析、创建表、实现自动分单、数据表格编辑功能使用方法和工作单快速录入

    1 学习计划 1.业务受理需求分析 n 业务通知单 n 工单 n 工作单 2.创建业务受理环节的数据表 n 业务通知单 n 工单 n 工作单 3.实现业务受理自动分单 n 在CRM服务端扩展方法根据手 ...

  4. HTML5调用手机摄像机、相册功能 <input>方法

    最近用MUI框架做webapp项目,在有PLUS环境的基础上能直接调用手机底层的API来使用拍照或从相册选择上传功能! 在查资料的时候,想起了另一种用input调用摄像和相册功能的方法,之前没有深入了 ...

  5. jQuery实现购物车计算价格功能的方法

    本文实例讲述了jQuery实现购物车计算价格功能的简易方法,做的比较简单,现分享给大家供大家参考.具体如下: 目的: <%@ page language="java" con ...

  6. Burpsuite神器常用功能使用方法总结

    Burpsuite介绍: 一款可以进行再WEB应用程序的集成攻击测试平台. 常用的功能: 抓包.重放.爆破 1.使用Burp进行抓包 这边抓包,推荐360浏览器7.1版本(原因:方便) 在浏览器设置代 ...

  7. PHPCMS v9的表单向导实现问答咨询功能的方法

    本文主要介绍了在phpcms v9的表单向导里实现问答咨询功能的方法 phpcms v9内容管理系统本身是没有问答模块的,只有表单向导,但表单向导有很大的局限性,通过表单向导,我们只能查看用户提交的信 ...

  8. Ubuntu上开启Apache Rewrite功能的方法

    Ubuntu上开启Apache Rewrite功能的方法 本文介绍ubuntn系统中开启apache的urlrewrite功能的方法. 在Windows上开启Apache的urlRewrite非常简单 ...

  9. JSP使用网站访问人数统计功能,方法与技巧

    实现网站访问人数统计功能的步骤: 创建静态登录页面,并指定表单提交由登录处理页面进行处理. 创建登录处理页面获得登录信息,查询数据库,判断该用户是否注册,如果该用户已注册,把已登录用户的信息保存在一个 ...

随机推荐

  1. (转)《SSO CAS单点系列》之 实现一个SSO认证服务器是这样的!

    上篇我们引入了SSO这个话题<15分钟了解SSO是个什么鬼!>.本篇我们一步步深入分析SSO实现机理,并亲自动手实现一个线上可用的SSO认证服务器!首先,我们来分析下单Web应用系统登录登 ...

  2. java之简单工厂

    1.使用步骤 创建抽象/接口产品类,定义具体产品的公共接口方法:(产品接口类) 创建具体产品类,是继承抽象产品类的:(产品接口实现类) 创建工厂类,通过创建静态方法根据传入不同参数从而创建不同具体产品 ...

  3. 【转载】open-falcon部署

    运维监控系统之Open-Falcon   一.Open-Falcon介绍 1.监控系统,可以从运营级别(基本配置即可),以及应用级别(二次开发,通过端口进行日志上报),对服务器.操作系统.中间件.应用 ...

  4. fileupload上传文件时带参数

    var userID = ""; $('#picture').fileupload({ url: "http://localhost:35708/Handler/File ...

  5. Linux学习笔记之Linux计划任务Crontab

    0x00 cron 简介 cron 是 UNIX, SOLARIS,LINUX 下的一个十分有用的工具.通过 cron 脚本能使计划任务定期地在系统后台自动运行. 0x01 cron 命令 cront ...

  6. Vue学习笔记之表单绑定输入

    vue的核心:声明式的指令和数据的双向绑定. 那么声明式的指令,已经给大家介绍完了.接下来我们来研究一下什么是数据的双向绑定? 另外,大家一定要知道vue的设计模式:MVVM M是Model的简写,V ...

  7. zabbix监控路由器

    在路由器上添加团体名: snmp-server enable traps snmp-server community XXXX rw   1.使用Getif查看路由器端口信息 getif-2.31

  8. CUDA、tensorflow与cuDNN的版本匹配问题【转】

    本文转载自:https://blog.csdn.net/MahoneSun/article/details/80809042 一.问题现象 CUDA.tensorflow 与 cuDNN有版本匹配的问 ...

  9. 编译 Tensorflow 1.10 + CUDA9.2 + MKL【转】

    本文转自:https://www.solarck.com/compile-tensorflow-gpu.html 我的电脑系统是基于 Archlinux 的 Manjaro,软件包更新的比较激进,很早 ...

  10. HDU 6425 Rikka with Badminton(组合问题签到)题解

    题意:问你有多少种选法使得不能满足大于等于2个拍子且大于等于1个球. 思路:数学组合问题,分类讨论一下,刚开始的时候分的很乱,写的乱七八糟的...还有注意MOD,基本上有大数相乘的地方都要先MOD一下 ...