二叉树的销毁
二叉树的销毁不能直接把每一个节点销毁掉,因为如果把父节点销毁掉整棵子树就会弄丢造成内存泄漏

如果我们把1这个节点直接删除掉我们找不到1的左右子树了但是剩下的几点依然会占用堆区,所以我们应该从叶子节点开始删除,所以这里我们使用后序遍历 来遍历每一个左子树右子树根然后进行销毁
//销毁
void TreeDestroy(HPNode* root)
{
if (root == NULL)
return;
TreeDestroy(root->left);
TreeDestroy(root->right);
free(root);
}
层序遍历
除了前序遍历中序遍历和后序遍历以外,我们还可以对二叉树进行层序遍历,既然要对二叉树进行层序遍历,那我们就需要使用到队列来进行,队列的特点是先进先出


我们拿这个二叉树来举例,我们把1放进队列里面然后再把1从队列里面拿出来,再把2和4放进队列里面再把2从队列里面拿出来,再把3放进4后面然后把4取出来,再把5和6放在3的后面以此类推
void TreeLevelOrder(HPNode* root)
{
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
HPNode* front = QueueFront(&q);
QueuePop(&q);
printf("%d ", front->data);
if (front->left)
QueuePush(&q, front->left);
if (front->right)
QueuePush(&q, front->right);
}
QueueDestroy(&q);
}
int main()
{
HPNode* root = CreatBinarTree();
TreeLevelOrder(root);
printf("\n");
TreeDestroy(root);
root = NULL;
return 0;
}
这里需要把实现队列的代码放在另一个文件中在包含一下头文件

判断是否为完全二叉树
那我们怎么去判断是否为完全二叉树呢?有人可能会想用递归去求出二叉树的结点个数然后去比较是否满足完全二叉树的条件,虽然这个想法面对满二叉树可行但是满二叉树如果求出了高度则节点数是固定的,但对于完全二叉树而言是不可行的,因为完全二叉树有一个要求是最后一层不需要满节点但是一定要保证是连续的,那既然是连续的那我们为什么不可以根据一层一层的去判断呢?如果某一层的节点不连续就能说明不是完全二叉树就可以利用到我们刚才所学到的层序遍历,在层序遍历的过程中如果遇到了空节点就直接跳过,非空节点我们才会放进队列中


左边
HPNode* CreatBinarTree()
{
HPNode* node1 = BuyNode(1);
HPNode* node2 = BuyNode(2);
HPNode* node3 = BuyNode(3);
HPNode* node4 = BuyNode(4);
HPNode* node5 = BuyNode(5);
HPNode* node6 = BuyNode(6);
//HPNode* node7 = BuyNode(7);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node2->right = node6;
node4->left = node5;
//node3->right = node7;
return node1;
}
bool TreeComplete(HPNode* root)
{
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
HPNode* front = QueueFront(&q);
QueuePop(&q);
//遇到第一个NULL就可以判断了,如果对列中还有非空,就不是完全二叉树
if (front == NULL)
break;
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
while (!QueueEmpty(&q))
{
HPNode* front = QueueFront(&q);
QueuePop(&q);
if (front)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
return true;
}
int main()
{
HPNode* root = CreatBinarTree();
if (TreeComplete(root))
{
printf("是完全二叉树");
}
else
{
printf("不是完全二叉树");
}
return 0;
}

右边
HPNode* CreatBinarTree()
{
HPNode* node1 = BuyNode(1);
HPNode* node2 = BuyNode(2);
HPNode* node3 = BuyNode(3);
HPNode* node4 = BuyNode(4);
HPNode* node5 = BuyNode(5);
HPNode* node6 = BuyNode(6);
HPNode* node7 = BuyNode(7);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node2->right = node6;
node4->left = node5;
node3->right = node7;
return node1;
}
bool TreeComplete(HPNode* root)
{
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
HPNode* front = QueueFront(&q);
QueuePop(&q);
//遇到第一个NULL就可以判断了,如果对列中还有非空,就不是完全二叉树
if (front == NULL)
break;
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
while (!QueueEmpty(&q))
{
HPNode* front = QueueFront(&q);
QueuePop(&q);
if (front)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
return true;
}
int main()
{
HPNode* root = CreatBinarTree();
if (TreeComplete(root))
{
printf("是完全二叉树");
}
else
{
printf("不是完全二叉树");
}
return 0;
}

二叉树练习题
单值二叉树

首先判断根结点是否为空,为空则返回true如果不为空则返回两个孩子值进行比较,但是如果没有孩子则无需比较,如果判断有不同的情况则直接返回false,当根节点与孩子相同时则进行递归往下判断
bool isUnivalTree(struct TreeNode* root)
{
if(root==NULL)
return true;
if(root->left&&root->left->val!=root->val)
return false;
if(root->right&&root->right->val!=root->val)
return false;
return isUnivalTree (root->left )&& isUnivalTree(root->right);
}
相同的树

首先我们想一下怎么去证明两个树完全一样,如果是递归的思想是不是先判断两个树根节点是否相同如果不相同就直接返回false,但根节点相同的话我们就需要开始递归判断其左子树是否相同再判断右子树是否相同对于根的左子树而言又可以分为根和左子树右子树,当递归结束也就是两个树同时到空节点时则返回true,但如果某次递归一个树有值但另一个树对应位置是空则说明两棵树不相同返回false
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if(p==NULL&&q==NULL)
return true;
if(p==NULL||q==NULL)
return false;
if(p->val!=q->val)
return false;
return isSameTree(p->left,q->left)&& isSameTree(p->right,q->right);
}
对称二叉树

首先我们要证明根节点的左右孩子相同如果不相同则直接返回false相同则开始递归然后要同时证明第二层第一个节点的左孩子和第二个节点的右孩子是否相同以及第一个节点的右孩子和第二个节点的左孩子是否相同如果有一种情况不同就直接返回false,这样我们就需要两个变量同时进行所以我们还需要自己创建一个函数当某次递归如果其中一边不为空而另一边为空则说明树不对称也返回false,创建的函数将根节点的左子树和右子树带入也就相当于变成两个树的判断
bool _isSymmetric(struct TreeNode* p, struct TreeNode* q)
{
if(p == NULL && q == NULL)
{
return true;
}
if(p == NULL || q == NULL)
{
return false;
}
if(p->val != q->val)
{
return false;
}
return _isSymmetric(p->left, q->right) && _isSymmetric(p->right, q->left);
}
bool isSymmetric(struct TreeNode* root)
{
return _isSymmetric(root->left, root->right);
}
另一颗树的子树


这道题和第二题类似当我们进行递归时如果某次root的值与subroot相同时则开始进行比较两者是否相同,如果判断两者不同时不能直接返回false,原因是可能后面的递归会出现相同的情况只是一次判断不能说明没有相同的树只有当递归到空节点也就是到底时如果root的值与subroot的值还是不同则说明返回false
bool _isSubtree(struct TreeNode* p, struct TreeNode* q)
{
if(p == NULL && q == NULL)
{
return true;
}
if(p == NULL || q == NULL)
{
return false;
}
if(p->val != q->val)
{
return false;
}
return _isSubtree(p->left, q->left) && _isSubtree(p->right, q->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
if(root == NULL)
{
return false;
}
if(root->val == subRoot->val)
{
if(_isSubtree(root, subRoot))
{
return true;
}
}
return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}