前序遍历
前序遍历,又叫先前遍历
遍历顺序: 根 左子树 右子树 注意"根"位置 根在前
// 前序遍历:根 左子树 右子树 注意"根"位置 根在前
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
先序遍历递归展开图
中序遍历
中序遍历
遍历顺序: 左子树 根 右子树 注意"根"位置 根在中间
// 中序遍历:左子树 根 右子树 注意"根"位置 根在中
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
中序遍历递归展开图
后序遍历
前序遍历,
遍历顺序: 左子树 右子树 根 注意"根"位置 根在后
// 后序遍历:左子树 右子树 根 注意"根"位置 根在最后
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
获取结点总个数
// 可以使用迭代思想
思路:如为空 结点个数 为0如 不为空 结点总个数 左子树结点个数+右子树结点个数+(自己)
int TreeSize(BTNode* root)
{
return root == NULL ? 0
: TreeSize(root->left)
+ TreeSize(root->right) + 1;
}
获取二叉树总叶子结点
如一个结点的 左子树结点为空 并且 右子树也为空 那这个结点就是叶子节点
拆分问题
1.空树 没有叶子结点
2.叶子结点 左子树为空并且右子树为空 叶子结点+1
3.分支结点 继续
int TreeeLeafSize(BTNode* root)
{
//空结点
if (root == NULL) // 没有叶子结点
return 0;
//叶子结点 左子树为空并且右子树为空 那这个结点就是叶子节点
if (root->left == NULL && root->right == NULL)///叶子结点
return 1;
//分支结点 迭代———— 叶子结点的总个数 = 左叶子结点个数+ 右叶子结点个数
return TreeeLeafSize(root->left) + TreeeLeafSize(root->right);
}
获取第k层结点个数
思路:当对于父结点的第k层 == 相当于两个孩子结点第k-1层结点之和
int TreeLeveSize(BTNode* root, int k)
{
if (root == NULL)//树为空直接返回
return 0;
if (k == 1)
return 1;
// 当对于父结点的第k层 == 相当于两个孩子结点第k-1层结点之和
return TreeLeveSize(root->left, k - 1)
+ TreeLeveSize(root->right, k - 1);
}
递归展开图
二叉树查找 值为x的结点
拆分问题
1.先判断根结点
2.再去左子树找
3.再去右子树找
BTNode* TreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)// 空树
return NULL;
if (root->val == x) //先判断根结点
return root;
BTNode* left =TreeFind(root->left, x); //左子树找
if (left)
return left; //不为空说明找到了
BTNode* right = TreeFind(root->right, x); //右子树找
if (right)
return right;//不为空说明找到了
return NULL; //返回NULL不一定找不到
}
单值二叉树
/拆分问题
1.空树
2.根 和左右子树判断---- 迭代 -- 走 更新左右子树
/拆分问题
// 1.空树
// 2.根 和左右子树判断 迭代 -- 走
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);
}
判断两颗树是否相同
拆分问题;必须满足 两个树在结构上相同,并且节点具有相同的值
1. 根 和 根比较
2. 左子树 和 左子树 比较
3. 右子树 和 右子树 比较
//拆分问题;必须满足 两个树在结构上相同,并且节点具有相同的值
//
//1. 根 和 根比较
//2. 左子树 和 左子树 比较
//3. 右子树 和 右子树 比较
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
// 两课树 一起走到 空 说明 这两颗树再结构上是相同的,并且结点具有相同的值
if(p == NULL && q == NULL)
return true;
// 一颗树为空 一颗树不为空 不满足(两个树在结构上相同)
if((p == NULL && q != NULL)||(p != NULL && q == NULL))
return false;
//不满足(节点具有相同的值)
if(p->val != q->val)
return false;
// //2. 左子树 和 左子树 比较 完 继续比较 右子树 和 右子树
return isSameTree(p->left,q->left) && isSameTree(p->right, q->right);
}
层序遍历
利用队列先进先出的思想
先进 根结点 出 根结点 带入(根结点的左右孩子不为空进队列) --迭代 走
void BinaryTreeLevelOrder(BTNode* root)
{
Queue p1;
QueueInit(&p1);
QueuePush(&p1, root);
while (!QueueEmpty(&p1))
{
BTNode* front = QueueFront(&p1);
printf("%c ", front->val);
QueuePop(&p1);//在这里删除的是队列的数据 没有破坏二叉树 ,所以还是可以找到 当前结点左右孩子的
//左孩子不为空进队列
if (front->Lchild)
QueuePush(&p1, front->Lchild);
//右孩子不为空进队列
if (front->Rchild)
QueuePush(&p1, front->Rchild);
}
QueueDestroy(&p1);
}
判断二叉树是否是完全二叉树
思路:
利用队列先进先出的特点
先进 根结点 出 根结点 带入根结点的左右孩子(根结点的左右孩子为空也进队列) --迭代 走
遇到空 判断队列剩余的数据是否为空 如果剩余的数据全部为空 是完全二叉树 反之 不是 完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
Queue p1;
QueueInit(&p1);
if (root != NULL)
QueuePush(&p1, root);
while (!QueueEmpty(&p1))
{
BTNode* front = QueueFront(&p1);
if (front == NULL)
break;
QueuePop(&p1);
QueuePush(&p1, front->Lchild);
QueuePush(&p1, front->Rchild);
}
while (!QueueEmpty(&p1))
{
BTNode* front = QueueFront(&p1);
if (front != NULL)
{
QueueDestroy(&p1);
return false;
}
QueuePop(&p1);
}
QueueDestroy(&p1);
return true;
}
判断对称二叉树
判断二叉树是否为对称二叉树 ,则判断它的左右子树是否镜像 。因为镜像是对称的,所以它的左右子树遍历规则是相反的
// 因为对称二叉树是 镜像的所以 左子树 和右子树 判断规则是相反的
bool isSameTree(struct TreeNode* left, struct TreeNode* right)
{
// 两课树 一起走到 空 说明 这两颗树再结构上是相同的,并且结点具有相同的值
if(left == NULL && right == NULL)
return true;
// 一颗树为空 一颗树不为空 不满足(两个树在结构上相同)
if((left == NULL && right != NULL)||(left != NULL && right == NULL))
return false;
if(left->val != right->val)
return false;
// 左子树:遍历顺序 根 左 右 右子树:遍历顺序 根 右 左 若两次遍历成功 是 对称二叉树
return isSameTree(left->left,right->right) && isSameTree(left->right, right->left);
}
bool isSymmetric(struct TreeNode* root)
{
if(root == NULL)
return true;
return isSameTree(root->left, root->right);
}
判断二叉树是否是另一棵树的子树
判断subRoot是否为 root的子树,如果 root中包含和subRoot相同的结构并且结点值相同,就说明subRoot是root的子树, root 和subRoot 必须不为空
如下图 subRoot 就是root的子树:
思路:依次判断root的每一个结点都需要与subRoot判断, 当root结点的值 和 subRoot相等 ,并且root子树的结构和 subRoot 的结构一样 subRoot是root的子树
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
// 两课树 一起走到 空 说明 这两颗树再结构上是相同的,并且结点具有相同的值
if(p == NULL && q == NULL)
return true;
// 一颗树为空 一颗树不为空 不满足(两个树在结构上相同)
if((p == NULL && q != NULL)||(p != NULL && q == NULL))
return false;
//不满足(节点具有相同的值)
if(p->val != q->val)
return false;
// //2. 左子树 和 左子树 比较 完 继续比较 右子树 和 右子树
return isSameTree(p->left,q->left) && isSameTree(p->right, q->right);
}
//root 中每个结点 都需要和 subRoot 判断
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
if(root == NULL)
return false;
if(root->val == subRoot->val)
{
if(isSameTree(root, subRoot)) // 相同返回true
{
return true;
}
}
return isSubtree(root->left, subRoot)|| isSubtree(root->right,subRoot);
}
二叉树的深度遍历
前序遍历
遍历规则:根 左子树 右子树 ,前序遍历 中序遍历 后序遍历它们除了遍历规则其余都一样
注意,接下来所要说的深度遍历与前面有所不同,前面说到的深度遍历是将一棵二叉树遍历,并将遍历结果打印屏幕上(较简单)。而下面说到的深度遍历是将一棵二叉树进行遍历,并将遍历结果存储到一个动态开辟的数组中,将数组作为函数返回值进行返回。
思路:
1.首先计算二叉树中结点的个数,便于确定动态开辟的数组的大小。
2.遍历二叉树,将遍历结果存储到数组中。
3.返回数组。
// 思路
//1. 遍历二叉树 获取总结点的个数 ,确定开辟空间的大小
//2.遍历二叉树 把二叉树所有结点的值 存放到数组
// 3. 返回数组
int TreeSize(struct TreeNode* root)
{
return root == NULL ? 0 :TreeSize(root->left) +TreeSize(root->right)+1;
}
2.遍历二叉树 把二叉树所有结点的值 存放到数组
void prev_order(struct TreeNode* root, int*a, int* pi)
{
if(root == NULL) // 树为空直接返回
return ;
a[(*pi)++] =root->val; // 先存放根结点的值 存放进数组
prev_order(root->left, a,pi); // 左子树的结点存放进数组
prev_order(root->right, a,pi); // 右子树的结点存放进数组
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
*returnSize = TreeSize(root); //返回 数组的大小
int* a = malloc(sizeof(int)* (*returnSize));
int i =0;
prev_order(root, a,&i); //把二叉树中所有结点存放进数组
return a;
}
中序遍历
遍历规则: 左子树 根 右子树
// 思路
//1. 遍历二叉树 获取总结点的个数 ,确定开辟空间的大小
//2.遍历二叉树 把二叉树所有结点的值 存放到数组
// 3. 返回数组
// //1. 遍历二叉树 获取总结点的个数 ,确定开辟空间的大小
int TreeSize(struct TreeNode* root)
{
return root == NULL ? 0 :TreeSize(root->left) +TreeSize(root->right)+1;
}
// //2.遍历二叉树 把二叉树所有结点的值 存放到数组
void In_order(struct TreeNode* root, int* a,int *pi)
{
if(root == NULL) //树为空直接返回
return ;
return ;
In_order(root->left, a,pi);//先存放左子树的结点存放进数组
a[(*pi)++] = root->val; //根的结点存放进数组
In_order(root->right, a,pi);//右子树的结点存放进数组
}
int* inorderTraversal(struct TreeNode* root, int* returnSize){
*returnSize = TreeSize(root);//返回 数组的大小
int*a = malloc(sizeof(int)*(*returnSize));
int i=0;
In_order(root,a,&i);// //把二叉树中所有结点存放进数组
return a;//3. 返回数组
}
后序遍历
遍历规则: 左子树 右子树 根
/ 思路
//1. 遍历二叉树 获取总结点的个数 ,确定开辟空间的大小
//2.遍历二叉树 把二叉树所有结点的值 存放到数组
// 3. 返回数组
//1. 遍历二叉树 获取总结点的个数 ,确定开辟空间的大小
int TreeSize(struct TreeNode* root)
{
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
//2.遍历二叉树 把二叉树所有结点的值 存放到数组
void post_order(struct TreeNode* root, int* a,int *pi)
{
if(root == NULL)
return ;
post_order(root->left, a,pi);//先存放左子树的结点存放进数组
post_order(root->right, a,pi);//再放右子树的结点存放进数组
a[(*pi)++] = root->val;//根的结点存放进数组
}
int* postorderTraversal(struct TreeNode* root, int* returnSize){
*returnSize = TreeSize(root);//返回 数组的大小
int*a = malloc(sizeof(int)*(*returnSize));
int i=0;
post_order(root,a,&i);//把二叉树中所有结点的值存放进数组
return a;// 3. 返回数组
}