目录
一、树结构
1、树结构引出
树结构代表高效的查找与搜索语义
比如说电脑的文件管理器,还有公司的员工图都是采用了树的结构,通过树结构查找会变得十分的快捷
2、关于树的基础概念
树是一种非线性的数据结构,它是由n (n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
●有一个特殊的节点,称为根节点,根节点没有前驱节点
●除根节点外,其余节点被分成M(M>0)个互不相交的集合T1、T2、.....Tm,其中每一个集合Ti(1 <= i<=m)又是一棵与树类似的子树。每棵子树的根节点有且只有一个前驱,可以有0个或多个后继
●树是递归定义的
①子树是不相交的
②除了根节点没有前驱节点,其余的每个节点都有且只有一个父节点
③假设树结构中边的个数为x,树节点的个数为n,他们之间的关系是x=n-1
④节点的"度":该节点包含的子树个数
树的"度":树中最大节点的度
⑤叶子节点:就是度为0的节点(比如上图中的M,J,K节点)
非叶子节点:就是度不为0的节点,该节点存在子树
⑥根节点:没有前驱节点的节点,一个数只有一个根节点(比如上图的D节点)
⑦节点的层次:从根节点开始计算,对于上图来说,D根节点为第一层,I,J,K节点为第二层,M节点就是第三层
⑧树的高度:当前树中节点层次最大的即为树的高度(上图树的高度就是3)
二、二叉树
1、二叉树概念
树中节点最大的度,为2的数就叫做二叉树,也就是数的度为2的树
①在二叉树中,一个节点最多有两颗子树,二叉树节点的度<=2
②二叉树的有左右之分,且子树的次序不能颠倒,因此二叉树是有序树
2、二叉树常见的性质
①在深度为K的二叉树中,最多存在2^k -1个节点
②在第K层中,该层最多有2^(k-1)个节点
③假设树结构中边的个数为x,树节点的个数为n,他们之间的关系是x=n-1,通过这个可以推出,度为2的节点(N2)和度为0的节点(N0)之间的关系:
N0=N2+1
3、满二叉树和完全二叉树
①满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k -1,则它就是满二叉树。
②完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。要注意的是满二叉树是一种特殊的完全二叉树。(从视觉上来看,完全二叉树就是满二叉树缺少了右下角)
4、二叉树的编号问题
若根节点从1开始编号:若任意一个节点x,既存在子树也存在父节点,,则
该节点的父节点为x/2
左子树的编号为2x
右子树的标号为2x+1
三、二叉树的遍历操作
对于二叉树来说,一共有四种遍历方式
①深度优先遍历(dfs):前序遍历,中序遍历,后序遍历
②广度优先遍历(bfs): 层序遍历
1、前序遍历
先访问根节点,然后递归访问左子树,在递归访问右子树(根左右,就是第一次访问该节点就打印,并且第一个节点一定是根节点)
ABDEC
2、中序遍历
先递归访问根的左子树,然后是根节点,最后是右子树(左根右,就是第二次访问到该节点时就打印,左子树节点都在根节点左侧,右子树节点在根节点右侧)
DBEAC
3、后序遍历
先递归访问左子树,在递归访问右子树,最后是根节点(左右根,就是第三次访问到该节点时就打印)
DEBCA
4、层序遍历
将二叉树层层访问
ABCDE
5、遍历练习题
前序遍历:ABDEGHCF
中序遍历:DBGHEACF
后序遍历:DHGEBFCA
层序遍历:ABCDEFGH
四、二叉树遍历实现
package binary_tree;
/**
* 二叉树的基本操作
*/
class TreeNode<E> {
//节点值
E val;
//左子树的根
TreeNode<E> left;
//右子树的根
TreeNode<E> right;
public TreeNode(E val) {
this.val = val;
}
}
/**
* 二叉树的建立
* @param <E>
*/
public class MYbinarytree<E> {
public TreeNode<Character> root;
//建立一个简单的二叉树
public void buildTree(){
TreeNode<Character> node=new TreeNode<>('A');
TreeNode<Character> node1=new TreeNode<>('B');
TreeNode<Character> node2=new TreeNode<>('C');
TreeNode<Character> node3=new TreeNode<>('D');
TreeNode<Character> node4=new TreeNode<>('E');
TreeNode<Character> node5=new TreeNode<>('F');
TreeNode<Character> node6=new TreeNode<>('G');
TreeNode<Character> node7=new TreeNode<>('H');
//连接二叉树
node.left=node1;
node.right=node2;
node1.left=node3;
node1.right=node4;
node4.left=node6;
node6.right=node7;
node2.right=node5;
root=node;
}
/**
* 只需要二叉树的根节点,就可以按照前序遍历的方法输出
* @param root
*/
public void PreorderTraversal(TreeNode root){
if (root==null){
return;
}
//根左右,先打印根节点
System.out.print(root.val+" ");
//左子树节点的输出交给子函数处理
PreorderTraversal(root.left);
//右子树节点的输出交给子函树处理
PreorderTraversal((root.right));
}
/**
* 只需要二叉树的根节点,就可以按照中序遍历的方法输出
* @param root
*/
public void InorderTraversal(TreeNode root){
if (root==null){
return;
}
//左根右,先递归输出左节点,左节点的输出交给子函数处理
InorderTraversal(root.left);
//根节点的输出
System.out.print(root.val+" ");
//递归输出右节点,右节点的输出交给子函数处理
InorderTraversal(root.right);
}
/**
* 只需要二叉树的根节点,就可以按照后序遍历的方法输出
* @param root
*/
public void PostordeTraversall(TreeNode root){
if (root==null){
return;
}
//左右根,先递归输出左节点,左节点的输出交给子函数处理
PostordeTraversall(root.left);
//递归输出右节点,右节点的输出交给子函数处理
PostordeTraversall(root.right);
//根节点的输出
System.out.print(root.val+" ");
}
}