每日鸡汤:
🚀就算失败,我也想知道,自己倒在距离终点多远的地方。
目录
🏆一、二叉搜索树的后序遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回
true
,否则返回false
。假设输入的数组的任意两个数字都互不相同。
这道题目有点意思,如果找不到思路很难判断数组是否为一个二叉树后序遍历的结果。我们知道在后序遍历得到的序列中,最后一个数必然是根,然后前面的元素被分成两部分:左子树节点的值和右子树节点的值。而左右子树内部也遵循着后序遍历的原则。以题目给的二叉树的后序遍历为例,
这段序列的根为5,5的左子树节点的值为1,3,2;5的右子树节点的值为6.这其中左子树节点的值都小于根节点的值,右子树节点的值都大于右节点的值。然后左右子树内部也是一样的最后一个元素为根,左子树都比根小,右子树都比根大。这是一个递归的过程,如果都满足这样的规则,很显然这个序列是一个二叉树的后序遍历。
🖊递归求解
1、先把数组最后一个元素存下来(根),然后遍历其余数,如果遇到有值大于根,就停下来,这代表左右子树的分界线。
2、因为分界线左边的数值必定小于根,所以只要判断分界线右边的数,也就是右子树部分如果有数值小于根的值,说明不是一个二叉树的后序遍历,返回false。
3、不断划分数组区间,递归。
bool verifyPostorder(int* postorder, int postorderSize)
{
//左子树都比根小
//右子树都比根大
if(postorder==NULL||postorderSize<1)
return true;
int root=postorder[postorderSize-1];
int i=0;
for(i=0;i<postorderSize-1;++i)
{
if(postorder[i]>root)
break;
}
for(int j=i;j<postorderSize-1;++j)
{
if(postorder[j]<root)
return false;
}
bool left=true;
if(i>0)
left=verifyPostorder(postorder,i);
bool right=true;
if(i<postorderSize-1)
right=verifyPostorder(postorder+i,postorderSize-1-i);
return (left&&right);
}
🏆二、二叉树中和为某一值的路径
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
困难解法:BFS广度优先,把二叉树的层序遍历拿下来放到一个数组,在这同时用一个数组记录所有叶子节点在层序遍历数组中的下标。然后通过叶子节点去找父节点,(parent=(child-1)/2)满足加和等于target的路径记录下来。(博主写过,非常麻烦,在leetcode上直接超时跑不过去)。
🖊DFS递归求解
我们从根出发,不断访问子节点,当访问到叶子节点的时候,我们不仅要计算这条路径节点的值的和还要记录这一路径的所有节点的值,这就要求我们开辟一个数组记录这条路径上所有的值,当加和满足等于target时,我们就把这条路径上的值放到二维数组里面。如果走到叶子节点不满足加和等于target呢,我们要去掉这个叶子节点,也就是指向数组最后一个元素的指针--,(这里类似于栈pop出栈顶)。判断另一个叶子节点,综合下来整体类似于前序遍历。
int**ret;
int*retColSize;
int retSize;
int* path;
int pathSize;
const int MAX_Node=5001;
//要返回的路径
void dfs(struct TreeNode* root,int target)
{
if(root==NULL)
return;
path[pathSize++]=root->val;
target-=root->val;
if(root->left==NULL&&root->right==NULL&&target==0)//为叶子节点并且target==0,就是我们要找的路径时
{
int* tmp=(int*)calloc(pathSize,sizeof(int));
memcpy(tmp,path,sizeof(int)*pathSize);//为什么不直接把path给到retSize呢?因为没必要,我们只需要拷贝前pathSize的数据是有效数据
ret[retSize]=tmp;
retColSize[retSize]=pathSize;
retSize++;
}
dfs(root->left,target);
dfs(root->right,target);
pathSize--;//返回上一层递归时需要pathSize--
}
int** pathSum(struct TreeNode* root, int target, int* returnSize, int** returnColumnSizes)
{
ret=(int**)calloc(MAX_Node,sizeof(int*));//要返回的二维数组
retColSize=(int*)calloc(MAX_Node,sizeof(int));//要求返回的二维数组每组多少个元素
path=(int*)calloc(MAX_Node,sizeof(int));
retSize=pathSize=0;
dfs(root,target);//深度优先递归
*returnSize=retSize;
*returnColumnSizes=retColSize;
return ret;
}
这里还有几处细节和一些小tips:
1、为什么设为全局变量?(新get到的小技能)
如果我们在main函数里面设立局部变量,不仅需要传参,而且最重要的是递归参数不能传值,需要传地址,这可能要涉及到二级甚至三级指针,非常麻烦,传大量的参,还需要考虑参数的变化,甚至我们需要设一些返回值去接受,/(ㄒoㄒ)/~~全局变量的好处在这里就体现了出来。
2、为什么用中间变量拷贝再赋给二维数组?
这三行代码为什么不写成:
ret[retSize]=path;
这多好,为何多此一举还要用tmp呢?
①第一种写法pathSize个数据是我们需要的有效数据,其余并不需要,我们拷贝到的都是有效数据。
②第二种写法的问题会报错:1、你拷贝进了除pathSize个以外的其他无效数据,没必要;
2、你无法确定path的空间是否小于等于二维数组中的一维数组的空间,强行给的话可能会导致放不下,溢出报错。