文章目录
1.LeetCode:1367. 二叉树中的列表
[原题链接](https://leetcode.cn/problems/linked-list-in-binary-tree/)
给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表。
如果在二叉树中,存在一条一直向下的路径,且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,那么请你返回 True ,否则返回 False 。
一直向下的路径的意思是:从树中某个节点开始,一直连续向下的路径。
示例 1:
输入:
head = [4,2,8],
root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
输出:true
示例 2
输入
head = [1,4,2,6],
root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
输出:true
示例 3:
输入:
head = [1,4,2,6,8],
root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
输出:false
提示:
二叉树和链表中的每个节点的值都满足 1 <= node.val <= 100 。
链表包含的节点数目在 1 到 100 之间。
二叉树包含的节点数目在 1 到 2500 之间。
这道题就是在一个二叉树中找到一个子树,先序遍历并加以判断即可。
class Solution {
bool check(ListNode* head,TreeNode* root){
if(root==nullptr){
return head==nullptr;
}
if(head==nullptr){
return true;
}
if(root->val!=head->val){
return false;
}
return check(head->next,root->left)||check(head->next,root->right);
}
public:
bool isSubPath(ListNode* head, TreeNode* root) {
if(check(head,root)){
return true;
}
if(root==nullptr){
return false;
}
return isSubPath(head,root->left)||isSubPath(head,root->right);
}
};
2.LeetCode:1361. 验证二叉树
二叉树上有 n 个节点,
按从 0 到 n - 1 编号,其中节点 i 的两个子节点分别是 leftChild[i] 和 rightChild[i]。
只有 所有 节点能够形成且 只 形成 一颗 有效的二叉树时,返回 true;否则返回 false。
如果节点 i 没有左子节点,那么 leftChild[i] 就等于 -1。右子节点也符合该规则。
注意:节点没有值,本问题中仅仅使用节点编号。
示例 1:
输入:n = 4, leftChild = [1,-1,3,-1], rightChild = [2,-1,-1,-1]
输出:true
示例 2:
输入:n = 4, leftChild = [1,-1,3,-1], rightChild = [2,3,-1,-1]
输出:false
示例 3:
输入:n = 2, leftChild = [1,0], rightChild = [-1,-1]
输出:false
示例 4:
输入:n = 6, leftChild = [1,-1,-1,4,-1,-1], rightChild = [2,-1,-1,5,-1,-1]
输出:false
提示:
1 <= n <= 10^4
leftChild.length == rightChild.length == n
-1 <= leftChild[i], rightChild[i] <= n - 1
既然要验证是否是二叉树,我们就从最基本的概念出发。学过离散数学的话我们会知道树的概念是从图衍生出来的,而二叉树又是特殊的树。
那么这里就可以利用二叉树成立的条件来解决本题,首先二叉树中有且仅有一个入度为0的结点他是根节点,其余各个结点的入度都为1,且二叉树中所有结点的出度均为0或1或2。本题根据给出的根节点和左右子树的关系统计入度出度加以判断,最后再确认连通性(避免有环)即可。
class Solution {
bool vis[10005];
int deg[10005];
void dfs(vector<int>& leftChild,vector<int> &rightChild,int root){
vis[root]=true;
if(leftChild[root]!=-1){
dfs(leftChild,rightChild,leftChild[root]);
}
if(rightChild[root]!=-1){
dfs(leftChild,rightChild,rightChild[root]);
}
}
public:
bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
memset(vis,false,sizeof(vis));
memset(deg,0,sizeof(deg));
for(int i=0;i<n;++i){
if(leftChild[i]!=-1){
++deg[leftChild[i]];
}
if(rightChild[i]!=-1){
++deg[rightChild[i]];
}
}
int root=-1;
bool flag=false;
for(int i=0;i<n;++i){
if(deg[i]==0){
if(n==0){
return true;
}
if(flag){
return false;
}
root=i;
flag=true;
}
if(deg[i]>1){
return false;
}
}
if(!flag){
return false;
}
dfs(leftChild,rightChild,root);
for(int i=0;i<n;++i){
if(!vis[i]){
return false;
}
}
return true;
}
};
3.LeetCode:1457. 二叉树中的伪回文路径
给你一棵二叉树,每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的,当它满足:路径经过的所有节点值的排列中,存在一个回文序列。
请你返回从根到叶子节点的所有路径中 伪回文 路径的数目。
示例 1:
输入:root = [2,3,1,3,1,null,1]
输出:2
示例 2:
输入:root = [2,1,1,1,3,null,null,null,null,null,1]
输出:1
示例 3:
输入:root = [9]
输出:1
提示:
给定二叉树的节点数目在范围 [1, 1e5] 内
1 <= Node.val <= 9
非常简单的一道题目,简单在Node.val为[1,9]如果这个范围很大本题就麻烦的多了。
回到本题来,一条路径构成回文串的条件是该路径的数字可以随意组成一个回文串,也就是说该路径上的数字只能出现一次奇数个情况。而结点的值从1到9,这就和位运算联系到一起了。我们定义一个数字,他的二进制位存储1到9每个数字出现频率的信息,如果是偶数次该位为0否则为1。
那么如何做到呢?很简单,利用异或(x ^ x=0, 0 ^ x= x)。如果所有数字均出现偶数次,那么我们定义的这个数字为0,无须赘述。但是如果有一个数字出现了奇数次,那么最后异或的结果就是这个数字对应的二进制位上为1其他为0。也就是说此时数字的二进制位中仅有这一个1,这个时候的处理就需要一点位运算的基础了,我们知道消除掉到某个数字最后一个1的信息是利用x&(x-1)。而当某个数字出现了奇数次的时候,我们存储信息的数字的二进制位刚好只有一个1,所以一条路径是回文串的条件就变为了n==0||n&(n-1)==0.
bool isleaf(TreeNode* root){
return root->left==nullptr&&root->right==nullptr;
}
class Solution {
int ans;
void dfs(TreeNode* root,int & sum){
int tmp=sum^(1<<root->val);
if(isleaf(root)){
if(tmp==0||(tmp&(tmp-1))==0){
ans++;
}
return;
}
if(root->left){
dfs(root->left,tmp);
}
if(root->right){
dfs(root->right,tmp);
}
}
public:
int pseudoPalindromicPaths (TreeNode* root) {
ans=0;
if(!root){
return 0;
}
int sum=0;
dfs(root,sum);
return ans;
}
};
4.LeetCode:1373. 二叉搜索子树的最大键值和
给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。
二叉搜索树的定义如下:
任意节点的左子树中的键值都 小于 此节点的键值。
任意节点的右子树中的键值都 大于 此节点的键值。
任意节点的左子树和右子树都是二叉搜索树。
示例 1
输入:root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6]
输出:20。
示例 2:
输入:root = [4,3,null,1,2]
输出:2
示例 3:
输入:root = [-4,-2,-5]
输出:0
示例 4:
输入:root = [2,1,3]
输出:6
示例 5:
输入:root = [5,4,8,3,null,6,3]
输出:7
提示:
每棵树有 1 到 40000 个节点。
每个节点的键值在 [-4 * 10^4 , 4 * 10^4] 之间。
很不错的一道树形dp的题目,递归函数有五个参数,根节点root,以该结点为根的子树最大值maxv,最小值minv(用来返回给上一级判断上一级是否为二叉搜索树),最大键值和sum,是否是二叉搜索树is。这里向左右子树要的信息有左右子树的最大最小值,和他们最大键值和以及是否是二叉搜索树。
树形dp一向是思路确定了代码写着就很简单了,本题也是如此。首先确定递归结束条件,当前节点为空时maxv设置为最小,minv设置为最大,is设置为1,sum设置为0并返回。
之后先向左右子树获取到他们的信息之后,更新sum的条件是左右子树均为二叉搜索树,并且root->val大于左子树最大值小于右子树最小值。否则就说明当前的子树并不是二叉搜索树,设置非法状态。而当前树的最大最小值为根节点的值和左右子树最大最小值比较的结果,这个就需要一直更新了。
class Solution {
int ans;
public:
void dfs(TreeNode* root,int& maxv,int& minv,int &sum,int&is){
if(root==nullptr){
maxv=-2147483648;
minv=2147483647;
sum=0;
is=1;
return;
}
int lmax,lmin,lsum,lis;
int rmax,rmin,rsum,ris;
dfs(root->left,lmax,lmin,lsum,lis);
dfs(root->right,rmax,rmin,rsum,ris);
sum=root->val;
if(lmax<root->val&&root->val<rmin&&lis&&ris){
is=1;
sum=root->val+lsum+rsum;
}else {
is=0;
sum=0;
}
maxv=max(lmax,rmax);
maxv=max(maxv,root->val);
minv=min(lmin,rmin);
minv=min(minv,root->val);
ans=max(ans,sum);
}
int maxSumBST(TreeNode* root) {
ans=0;
int maxv,minv,sum,is;
dfs(root,maxv,minv,sum,is);
return ans;
}
};