文章目录
一、树的基本概念
树是一种非线性结构(节点之间的关系是一对多),节点为0的树称为空树。
节点的度:一个节点含有的子树的个数。例如:下图D节点的度是2。
树的度:树中节点最大度称为数的度。例如:下图树的度是3。
叶子节点或终端节点:度为0的节点。例如:E,F,G,H,I,J都是叶子节点。
双亲节点或父节点:若一个节点含有子节点,则称这个节点为子节点的父节点。例如:B节点含有F,E.G,B节点为E,F,G的父节点。
孩子节点:是指某个节点的子树的根节点。例如:A的孩子节点是B,C,D。
根节点:一颗树中没有双亲的节点。例如:A为这棵树的根节点。
节点的层次:一棵树中,根为第一层,根的子节点为第二层,以此类推。
树的高度或深度:树中节点的最大层次。如下图树的高度为3。
非终端节点或分支节点:度不为0的节点。
兄弟节点:具有相同父节点的节点称为兄弟节点。
森林:由m(m>=0)棵树组成的集合称为森林。

二、二叉树(重点)
2.1二叉树的概念
二叉树是节点的度不大于2的的有序树。
二叉树的基本形态有5种:
1.空二叉树

2.只有一个根节点

3.根节点只有左子树

4.根节点只有右子树

5.根节点既有左子树,又有右子树。

2.2两种特殊的二叉树
1.满二叉树:一颗二叉树中每一层节点都达到最大值,则这棵树就是满二叉树。
2.完全二叉树:在满二叉树的基础上,去掉右下的一些连续叶子节点所形成的树。
满二叉树是特殊的完全二叉树。

2.3二叉树的性质(重要)

2.4二叉树的存储
二叉树的存储结构为:顺序结构和类似链表的链式存储(包括孩子表示法,双亲表示法,孩子双亲表示法)。
顺序存储结构适用于:完全二叉树和满二叉树,因为完全二叉树和满二叉树的节点符合自上而下、自左向右排列,因此采用顺序存储比较合适,这样可以最大程度的节省空间,又能利用数组的下标确定节点在二叉树的位置。但对于一般的二叉树采用顺序存储会浪费很多空间。
链式存储:由于顺序存储的空间利用率较低,所以二叉树一般采用链式存储。
三、二叉树的遍历
以下三种遍历基于我画的这棵二叉树。

3.1先序遍历的文字和图示遍历表示
先序遍历:先遍历根节点,再遍历左子树,最后遍历右子树。每颗树都要满足根左右。
(可以不看较为啰嗦,下面有代码和图示实现遍历)
先走到A是根节点,打印A
再遍历A的左子树,遇到B节点, B是子树根节点,打印B
再遍历B的左子树,遇到D节点,D是子树的根节点,打印D
再遍历D的左子树,为空,返回到D节点
再遍历D的右子树,为空,返回到D节点
D节点返回到B节点,遍历B的右子树,E为右子树的根节点,打印E节点。
遍历E节点的左子树,左子树为空,返回到E节点。
遍历E节点的右子树,子树的根节点为H,打印H
遍历H节点的左子树为空,返回到H节点
遍历H节点的右子树为空,返回到H节点
H节点左右子树遍历完,返回到E节点。
E节点左右子树遍历完,返回到B节点。
B节点左右子树遍历完,返回到A 节点,继续遍历A的右子树。
C是子树的根节点,打印C节点。
遍历C节点的左子树,F是子树的根节点,打印F节点
遍历F节点的左子树为空,返回到F节点
遍历F节点的右子树为空,返回到F节点
F节点左右子树遍历完,返回到C节点,遍历C节点的右子树
右子树的根节点的是G,打印G节点
遍历G节点的左子树为空,返回到G节点
遍历G节点的右子树为空,返回到G节点
G节点左右子树遍历完,返回到C节点。
C节点的左右子树遍历完,C节点返回到A节点。
以上就是前序遍历的递归过程。
所以先序遍历的顺序是:ABDEHCFG。

3.1.1先序遍历的代码递归实现
BinaryTree类
java
public class BinaryTree {
static class TreeNode{
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val) {
this.val = val;
}
}
public TreeNode createBinaryTree(){
TreeNode A = new TreeNode('A');
TreeNode B = new TreeNode('B');
TreeNode C = new TreeNode('C');
TreeNode D = new TreeNode('D');
TreeNode H = new TreeNode('H');
TreeNode E = new TreeNode('E');
TreeNode F = new TreeNode('F');
TreeNode G = new TreeNode('G');
A.left = B;
B.left = D;
B.right = E;
E.right = H;
A.right = C;
C.left = F;
C.right = G;
return A;//返回根节点
}
public void preOrder(TreeNode root){
if(root == null){
return;
}
//遍历根
System.out.print(root.val+" ");
//遍历左子树
preOrder(root.left);
preOrder(root.right);
}
}
Test测试类
java
public class Test {
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
BinaryTree.TreeNode root = binaryTree.createBinaryTree();
System.out.println("根节点:"+root.val);
binaryTree.preOrder(root);
}
}
打印结果为:

3.2中序遍历的文字和图示遍历表示
中序遍历:先遍历左子树,再遍历根节点,最后遍历右子树。每一棵树都要按照左根右遍历。
先从根节点进入,不打印根,遍历根的左子树,走到B节点,不打印B节点,遍历B这棵子树的左子树,走到D节点,遍历D的左子树为空返回到D节点,打印D节点,遍历D节点的右子树,为空返回到D节点,D节点遍历完返回到B节点,打印B节点,遍历B节点的右子树...接下来按照这个方式继续遍历即可得到这棵树的中序遍历。
所以中序遍历的顺序是:DBEHAFCG。

3.2.1中序遍历的代码递归实现
BinaryTree类
java
public class BinaryTree {
static class TreeNode{
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val) {
this.val = val;
}
}
public TreeNode createBinaryTree(){
TreeNode A = new TreeNode('A');
TreeNode B = new TreeNode('B');
TreeNode C = new TreeNode('C');
TreeNode D = new TreeNode('D');
TreeNode H = new TreeNode('H');
TreeNode E = new TreeNode('E');
TreeNode F = new TreeNode('F');
TreeNode G = new TreeNode('G');
A.left = B;
B.left = D;
B.right = E;
E.right = H;
A.right = C;
C.left = F;
C.right = G;
return A;//返回根节点
}
public void inOrder(TreeNode root){
if(root == null){
return;
}
//先遍历左子树
inOrder(root.left);
//遍历根
System.out.print(root.val+" ");
//遍历右子树
inOrder(root.right);
}
}
Test测试类
java
public class Test {
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
BinaryTree.TreeNode root = binaryTree.createBinaryTree();
System.out.println("根节点:"+root.val);
binaryTree.inOrder(root);
}
}
打印结果

3.3后序遍历的文字和图示遍历表示
后序遍历:先遍历左子树,再遍历右子树,最后遍历根节点。每一棵树都要按照左右根遍历。

所以后序遍历的顺序是:DHEBFGCA。
3.3.1后序遍历的代码递归实现
BinaryTree类
java
public class BinaryTree {
static class TreeNode{
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val) {
this.val = val;
}
}
public TreeNode createBinaryTree(){
TreeNode A = new TreeNode('A');
TreeNode B = new TreeNode('B');
TreeNode C = new TreeNode('C');
TreeNode D = new TreeNode('D');
TreeNode H = new TreeNode('H');
TreeNode E = new TreeNode('E');
TreeNode F = new TreeNode('F');
TreeNode G = new TreeNode('G');
A.left = B;
B.left = D;
B.right = E;
E.right = H;
A.right = C;
C.left = F;
C.right = G;
return A;//返回根节点
}
public void postOrder(TreeNode root){
if(root == null){
return;
}
//先遍历左子树
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val+" ");
}
}
Test测试类
java
public class Test {
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
BinaryTree.TreeNode root = binaryTree.createBinaryTree();
System.out.println("根节点:"+root.val);
binaryTree.postOrder(root);
}
}
打印结果

四、二叉树的基本操作(代码实现)
4.1获取树中的节点的个数
java
public int size(TreeNode root){
//总节点个数=左子树的节点个数+右子树的节点个数+1(根节点)
if(root == null){
return 0;
}
return size(root.left)+size(root.right)+1;
}
4.2获取树中叶子节点的个数
java
public int getLeafNodeCount(TreeNode root){
//叶子节点个数=左子树的叶子节点个数+右子树的叶子节点个数
if(root == null){
return 0;
}
if( root.left == null && root.right == null){
return 1;
}
return getLeafNodeCount(root.left)+getLeafNodeCount(root.right);
}
4.3获取树中第K层节点的个数
java
public int getKLevelNodeCount(TreeNode root,int k){
//获取第K层的节点个数,root左树的第k-1层+root右树的第k-1层,root也算一层
if(root == null){
return 0;
}
if(k == 1){
return 1;
}
return getKLevelNodeCount(root.left,k-1)+getKLevelNodeCount(root.right,k-1);
}
4.4获取二叉树的高度
java
public int getHeight(TreeNode root){
//左子树的高度和右子树的高度的最大值+1;
if(root == null){
return 0;
}
return Math.max(getHeight(root.left),getHeight(root.right))+1;
}
4.5检测val字符是否存在
java
public TreeNode find(TreeNode root,char val) {
if (root == null) {
return root;
}
if (root.val == val) {
return root;
}
BinaryTree.TreeNode left = find(root.left, val);
if (left != null) { //找到了
return left;
}
BinaryTree.TreeNode right = find(root.right, val);
if (right != null) {
return right;
}
return null; //没找到
}