数据结构与算法:树 二叉树入门(一)
Tips: 采用java语言,关注博主,底部附有完整代码
工具:IDEA
本系列介绍的是数据结构: 树
这是第1篇目前计划一共有12篇:
- 二叉树入门 本篇
- 顺序二叉树
- 线索化二叉树
- 堆排序
- 赫夫曼树(一)
- 赫夫曼树(二)
- 赫夫曼树(三)
- 二叉排序树(BST)
- 平衡二叉排序树AVL
- 2-3树,2-3-4树,B树 B+树 B*树 了解
- 数据结构与算法:树 红黑树 (十一)
敬请期待吧~~
高光事例:
遍历 | image |
---|---|
前序遍历 | |
中序遍历 | |
后续遍历 | |
层序遍历 |
常用术语
首先先来看一颗简单的树
首先最顶部叫做树根,也叫根节点
树根左右两侧分别有两个结点
- 左子结点
- 右子结点
左子结点和右子结点统称为子结点
那么换句话说就是 1的子结点有3 和 5
或者
- 1的左子结点是 3
- 1的右子结点是 5
此时3 是1的左子结点 ,3也是 12的父结点
通俗的说:3又是父亲又是儿子
- 3是12的父亲
- 3是1的儿子
其他叫法相同
例如
7的父结点是4
8的父结点是5
4的右子结点是6
正常情况下一颗树都有2个结点(左子结点 / 右子结点),如果一颗树只有一边有结点,例如这样:
那么8也可以叫做5的右残结点
此时8没有子结点,也可以叫叶子 或 叶子结点
在这颗树中12, 7, 6, 8 都是叶子结点
只要有父结点和子结点,那么这个整体就是一颗子树,例如这些
子树1 | 子树2 | 子树3 |
---|---|---|
树也可以分层,例如这样:
节点的层次:从根节点开始,假设根节点为第1层,根节点的子节点为第2层,依此类推,如果某一个节点位于第L层,则其子节点位于第L+1层 。
其他叫法可以参考:百度百科
树的形态
树的形态大致有3种:
- 完美二叉树
- 完全二叉树
- 完满二叉树 (满二叉树)
name | image | 描述 |
---|---|---|
完美二叉树 | 除了叶子结点之外的每一个结点都有2个孩子,并且每一层都被完全填充 | |
完全二叉树 | 最后一层的结点全部靠左,除了最后一层外其他层都完全填充 | |
完满二叉树 | 除了叶子结点外,如果你有孩子,那么必须有2个孩子 |
三者的区别:
- 完美二叉树一定是完全二叉树,完满二叉树
- 完全二叉树不一定是完满二叉树
- 如果一颗树是完满二叉树和完全二叉树,那么也不一定是完美二叉树
- 完满二叉树不一定是完全二叉树
这些话有点绕,只要把这三种树的特点记住,自己想一想就清晰啦~
创建一颗树
先创建一个结点类
public static class TreeHeroNode {public int id;public String name;// 左节点public TreeHeroNode leftNode;// 右节点public TreeHeroNode rightNode;public TreeHeroNode(int id, String name) {this.id = id;this.name = name;}@Overridepublic String toString() {return "TreeHeroNode{" +"id=" + id +", name='" + name + '\'' +'}';}
}
在创建一个树类
public class BinaryTree {private final TreeHeroNode root;/*** @param root 跟节点*/public BinaryTree(TreeHeroNode root) {this.root = root;if (this.root == null) {throw new NullPointerException("空树?");}}public static class TreeHeroNode {....}
}
直接传入根节点就可以啦
使用:
BinaryTree.TreeHeroNode root = new BinaryTree.TreeHeroNode(1, "张飞");
BinaryTree.TreeHeroNode node3 = new BinaryTree.TreeHeroNode(3, "关羽");
BinaryTree.TreeHeroNode node5 = new BinaryTree.TreeHeroNode(5, "吕布");
BinaryTree.TreeHeroNode node12 = new BinaryTree.TreeHeroNode(12, "马超");
BinaryTree.TreeHeroNode node4 = new BinaryTree.TreeHeroNode(4, "邢道荣");
BinaryTree.TreeHeroNode node8 = new BinaryTree.TreeHeroNode(8, "刘备");
BinaryTree.TreeHeroNode node7 = new BinaryTree.TreeHeroNode(7, "曹操");
BinaryTree.TreeHeroNode node6 = new BinaryTree.TreeHeroNode(6, "诸葛亮");// 设置节点
root.leftNode = node3;
root.rightNode = node5;node3.leftNode = node12;
node3.rightNode = node4;node4.leftNode = node7;
node4.rightNode = node6;node5.rightNode = node8;BinaryTree binaryTree = new BinaryTree(root);
此时结构图长这样:
可能有初学者朋友看到这里有点问题,为什么这里不直接写一个add() 方法添加呢…
因为: 树和其他结构不同,如果细分的话有
- 有序树
- 无序树
当前就是无序树,因为他没有顺序,所以说一个新结点加在左侧或者右侧根本控制不了,等后面讲到有序树的时候,自然就有添加方法啦!
本篇只需要把创建一颗树和树的遍历,查找,删除搞会即可!
树的遍历
树的遍历分为4种情况
前序遍历
中序遍历
后序遍历
层序遍历
前序遍历
前序遍历规则:
先遍历父结点,然后左子结点,最后右子结点,如果有子结点那么先遍历子结点
先来看一眼效果图:
来看一眼代码:
public static class TreeHeroNode {// 左节点public TreeHeroNode leftNode;// 右节点public TreeHeroNode rightNode;// 前序遍历public void show() {// 先打印父结点System.out.println(this);// 在打印左子结点if (leftNode != null) {leftNode.show();}// 在打印右子结点if (rightNode != null) {rightNode.show();}}@Overridepublic String toString() {return "TreeHeroNode{" +"id=" + id +", name='" + name + '\'' +'}';}
}
这里采用的是递归的办法进行遍历,如果递归看不懂,可以使用debug多走几次!
重点是思路!
中序遍历
能看懂前序遍历中序遍历就简单多了:
中序遍历规则:
先遍历左子结点,在遍历父结点,最后遍历右子结点,有子结点先遍历子结点
效果图:
代码:
public static class TreeHeroNode {// 左节点public TreeHeroNode leftNode;// 右节点public TreeHeroNode rightNode;// 中序遍历public void show() {// 先打印左子结点if (leftNode != null) {leftNode.show();}// 在打印父结点System.out.println(this);// 在打印右子结点if (rightNode != null) {rightNode.show();}}@Overridepublic String toString() {return "TreeHeroNode{" +"id=" + id +", name='" + name + '\'' +'}';}
}
后序遍历
后续遍历规则:
先遍历左子结点,在遍历右子结点,最后遍历父结点
效果图:
完整代码:
public static class TreeHeroNode {// 左节点public TreeHeroNode leftNode;// 右节点public TreeHeroNode rightNode;// 中序遍历public void show() {// 先打印左子结点if (leftNode != null) {leftNode.show();}// 在打印右子结点if (rightNode != null) {rightNode.show();}// 最后打印父结点System.out.println(this);}@Overridepublic String toString() {return "TreeHeroNode{" +"id=" + id +", name='" + name + '\'' +'}';}
}
前序,中序,后序遍历:
最重要的就是父结点
- 父结点先遍历就是前序遍历
- 父结点最后遍历就是后续遍历
- 父结点在中间就是中序遍历
层序遍历
什么是层序遍历?
上面也提到过,树是有层级别的,再来看一眼这张图:
层序遍历就是从第一层开始,依次往下,并且从左到右遍历
这张图层序遍历就是 1,3,5,12,4,8,7,6
来看看完整效果图:
看着这张图有点怪,其实这就是详细流程图!
整体思路就是通过一个队列(Queue)
- 先把root结点保存起来
- 通过一个while循环,删除当前结点
- 在删除的时候,如果左子结点或右子结点不为null,那就就添加到队列中
- 知道这个队列中没有数据证明已经遍历完成了
来看看完整代码:
# TreeHeroNode.java// 层序遍历
public void showFloor() {Queue<TreeHeroNode> queue = new LinkedList<>();// 添加当前结点到队列中queue.add(this);// 如果队列中一直有数据就一直循环,知道队列为nullwhile (!queue.isEmpty()) {// 移除队列中的元素TreeHeroNode removeNode = queue.remove();System.out.println(removeNode);// 如果左节点不为null 就添加到队列中if (removeNode.leftNode != null) {queue.add(removeNode.leftNode);}// 如果右节点不为null 就添加到队列中if (removeNode.rightNode != null) {queue.add(removeNode.rightNode);}}
}
走到这里4大遍历就完成了!
树的查找
前序查找
只要能看懂树的遍历,那么查找也应该就会了!
这里只以前序查找举例, 中序查找,后续查找,层序查找代码都雷同!
# TreeHeroNode.java// 树节点public static class TreeHeroNode {public int id;public String name;// 左节点public TreeHeroNode leftNode;// 右节点public TreeHeroNode rightNode;/*** 前序查找* @param valueID: 需要查找的ID*/public TreeHeroNode findValue(int valueID) {System.out.println(this);TreeHeroNode resultTemp = null;// 如果当前id和查找的id相同,这直接返回当前对象if (id == valueID) {return this;}// 查找左侧if (leftNode != null) {resultTemp = leftNode.findValue(valueID);}// 如果在查找左子树的过程中找到了那么就直接结束if(resultTemp != null){return resultTemp;}// 查找右子树if (rightNode != null) {resultTemp = rightNode.findValue(valueID);}// 查找完右子树直接返回// 如果是null 说明没有找到return resultTemp; }
}
树的删除
前序删除
删除结点和查找结点不同
因为要删除的结点可能是
- 子树
- 叶子结点
- 左子 / 右子结点
假设删除的是子树,那么目前就让整颗子树删除,例如这样:
那么删除一个叶子结点就是这样:
整体思路:
- 首先得会前序遍历
- 然后在遍历的过程中,如果找到了要删除的结点
- 先要看一下他是左子结点 还是右子结点
- 如果是左子结点,那么就让他左子结点 = null 即可
- 如果是右子结点,那么就让他右子结点 = null 即可
来看看完整代码:
// 树节点
public static class TreeHeroNode {public int id;public String name;// 左节点public TreeHeroNode leftNode;// 右节点public TreeHeroNode rightNode;public TreeHeroNode removeID(int valueID) {TreeHeroNode resultTemp;// 前序删除if (id == valueID) {return this;}// 左子节点 != null 就递归查找if (leftNode != null) {resultTemp = leftNode.removeID(valueID);// 如果找到了当前左节点,那么让当前左节点置空即可if (resultTemp != null) {leftNode = null;}}// 查找右子节点if (rightNode != null) {resultTemp = rightNode.removeID( valueID);// resultTemp != null 表示找到了右子节点,将节点置空即可if (resultTemp != null) {rightNode = null;}}return null;}}
在后面的系列中,会讲解如何删除一个结点就删除一个结点,而不是删除整颗树的! 不要心急,耐心期待吧~~
完整代码
原创不易,您的点赞就是对我最大的支持!
其他树结构文章:
- 二叉树入门 本篇
- 顺序二叉树
- 线索化二叉树
- 堆排序
- 赫夫曼树(一)
- 赫夫曼树(二)
- 赫夫曼树(三)
- 二叉排序树(BST)
- 平衡二叉排序树AVL
- 2-3树,2-3-4树,B树 B+树 B*树 了解
- 数据结构与算法:树 红黑树 (十一)
数据结构与算法:树 二叉树入门(一)相关推荐
- 数据结构与算法之二叉树的序列化和反序列化及判断一棵树是否为平衡二叉树
数据结构与算法之二叉树的序列化和反序列化及判断一棵树是否为平衡而二叉树 目录 二叉树的序列化和反序列化 判断一棵树是否为平衡而二叉树 1. 二叉树的序列化和反序列化 1. 递归版本序列化和反序列化 代 ...
- 数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树
[本文谢绝转载,原文来自http://990487026.blog.51cto.com] 树 数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树二叉树的创建,关系建立二叉树的创建 ...
- 常考数据结构与算法:输出二叉树的右视图
题目描述 请根据二叉树的前序遍历,中序遍历恢复二叉树,并打印出二叉树的右视图 上图树的右视图为:{1,4,3,7} 做此题之前可以先做下面3道题: 1. 常考数据结构与算法:求二叉树的层序遍历 2.常 ...
- 数据结构与算法之-----二叉树(一)
[ 写在前面的话:本专栏的主要内容:数据结构与算法. 1.对于初识数据结构的小伙伴们,鉴于后面的数据结构的构建会使用到专栏前面的内容,包括具体数据结构的应用,所使用到的数据结构,也是自己构建的,未使用 ...
- 数据结构与算法:二叉树专题
数据结构与算法:二叉树专题 前言 前提条件 基础知识 二叉树链式存储结构 二叉树中序遍历 二叉树层序遍历 常见编程题 把一个有序整数数组放到二叉树中 逐层打印二叉树结点数据 求一棵二叉树的最大子树和 ...
- 数据结构与算法之二叉树的先序遍历,中序遍历,后序遍历
数据结构与算法之二叉树的先序遍历,中序遍历,后移遍历 目录 实现二叉树的先序,中序,后序遍历,包括递归方式和非递归方式 在二叉树中找到一个节点的后继节点 1. 实现二叉树的先序,中序,后序遍历,包括递 ...
- 数据结构与算法练习-二叉树中序遍历
python数据结构与算法练习-二叉树中序遍历 二叉树中序遍历 思路 python实现 二叉树中序遍历 链接: link. 给定一个二叉树的根节点 root ,返回它的 中序 遍历. 样例 输入:ro ...
- Python__数据结构与算法——树、二叉树(实现先、中、后序遍历)
目录 一.树 二.二叉树 树和前面所讲的表.堆栈和队列等这些线性数据结构不同,树不是线性的.在处理较多数据时,使用线性结构较慢,而使用树结构则可以提高处理速度.不过,相对于线性的表.堆栈和队列等线性数 ...
- Java数据结构与算法——树(基本概念,很重要)
声明:码字不易,转载请注明出处,欢迎文章下方讨论交流. 有网友私信我,期待我的下一篇数据结构.非常荣幸文章被认可,也非常感谢你们的监督. 前言:Java数据结构与算法专题会不定时更新,欢迎各位读者监督 ...
最新文章
- Cookie实现记住密码、自动登录
- 液相色谱柱PHP,C18液相色谱柱 - 食品仪器分析 - 食品论坛 - Powered by Discuz!
- LeetCode 1353. 最多可以参加的会议数目(排序+贪心,优先队列,难)
- (译)如何使用GameCenter制作一个简单的多人游戏教程:第一部分
- 浅谈CC攻击原理与防范
- 何小鹏谈财务自由:痛苦彷徨,7块钱的快餐只能偷偷吃
- Autograd看这一篇就够了!
- Python Web开发
- python统计pdf字数_Python统计字数的思路详解
- 小米无线路由器服务器用户名和密码忘了,小米路由器忘记密码怎么解决?设置新密码登陆方法介绍...
- wavread被删之后的替代audioread
- informix 访问mysql_C语言访问INFORMIX数据库
- 蓝桥杯 基础练习 字母图形
- android手机如何加速,小技巧:如何给Android手机上的Chrome浏览器加速
- 单应性矩阵的理解及求解1
- win10 下载 linux系统安装教程,windows10电脑中如何安装linux子系统
- Unity使用键盘wasd控制绑定角色和第一人称摄像机随鼠标移动
- Android指纹解锁源码分析
- java中三web_Java Web中的三大器
- 用TensorFlow和TFSlim实现图像分类与分割