给定一个二叉树,返回它的中序 遍历。(进阶: 递归算法很简单,你可以通过迭代算法完成吗?)
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,3,2]
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int>res;
if(root==nullptr)
return res;
stack<TreeNode*>s1;
while(root||!s1.empty()){
if(root){
s1.push(root);
root=root->left;
}
else{
TreeNode *tmp=s1.top();
s1.pop();
res.push_back(tmp->val);
root=tmp->right;
}
}
return res;}
};
//递归版本,用递归版本求二叉树的几种遍历只需要区别打印的位置
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int>res;
if(root==nullptr)
return res;
inorder(root,res);
return res;
}
void inorder(TreeNode *root,vector<int>&res){
if(root==nullptr)
return;
inorder(root->left,res);
res.push_back(root->val);//打印的位置
inorder(root->right,res);
}
};
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3]
是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
//非递归版本,采用层序遍历的方式,每次取出两个结点进行比较
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root==nullptr||(!root->left&&!root->right))
return true;
vector<int>res;
queue<TreeNode *>q;
q.push(root->left);
q.push(root->right);
while(!q.empty()){
TreeNode *t1=q.front();
q.pop();
TreeNode *t2=q.front();
q.pop();
if(t1==nullptr&&t2==nullptr)//如果两个结点同时为nullptr则continue
continue;
if(t1==nullptr||t2==nullptr)
return false;//如果两结点中只有一个为nullptr,肯定不是对称的
if(t1->val!=t2->val)
return false;//两结点值不相同,肯定不对称
q.push(t1->left);//先push两边,再中间,因为取结点时要同时取出用于比较的两个对称结点
q.push(t2->right);
q.push(t1->right);
q.push(t2->left);
}
return true;
}
};
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root==nullptr||(!root->left&&!root->right))
return true;
return symmetric(root->left,root->right);
}
bool symmetric(TreeNode *root1,TreeNode *root2){
if(root1==nullptr&&root2==nullptr)
return true;
if(root1==nullptr||root2==nullptr)
return false;
if(root1->val!=root2->val)
return false;
return symmetric(root1->left,root2->right)&&symmetric(root1->right,root2->left);
}
};
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
//二叉搜索树的中序遍历序列是有序的,以数组的中间位为分界线,
左右的长度不会大于1,并且左侧的值都小于中间数的值,右侧的数都大于中间数的值。
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
if(nums.empty())
return nullptr;
int i=0;int j=nums.size()-1;
TreeNode *root=BST(nums,i,j);
return root;
}
TreeNode *BST(vector<int>& nums,int i,int j){
if(i>j)//递归结束条件
return nullptr;
int mid=i+(j-i)/2;
TreeNode *root=new TreeNode(nums[mid]);
root->left=BST(nums,i,mid-1);
root->right=BST(nums,mid+1,j);
return root;
}
};
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
//与有序数组转化为二叉搜索树思路一样,但这里需要找到链表的中间结点,
并且需要断开中间结点与前一个结点以及后一个结点,所以实际需要找的是
中间结点的上一个结点,才能断开中间结点与上一个结点。
class Solution {
public:
TreeNode* sortedListToBST(ListNode* head) {
if(head==nullptr)
return nullptr;
if(head->next==nullptr)
return new TreeNode(head->val);//这个条件一定要加上
ListNode *pre=findmidpre(head);
ListNode *mid=pre->next;
pre->next=nullptr;//断开连接
TreeNode* root=new TreeNode(mid->val);
root->left=sortedListToBST(head);
root->right=sortedListToBST(mid->next);
return root;
}
ListNode *findmidpre(ListNode*head){
ListNode *p1=head;
ListNode *p2=head;
ListNode *pre=head;
while(p2&&p2->next){
pre=p1;//保存中间结点的上一个结点
p1=p1->next;
p2=p2->next->next;
}
return pre;
}
};
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
//非递归版本,层序遍历,每遍历一层遇到了叶子结点就返回当前的深度
class Solution {
public:
int minDepth(TreeNode* root) {
if(root==nullptr)
return 0;
queue<TreeNode *>q;
q.push(root);
int count=0;
while(!q.empty()){
count++;
int size=q.size();
while(size--){
TreeNode *tmp=q.front();
q.pop();
if(!tmp->left&&!tmp->right)
return count;
if(tmp->left)
q.push(tmp->left);
if(tmp->right)
q.push(tmp->right);
}
}
return count;
}
};
//递归版本
class Solution {
public:
int minDepth(TreeNode* root) {
if(root==nullptr)
return 0;
int left=minDepth(root->left);
int right=minDepth(root->right);
if(left&&right)//判断是否左右子树都不为空,根结点必须连接到叶子结点上才构成路径
return min(left,right)+1;
if(left)
return left+1;
return right+1;
}
};
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int>res;
if(root==nullptr)
return res;
stack<TreeNode *>s;
s.push(root);
while(!s.empty()){
TreeNode *tmp=s.top();
s.pop();
res.push_back(tmp->val);
if(tmp->right)
s.push(tmp->right);
if(tmp->left)
s.push(tmp->left);
}
return res;
}
};
class Solution {
public:
//层序遍历,每遇到一个结点就翻转它的左右结点。
TreeNode* invertTree(TreeNode* root) {
if(root==nullptr)
return nullptr;
queue<TreeNode *>q;
q.push(root);
while(!q.empty()){
int size=q.size();
while(size--){
TreeNode *node=q.front();
q.pop();
TreeNode *tmp=node->left;
node->left=node->right;
node->right=tmp;
if(node->left)
q.push(node->left);
if(node->right)
q.push(node->right);
}
}
return root;
}
};
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==nullptr)
return nullptr;
root->left=invertTree(root->left);
root->right=invertTree(root->right);
TreeNode *tmp=root->left;
root->left=root->right;
root->right=tmp;
return root;
}
};
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 输出: 6 解释: 节点2
和节点8
的最近公共祖先是6。
递归版本
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==nullptr)
return nullptr;
if(root->val==p->val||root->val==q->val)
//如果root至少与其中一个值相同则root就是最近的公共祖先
return root;
if(p->val>root->val&&q->val>root->val)
//如果两个结点的值都大于root的值,说明两个结点都位于右子树,到右子树中去找
return lowestCommonAncestor(root->right,p,q);
if(p->val<root->val&&q->val<root->val)
//如果两个结点的值都小于root的值,说明两个结点都位于左子树,到左子树中去找
return lowestCommonAncestor(root->left,p,q);
//剩余的一种情况是一个结点位于左,一个结点在右边,则root是他们的最近公共祖先
return root;
}
};
非递归版本:
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while(root){
if(root->val>p->val&&root->val>q->val)
{
root=root->left;
continue;
}
if(root->val<q->val&&root->val<p->val){
root=root->right;
continue;
}
return root;
}
return nullptr;
}
};
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==nullptr)
return nullptr;
if(root->val==q->val||root->val==p->val)
//如果root的值等于其中一个则root为他们的最近公共祖先
return root;
TreeNode *left=lowestCommonAncestor(root->left,p,q);//在左子树中去找这两个结点
TreeNode *right=lowestCommonAncestor(root->right,p,q);//在右子树中去找
if(left&&right)//如果左右子树都找到了,则说明一个在左一个在右,他们的祖先是root
return root;
else if(left)//只在左子树中找到了两个结点
return left;
return right;//只在右子树中找到了两个结点
}
};
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例 1:
输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
class Solution {
public:
int rob(TreeNode* root) {
if(root==nullptr)
return 0;
vector<int>res(2);
res=Rob(root);//定义一个数组,res[0]表示不偷root,res[0]=左结点的最大值+右结点的最大值,res[1]表示偷root,res[1]=root的值+左结点的左右子树的最大值+右结点的左右子树的最大值
return max(res[0],res[1]);
}
vector<int> Rob(TreeNode *root){
if(root==nullptr)
return vector<int>(2);
vector<int>left(2);
vector<int>right(2);
vector<int>res(2);
left=Rob(root->left);
right=Rob(root->right);
res[0]=max(left[0],left[1])+max(right[0],right[1]);//左最大值+右最大值
res[1]=root->val+left[0]+right[0];
return res;
}
};
//记忆化搜索,把前面的值保存下来避免重复计算
class Solution {
unordered_map<TreeNode*,int>mp;
public:
int rob(TreeNode* root) {
if(root==nullptr)
return 0;
if(mp[root])//如果之前已经计算过了,直接返回对应的值
return mp[root];
TreeNode *lnode=root->left;
TreeNode *rnode=root->right;
int left=lnode?rob(lnode->left)+rob(lnode->right):0;
int right=rnode?rob(rnode->left)+rob(rnode->right):0;
mp[root]=max(root->val+left+right,rob(lnode)+rob(rnode));
return mp[root];
}
};
//还可以通过后序遍历,从叶子节点向根节点递推
class Solution {
public:
int rob(TreeNode* root) {
if(root==nullptr)
return 0;
postorder(root);
return root->val;
}
void postorder(TreeNode *root){
if(root==nullptr)
return;
if(root->left)
postorder(root->left);
if(root->right)
postorder(root->right);
int res0=0;
int res1=root->val;
if(root->left){
res0+=root->left->val;
if(root->left->left)
res1+=root->left->left->val;
if(root->left->right)
res1+=root->left->right->val;
}
if(root->right){
res0+=root->right->val;
if(root->right->left)
res1+=root->right->left->val;
if(root->right->right)
res1+=root->right->right->val;
}
root->val=max(res0,res1);
}
};
计算给定二叉树的所有左叶子之和。
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
int sum=0;
if(root==nullptr)
return 0;
if(root->left){
if(!root->left->left&&!root->left->right)
sum+=root->left->val;
else
sum+=sumOfLeftLeaves(root->left);
}
sum+=sumOfLeftLeaves(root->right);
return sum;
}
};
class Solution {
public:
int pathSum(TreeNode* root, int sum) {
if(root==nullptr)
return 0;
//双重递归,因为需要从每一个结点开始遍历
return path(root,sum)+pathSum(root->left,sum)+pathSum(root->right,sum);
}
int path(TreeNode *root,int sum){
if(root==nullptr)
return 0;
int res=0;
if(root->val==sum)
res++;
res+=path(root->left,sum-root->val);
res+=path(root->right,sum-root->val);
return res;
}
};
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
假定 BST 有如下定义:
- 结点左子树中所含结点的值小于等于当前结点的值
- 结点右子树中所含结点的值大于等于当前结点的值
- 左子树和右子树都是二叉搜索树
例如:
给定 BST [1,null,2,2]
,
思路:二叉搜索树的中序遍历序列是有序的,维护一个最大值maxnum,表示出现频率最高的数,维护一个curnum,为当前值的出现频率,如果curnum==maxnum,则两个数都是所找的数,加入数组中。如果curnum>maxnum,把数组中的值清空,加入新的值。
class Solution {
public:
vector<int> findMode(TreeNode* root) {
vector<int>res;
if(root==nullptr)
return res;
TreeNode *pre=nullptr;//pre表示当前结点的上一个结点
int curnum=1;
int max=0;
stack<TreeNode *>s;
while(root||!s.empty()){
if(root){
s.push(root);
root=root->left;
}
else{
root=s.top();
s.pop();
if(pre){//判断是不是第一个结点,第一个结点没有pre
if(root->val==pre->val)
curnum++;
else
curnum=1;
}
if(curnum==max)
res.push_back(root->val);
if(curnum>max){
max=curnum;
res.clear();
res.push_back(root->val);
}
pre=root;//令当前结点等于下一个结点的上一个结点
root=root->right;
}
}
return res;
}
};
//采用层序遍历的方式,从右向左遍历,用队列来保存结点,最后一个结点就是左下角结点
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
if(root==nullptr)
return 0;
queue<TreeNode *>q;
q.push(root);
TreeNode *tmp;
while(!q.empty()){
tmp=q.front();
q.pop();
if(tmp->right)
q.push(tmp->right);
if(tmp->left)
q.push(tmp->left);
}
return tmp->val;
}
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int getMinimumDifference(TreeNode* root) {
if(root==nullptr)
return 0;
stack<TreeNode *>s;
TreeNode *pre=nullptr;//表示前一个结点,初始化的前一个结点是空指针
int min=INT_MAX;//定义一个最小值为整数类型的最大值,每遍历一个结点比较一次决定是否更新它
while(!s.empty()||root){
if(root){
s.push(root);
root=root->left;
}
else{
root=s.top();
s.pop();
if(pre){//判断是否是第一个结点,第一个结点没有pre
int diff=root->val-pre->val;
if(diff<min)
min=diff;
}
pre=root;
root=root->right;
}
}
return min;
}
};
//采用右中左的遍历方式,把上一个结点的值累加到当前节点上
class Solution {
public:
TreeNode *pre=nullptr;//定义上一个结点,初始化为nullptr
TreeNode* convertBST(TreeNode* root) {
if(root==nullptr)
return nullptr;
convertBST(root->right);
if(pre){//判断是否为第一个结点,第一个结点的pre为空
root->val+=pre->val;
}
pre=root;
convertBST(root->left);
return root;
}
};
这道题需要找到以任意一个结点为根结点的左右子树高度的最大值。
因此设置一个全局变量,每次遍历到一个结点都比较一次决定是否更新max.
做类似的二叉树的题时,不用纠结递归函数内部的过程,就把二叉树看作只
有2层的根结点和它的左右结点,明确递归终止条件,明确最后要向上级返回的是什么。
class Solution {
public:
int maxpath=0;
int diameterOfBinaryTree(TreeNode* root) {
if(root==nullptr)
return 0;
diameter(root);
return maxpath;
}
int diameter(TreeNode *root){
if(root==nullptr)
return 0;
int left=diameter(root->left);
int right=diameter(root->right);
int path=left+right;
maxpath=path>maxpath?path:maxpath;
return max(left,right)+1;
}
};
给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
示例 1:
给定的树 s:
3
/ \
4 5
/ \
1 2
给定的树 t:
4
/ \
1 2
返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。
示例 2:
给定的树 s:
3
/ \
4 5
/ \
1 2
/
0
给定的树 t:
4
/ \
1 2
返回 false
class Solution {
public:
bool isSubtree(TreeNode* s, TreeNode* t) {
if(s==nullptr||t==nullptr)
return false;
return subtree(s,t)||isSubtree(s->left,t)||isSubtree(s->right,t);
//从若上一个结点不满足条件,就从它的子节点遍历。直到以每一个结点为根节点判断过一次,双重递归
}
bool subtree(TreeNode* s, TreeNode* t){
if(t==nullptr&&s==nullptr)
return true;
if(s==nullptr||t==nullptr)
return false;
if(s->val!=t->val)
return false;
return subtree(s->left,t->left)&&subtree(s->right,t->right);
}
};
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
//非递归版本
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if(t1==nullptr)
return t2;
if(t2==nullptr)
return t1;
queue<TreeNode *>q1,q2;
q1.push(t1);
q2.push(t2);
t1->val+=t2->val;
while(!q1.empty()&&!q2.empty()){
TreeNode *tmp1=q1.front();
q1.pop();
TreeNode *tmp2=q2.front();
q2.pop();
if(tmp1->left&&tmp2->left){
tmp1->left->val+=tmp2->left->val;
q1.push(tmp1->left);
q2.push(tmp2->left);
}
else {
if(tmp2->left)
tmp1->left=tmp2->left;
}
if(tmp1->right&&tmp2->right){
tmp1->right->val+=tmp2->right->val;
q1.push(tmp1->right);
q2.push(tmp2->right);
}
else {
if(tmp2->right)
tmp1->right=tmp2->right;
}}
return t1;
}
};
//递归版本
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if(!t1&&!t2)
return nullptr;
if(t1==nullptr)
return t2;
if(t2==nullptr)
return t1;
TreeNode *root=new TreeNode(t1->val+t2->val);
root->left=mergeTrees(t1->left,t2->left);
root->right=mergeTrees(t1->right,t2->right);
return root;
}
};
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int L, int R) {
if(root==nullptr)
return nullptr;
if(root->val<L)//左子树全都不满足条件,舍弃左子树。递归右子树
return trimBST(root->right,L,R);
else if(root->val>R)//右子树全都不满足条件,舍弃右子树。递归左子树
return trimBST(root->left,L,R);
//否则,修剪左右子树,根结点不变
root->left=trimBST(root->left,L,R);
root->right=BST(root->right,L,R);
return root;}
};
//层序遍历,找到比root->val大的第一个数
class Solution {
public:
int findSecondMinimumValue(TreeNode* root) {
if(root==nullptr)
return -1;
int num=root->val;
long long minnum=LONG_LONG_MAX;
queue<TreeNode *>q;
q.push(root);
while(!q.empty()){
TreeNode *tmp=q.front();
q.pop();
if(tmp->val<minnum&&tmp->val>num)
minnum=tmp->val;
if(tmp->left)
q.push(tmp->left);
if(tmp->right)
q.push(tmp->right);
}
if(minnum!=LONG_LONG_MAX)
return minnum;
return -1;
}
};
//递归版本
class Solution {
public:
int findSecondMinimumValue(TreeNode* root) {
if(root==nullptr)
return -1;
if(!root->left&&!root->right)
return -1;
int left=root->left->val;int right=root->right->val;
if(root->val==root->left->val)
left=findSecondMinimumValue(root->left);
if(root->val==root->right->val)
right=findSecondMinimumValue(root->right);
if(left!=-1&&right!=-1)
return min(left,right);
if(left!=-1)
return left;
return right;
}
};
class Solution {
public:
int maxpath=0;
int longestUnivaluePath(TreeNode* root) {
if(root==nullptr)
return 0;
longpath(root);
return maxpath;
}
int longpath(TreeNode *root){
if(root==nullptr)
return 0;
int left=longpath(root->left);
int right=longpath(root->right);
if(root->left&&root->val==root->left->val)
left=left+1;
else
left=0;
if(root->right&&root->right->val==root->val)
right=right+1;
else
right=0;
int path=left+right;
maxpath=maxpath>path?maxpath:path;
return max(left,right);
}
};
class Solution {
public:
bool isBalanced(TreeNode* root) {
if(root==nullptr)
return true;
if(!root->left&&!root->right)
return true;
int left=path(root->left);
int right=path(root->right);
if(abs(left-right)>1)
return false;
return isBalanced(root->left)&&isBalanced(root->right);
}
int path(TreeNode *root){
if(root==nullptr)
return 0;
int left=path(root->left);
int right=path(root->right);
return max(left,right)+1;
}
};
class Solution {
public:
bool isBalanced(TreeNode* root) {
if(root==nullptr)
return true;
if(!root->left&&!root->right)
return true;
int dep=0;
return balanced(root,dep);
}
//记录每一层的高度,若某一层不平衡就直接返回false;
bool balanced(TreeNode *root,int &dep){
if(root==nullptr)
return true;
int left=0;int right=0;
if(balanced(root->left,left)&&balanced(root->right,right)){
if(abs(left-right)>1)
return false;
dep=left>right?left+1:right+1;
return true;
}
return false;
}
};