目录
一、树形结构
1.树的概念

以上的这些都不是树。
①子树是不想交的
②除了根节点外,每个节点有且仅有一个父节点;一颗N个节点的树有N-1条边
2.有关树的一些概念

3.数的表示形式
class Node {
int value; // 树中存储的数据
Node firstChild; // 第一个孩子引用
Node nextBrother; // 下一个兄弟引用
}
如图所示:
4.树的应用
二、二叉树
1.二叉树的概念

由图可知:
①二叉树不存在度大于2的结点

2.两种特殊的二叉树

3.二叉树的性质
(4)具有n个结点的完全二叉树的深度k为log₂(n+1)上取整
推导如下:
所以2^K-1 = n------------------>k=log₂(n+1)
我们最好记住一些常见的数:2^10=1024 2^9=512 2^8=256 2^7=128
(5)对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:
- 若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
- 若2i+1<n,左孩子序号:2i+1,否则无左孩子
- 若2i+2<n,右孩子序号:2i+2,否则无右孩子
我们来看一个题:
在具有 2n 个结点的完全二叉树中,叶子结点个数为( )


4.二叉树的存储
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}
②孩子双亲表示法:
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
Node parent; // 当前节点的根节点
}
5.二叉树的遍历

前序遍历结果:1 2 3 4 5 6
中序遍历结果:3 2 1 5 4 6


代码实现:
对于此树,我们先用穷举法建立:
public BTNode createTree(){
BTNode A = new BTNode('A');
BTNode B = new BTNode('B');
BTNode C = new BTNode('C');
BTNode D = new BTNode('D');
BTNode E = new BTNode('E');
BTNode F = new BTNode('F');
BTNode G = new BTNode('G');
BTNode H = new BTNode('H');
A.left = B;
A.right = C;
B.left = D;
B.right = E;
C.left = F;
C.right = G;
E.right = H;
return A;
}
前序遍历:
void preOrder(BTNode root){
if(root == null){
return;
}
System.out.print(root.val+" ");
preOrder(root.left);
preOrder(root.right);
}

如果是带返回值的前序遍历呢?
法一:遍历思路:
public List<Integer> preOrder(BTNode root){
List<Integer> retlist = new ArrayList<>();
if(root == null){
return retlist;
}
retlist.add(root.val);
preOrder(root.left);
preOrder(root.right);
return retlist;
}
法二:子问题思路:
public List<Integer> preorder(BTNode root) {
List<Integer> retlist = new ArrayList<>();
if(root == null){
return retlist;
}
retlist.add(root.val);
List<Integer> leftTree = preorder(root.left);
retlist.addAll(leftTree);
List<Integer> rightTree = preorder(root.right);
retlist.addAll(rightTree);
return retlist;
}
中序遍历:
void inOrder(BTNode root){
if(root == null){
return;
}
inOrder(root.left);
System.out.print(root.val+" ");
inOrder(root.right);
}
或者:(子问题思路)
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> retlist = new ArrayList<>();
if(root == null){
return retlist;
}
List<Integer> leftTree = inorderTraversal(root.left);
retlist.addAll(leftTree);
retlist.add(root.val);
List<Integer> rightTree = inorderTraversal(root.right);
retlist.addAll(rightTree);
return retlist;
}
后序遍历:
void postOrder(BTNode root){
if(root == null){
return;
}
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val+" ");
}
或者:(子问题思路:)
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> retlist = new ArrayList<>();
if(root == null){
return retlist;
}
List<Integer> leftTree = postorderTraversal(root.left);
retlist.addAll(leftTree);
List<Integer> rightTree = postorderTraversal(root.right);
retlist.addAll(rightTree);
retlist.add(root.val);
return retlist;
}

3.现在我们来看一个有关遍历的小题:
二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG,则二叉树后序遍历序列为______
所以:HIFKJGE
6.二叉树的基本操作
(1)获取树中节点的个数
//遍历思路:
int count = 0;
int size(BTNode root){
if(root == null){
return 0;
}
count++;
size(root.left);
size(root.right);
return count;
}
//子问题思路 root.left+root.right+1
int size2(BTNode root){
if(root == null) {
return 0;
}
return size2(root.left)+size2(root.right)+1;
}
(2)获取叶子节点的个数
//遍历思路:遇到叶子节点,就让计数器++ 叶子结点:root.left==null&&root.right==null
int leafCount = 0;
int getLeafNodeCount(BTNode root){
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
leafCount++;
}
getLeafNodeCount(root.left);
getLeafNodeCount(root.right);
return leafCount;
}
//子问题思路:左树的叶子+右树的叶子=整棵树的叶子
int getLeafNodeCount2(BTNode root){
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
return getLeafNodeCount2(root.left) + getLeafNodeCount2(root.right);
}
(3)获取第K层节点的个数
//第k层节点的个数=第k-1层的左子树节点的个数+第k-1层右子树节点的个数,当k==1时,递归结束
int getKLevelNodeCount(BTNode root, int k){
if(root == null || k <= 0){
return 0;
}
if(k == 1){
return 1;
}
return getKLevelNodeCount(root.left,k-1) + getKLevelNodeCount(root.right,k-1);
}
(4)获取二叉树的高度
//左树的高度和右树的高度取最大值,最大值+1即为所求
int getHeight(BTNode root){
if(root == null){
return 0;
}
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
设有n个节点,该代码的时间复杂度为O(n)(每个结点都会被递归一遍),空间复杂度为O(log₂n)(会开辟树的高度个栈,因为它会先执行左边,再执行右边,当左边执行完后,为左边开辟的空间会释放,所以不管是左边高还是右边高,都会只开辟树的高度个栈)
(5)检测值为value的元素是否存在
//遍历二叉树当中的节点,查看某一个节点的值是不是我要找的数据;先判断根节点的值是不是我们的val,不是的话先去左边找,还是找不到的话就去右边找,找到就返回
BTNode find(BTNode root, char val){
if(root == null){
return null;
}
if(root.val == val){
return root;
}
BTNode retLeft = find(root.left,val);
if(retLeft != null){
return retLeft;
}
BTNode retRight = find(root.right,val);
if (retRight != null) {
return retRight;
}
return null;
}
可以在主函数中增加一个捕捉异常的代码段!
(6)判断一棵树是不是完全二叉树
①需要用到队列这种数据结构
②当root不为空的时候,把一个元素入队,然后出队,定义一个临时节点cur接收出队的元素,若cur==null,检查队列里面是否全部都为null,如果是则为完全二叉树,否则不是完全二叉树;若cur != null,将cur的左孩子和右孩子都放进队列里面
boolean isCompleteTree(BTNode root){
if(root == null){
return true;
}
Queue<BTNode> queue = new LinkedList<>();
queue.offer(root);
while(! queue.isEmpty()){
BTNode cur = queue.poll();
if(cur != null){
queue.offer(cur.left);
queue.offer(cur.right);
}else{
break;
}
}
while(!queue.isEmpty()){
BTNode top = queue.peek();
if(top != null){
return false;
}
queue.poll();
}
return true;
}