一、k层节点的计算
k层问题相当于是求树叶子节点的计算的另一种变形,只不过多了一个限制条件,所以我们可以很轻松的写出如下代码:
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if (root == NULL)//这不可加k==1的情况,因为前面有可能直接为空,会造成对空指针的解引用。
{
return 0;
}
if (k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}
注意!给第一个if条件不能加k==1,因为可能会出现下面的情况
当左子树还没有到k==1时直接跳出循环,如果加上可能会导致对空指针的引用。
二、找节点
递归说到底,其实只需要考虑两个问题,一个是递归结束条件,另一个是思路,有了这两个就可以写出基本的递归形式。例如:
找节点结束有两种情况:
1、节点找到空也找不到。2、找到返回值。
思路:
先找左树,在找右树。
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
//结束条件
if (root == NULL)
{
return NULL;
}
if (root->_data == x)
{
return root;
}
//开始从左树找
BTNode* x1 = BinaryTreeFind(root->_left, x);
if (x1)
{
return x1;//找到了返回
}
BTNode* x2 = BinaryTreeFind(root->_right, x);//左树找完找右树
if (x2)
{
return x2;
}
//到这说明树找完了,找不到
return NULL;
}
注意不可写出如下形式:
//结束条件
if (root == NULL)
{
return NULL;
}
if (root->_data == x)
{
return root;
}
BinaryTreeFind(root->_left, x);
BinaryTreeFind(root->_right, x);
递归不可能一次性将函数值直接返回给main函数,他只能返回给其上一层递归函数,所以我们要保存返回数据判断。
其次:
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
//结束条件
if (root == NULL)
{
return NULL;
}
if (root->_data == x)
{
return root;
}
//BinaryTreeFind(root->_left, x);
//BinaryTreeFind(root->_right, x);
//开始从左树找
BTNode* x1 = BinaryTreeFind(root->_left, x);
BTNode* x2 = BinaryTreeFind(root->_right, x);//左树找完找右树
if (x1)
{
return x1;//找到了返回
}
//BTNode* x2 = BinaryTreeFind(root->_right, x);//左树找完找右树
if (x2)
{
return x2;
}
//到这说明树找完了,找不到
return NULL;
}
这种写法虽然对,但是可能会导致无用的计算,如果左树找到直接返回即可,没必要到右树去找。(函数只有碰见返回值或者运行完函数才会停止,即这一层递归才能结束)。
递归展开图如上。
三、通过遍历创建树
通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
后面的oj题会用到这个这里提前讲一下:
结束条件:
遇到#说明该节点为空,不向下继续插入。
思路:
先根,再建立左树,再建立右树。
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
arr[*pi]=='#'||arr[*pi]=='\0'
{
(*pi)++;
return NULL;
}
BTNode* root = (BTNode*) malloc(sizeof(BTNode));//先根
root->_data = a[(*pi)++];//读完一个pi要往后走
root->_left = BinaryTreeCreate(a, pi);//再左
root->_right = BinaryTreeCreate(a, pi);//再右
return root;//完事返回根。
}
递归展开图
四、相同树问题
常见错误:
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
if(p==NULL||q==NULL)
{
return false;
}
if(p->val==q->val)
{
return true;
}
return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}
条件判断一定要写递归终止时的情况,而不能写继续的情况。
结束条件:
两个数遍历完,两个值不相同,一个为空一个不为空。
思路:先检查左树再检查右树:
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
if(q==NULL&&p==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);
}
五、对称二叉树
实际上就是相同树的另一种形式:
bool compare(struct TreeNode *left,struct TreeNode *right)
{
if(left==NULL && right==NULL)
return true;
if(left==NULL || right ==NULL)
return false;
if(left->val != right->val)
return false;
return compare(left->left,right->right) && compare(left->right,right->left);
}
bool isSymmetric(struct TreeNode* root)
{
return compare(root->left,root->right);
}
六、另一颗树的子树
结束条件
当被比树为空时(递归到为空时说明这条路行不通,返回假),则一定不是
当是子树时返回真,不是则继续递归,
思路:分为左树和右树:
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
if(q==NULL&&p==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);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
if(root==NULL)
{
return false;
}
if(isSameTree(root,subRoot))
{
return true;
}
return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}
七、二叉树的构建及遍历。
前面我们已经打好了基础这直接写:
#include <stdio.h>
#include<stdio.h>
typedef struct TreeNode
{
char val;
struct TreeNode* left;
struct TreeNode* right;
}TreeNode;
TreeNode* maketree(char*arr,int*count)
{
if(arr[*count]=='#'||arr[*count]=='\0')
{
return NULL;
}
TreeNode* newnode = (TreeNode*)malloc(sizeof(TreeNode));
newnode->val = arr[(*count)++];
newnode->left = maketree(arr,count);
(*count)++;
newnode->right = maketree(arr,count);
return newnode;
}
void Inorder(TreeNode* root)
{
if(root==NULL)
{
return;
}
Inorder(root->left);
printf("%c ",root->val);
Inorder(root->right);
}
int main()
{
char arr[101];
scanf("%s",arr);
int count = 0;
TreeNode* tree = maketree(arr,&count);
Inorder(tree);
return 0;
}
关于这里为什么下标传指针接下来一道题给大家解释
八、前序遍历
这里比较特殊的是他要将值放入在一个数组中,所有我们很容易有下面的思路
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int BinaryTreeSize(struct TreeNode * root)
{
if (root == NULL)
{
return 0;
}
return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
void preorder(struct TreeNode* root, int* a,int i)
{
if(root==NULL)
{
return ;
}
a[i++]=root->val;
preorder(root->left,a,i);
preorder(root->right,a,i);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize=BinaryTreeSize(root);
int *a =(int *)malloc(sizeof(int)*(*returnSize));
int i=0;
preorder(root,a,i);
return a;
}
如果我们这么写就会报出这样的错误,因为i为局部变量,在递归给下一层时不会对上一层经行修改,所以会出现数据覆盖的错误;
所以我们得传i的地址,这样在递归回去时i的值就会发生变化。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int BinaryTreeSize(struct TreeNode * root)
{
if (root == NULL)
{
return 0;
}
return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
void preorder(struct TreeNode* root, int* a,int* i)
{
if(root==NULL)
{
return ;
}
a[(*i)++]=root->val;
preorder(root->left,a,i);
preorder(root->right,a,i);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize=BinaryTreeSize(root);
int *a =(int *)malloc(sizeof(int)*(*returnSize));
int i=0;
preorder(root,a,&i);
return a;
}