一:二叉树的四种遍历方式
(1):前序遍历
- 前序遍历简单的说,就是先遍历根,然后左子树,最后右子树
- 根->左子树->右子树
- 假设我们有这么一棵树

- 那么前序遍历会怎么样呢?
- A->B->-D->NULL->NULL->G->NULL->NULL->C->E->NULL->NULL->F->NULL->NULL
- 为什么呢?先是根A,然后左子树B,左子树B此时又是新的根,继续进行根->左子树->右子树,所以B之后为D,然后为D的左为NULL,NULL没有左右子树,到此为止
- 再遍历D的右子树为空,到此为止D作为B的左子树遍历完成,遍历B的右子树
- B的右子树的根为D,之后再遍历D的左子树,为NULL,遍历D的右子树为NULL,此时B的右子树也遍历完了,而B作为A的左子树,接下来就要遍历A的右子树C
- 如此反复,后续过程不再赘述。
- 代码也很简洁
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%c ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
(2):中序遍历
- 中序遍历为左子树->根->右子树
- 以前序遍历中的树为例,中序遍历的顺序如下
- NULL->D->NULL->B->NULL->G->NULL->A->NULL->E->NULL->C->NULL->F->NULL
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%c ", root->data);
InOrder(root->right);
}
(3):后序遍历
- 前序遍历为左子树->根->右子树
- 以前序遍历中的树为例,后序遍历的顺序如下
- NULL->NULL->D->NULL->NULL->G->B->NULL->NULL->E->NULL->NULL->F->C->A
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%c ", root->data);
}
(4):层序遍历(借助队列)
- 前面的前中后序遍历都为深度优先遍历,而层序遍历为广度优先遍历
- 它的遍历方式是一层一层遍历,一层遍历完才到下一层
- 我们可以借助队列实现
- 首先根不为空就先把根入队列
- 之后进入循环,直到队列为空
- 什么循环呢?就是先将队头数据出队列,然后如果队头的左右节点不为空,就将它们入队列
- 大致过程如下,这样我们就可以实现层序遍历了
- 最后别忘了销毁队列

void TreeLevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root != NULL)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
printf("%c ", front->data);
QueuePop(&q);
if (front->left)
{
QueuePush(&q, front->left);
}
if (front->right)
{
QueuePush(&q, front->right);
}
}
QueueDestroy(&q);
}
二:完全二叉树
- 如果理解了层序遍历的思想,判断树是否为完全二叉树就十分简单了
- 我们就按层序遍历二叉树,如果是完全二叉树,等出到第一个空以后,后面的所有节点都应该为空,如果后面的节点有非空节点,则不是完全二叉树
- 需要注意的是,层序遍历时,如果为空是不需要入队列的,但是判断是否为完全二叉树时,节点的值无论是否为空要入对列
- 因为当我们碰到第一个空时,说明二叉树所有节点都已经进入对列了,就要跳出循环
- 再判断剩余的节点是否有不为空的,如果有不为空的节点,则此树不是完全二叉树
bool TreeBinaryComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root != NULL)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
{
break;
}
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front)
{
return false;
}
}
QueueDestroy(&q);
return true;
}
三:单值二叉树
- 首先我们先判断根是否为空,如果根为空可认为是单值,返回true
- 然后再判断左子树的值是否和根的值相等,如果不相等,就返回false
- 为了防止左子树为空,判断条件还要&&root->left
- 同理判断右子树的值是否与根相同,不同返回false
- 这里为什么不是相同返回true呢?因为只是左子树和右子树与根节点的值相同并不能得出这个二叉树就是单值二叉树,我们还需要判断剩余节点的值是否是一致的
- 所以我们可以通过递归,再次判断左子树的左右子树是否与根相同,而单左子树所有的值与根节点的值相同仍不能得出树为单值二叉树,所以再判断右子树,当左右子树的值均与根的值相同时,此树就为单值二叉树。
- 而如果左子树中有不相同的值的话,已经能说明此树不是单值二叉树了,就不用判断右子树了
- 故最后我们可以写成这样
- return isUnivalTree(root->left) && isUnivalTree(root->right);
bool isUnivalTree(BTNode* root)
{
if (root == NULL)
{
return true;
}
if (root->left && root->left->data != root->data)
{
return false;
}
if (root->right && root->right->data != root->data)
{
return false;
}
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
四:二叉树最大深度
- 求二叉树的深度十分简单,我们可以这么想
- 一个树的深度等于它左树的深度和右树的深度中大的那一个+1,加一是因为根不为空也算一层
- 通过这个思想加上递归,求最大深度就很容易了
int TreeMaxDepth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int leftdepth = TreeMaxDepth(root->left);
int rightdepth = TreeMaxDepth(root->right);
return leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;
}
五: 平衡二叉树
- 平衡二叉树的定义为:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1
- 比如这个二叉树为平衡二叉树

- 但是这个二叉树就不是平衡二叉树

- 那么改如何判断一个二叉树是否是平衡二叉树呢?
- 假设这个树只有两层,我们可以把左右子树的高度给算出来,如果差小于2,那么此树为平衡二叉树,如果层数大于2呢?
- 我们可以判断这个树的左右子树是不是平衡二叉树,如果一个树的左右子树均为平衡二叉树并且左右子树高度差小于2,那么这个树就是平衡二叉树
- 我们固然可以采取前序遍历的方式,先算出左右子树的高度,再判断左右子树是否是平衡二叉树和左右子树的高度是否小于2
- 代码如下
bool isBalanced(BTNode*root)
{
if (root == NULL)
{
return true;
}
int leftDepth = TreeMaxDepth(root->left);
int rightDepth = TreeMaxDepth(root->right);
return abs(leftDepth - rightDepth) < 2
&& isBalanced(root->left)
&& isBalanced(root->right);
- 但这样太慢了,每次都要求高度,我们能不能用后序遍历呢,先求左右子树高度,到时候将左右子树高度大的那个+1就是根的高度了,就不用每次重复求了
- 修改后的代码如下
bool isBalanced(BTNode*root,int*ph)
{
if (root == NULL)
{
*ph = 0;
return true;
}
int leftDepth = 0;
if (isBalanced(root->left, &leftDepth) == false)
{
return false;
}
int rightDepth = 0;
if (isBalanced(root->right, &rightDepth) == false)
{
return false;
}
*ph = fmax(leftDepth, rightDepth) + 1;
return abs(leftDepth - rightDepth) < 2;
}
六:对称二叉树
- 什么样的二叉树是对称二叉树呢,其实就是沿中间的一条线翻过去能完全重合的就是对称二叉树
- 比如这个

- 最简单的方法应该是将二叉树翻转,然后判断翻转后的二叉树是否相等
- 但是这样要多写两个函数,我们能不能另辟蹊径呢?
- 我们可以这么想
- 首先判断根是否为空,如果为空,返回true
- 根不为空,传递根的左子树和右子树,去判断左子树和右子树是否相等(注意均为空也是相等,一个为空一个不为空不相等),不相等返回false(这是两层的情况),当层数大于2时
- 如果左右子树仍然相等,判断左子树的左节点和右子树的右节点是否相等,和左子树的右节点和右子树的左节点是否相等(进行递归),注意中间要用&&链接,光左子树是对称的还不能说明此树是对称的
- 因为刚开始只有根,所以我们单独拿一个函数判断根是否为空,不为空传递根的左右子树 进入另一个函数进行递归
- 总的思想仍然是前序遍历,先判断左右两个子树的根是否相等,相等才继续比较子树部分,不相等直接结束
bool _isSymmetric(BTNode* left, BTNode* right)
{
if (left == NULL && right == NULL)
{
return true;
}
if (left == NULL || right == NULL)
{
return false;
}
if (left->data != right->data)
{
return false;
}
return _isSymmetric(left->left, right->right)
&& _isSymmetric(left->right, right->left);
}
bool isSymmetric(BTNode* root)
{
if (root == NULL)
{
return true;
}
return _isSymmetric(root->left, root->right);
}