C++日常刷题积累
今日刷题汇总 - day024
1、判断是不是平衡二叉树
1.1、题目
1.2、思路
读完题,知道让判断节点数为n的二叉树是否满足平衡二叉树。其中,平衡二叉树具备以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都满足是一棵平衡二叉树。那么根据性质,我们需要求子树的高度差,还需要判断左右子树满足平衡性质,所以封装一个求高度的函数height,递归左右子树求得高度,为left和right最大的值为树的高度,其中注意根节点从1计数,所以需要加上本身最后需要+1.然后求得高度后,计算高度差,满足绝对值小于1,那么大于1或小于-1的则不满足返回false即可,最后再递归求左右子树判断是否满足以上条件即可。除此之外,我们发现,递归的过程中会涉及一些没必要的重复操作,依次当遍历到left或right高度为-1时就没必要继续向后遍历了,因为向后遍历就超过绝对值范围了,则采用剪枝法的思想直接返回即可,避免重复遍历的无用功,再加上左右子树的高度不能为负数,最低就是空为0,所以这里用-1判断当前位置是否满足为平衡二叉树即可,是-1则不是平衡二叉树,不是-1则是平衡二叉树。那么接下来,就是程序实现。
1.3、程序实现
首先根据思路,封装一个height函数利用递归求得数得高度,然后结合平衡二叉树的性质判断高度差的绝对值是否满足条件,最后在递归判断左右子树都满足则为true.
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot)
{
if(pRoot == NULL)
return true;
//求左子树高度
int left = height(pRoot->left);
//求右子树高度
int right = height(pRoot->right);
//高度差的绝对值不超过1,否则false
if(left - right > 1 || left - right < -1)
return false;
//递归,左右两个子树都是一棵平衡二叉树。
return IsBalanced_Solution(pRoot->left) && IsBalanced_Solution(pRoot->right);
}
//递归求左右子树的高度
int height(TreeNode* root)
{
if(root == NULL)
return 0;
int left = height(root->left);
int right = height(root->right);
//注意,子树最大深度加上自己本身
return (left > right) ? left + 1 : right + 1;
}
};
1.4、程序实现 – dfs(剪枝)
采用剪枝法和平衡二叉树高度的性质设定-1为判断条件,然后递归求得左右子树的高度差并且满足高度不等于-1,则为平衡二叉树。
剪枝的核心思想是在搜索过程中尽早发现并排除那些无法产生解或不是最优解的分支,从而减少搜索空间,提高搜索效率。
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot)
{
return dfs(pRoot) != -1;
}
int dfs(TreeNode* root)// 返回值不是 -1 的话,其余的返回值表⽰的是树⾼
{
if (root == nullptr)
return 0;
int left = dfs(root->left);
//拿到左子树得高度,判断左子树是否满足平衡二叉树,右子树同理
if (left == -1)
return -1; // 剪枝
int right = dfs(root->right);
if (right == -1)
return -1;
//到这步说明左右子树都满足平衡二叉树,则判断高度差,最后返回高度 !=-1则为true
return abs(left - right) <= 1 ? max(left, right) + 1 : -1;
}
};
2、 最大子矩阵
2.1、题目
2.2、思路
读完题,知道求一个N*N矩阵中子矩阵的最大元素和,由于子矩阵没有固定的约束,所以想到蛮力法就是将所有和的情况取最大值,但是这样4层for循环再加上遍历求和时间会超时,那么在此基础上进行优化。经过思考和分析想到之前做过的十字爆破解决枚举复杂的思路,对矩阵采用预处理,先计算好二维前缀和,就解决求和遍历的超时问题了。为了方便理解画个图:
那么接下来,就是程序实现。
2.3、程序实现 – 二维dp表
详解见思路即可。这里注意的是选择躲开一行一列,从1开始方便映射位置关系。
#include <iostream>
using namespace std;
const int N = 110;
int dp[N][N];
int main()
{
int n;
cin >> n;
int x;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
cin >> x;
//预处理,计算二维前缀和
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + x;
}
}
//使用二维dp表
int ret = -127 * N;//题目范围外的值
for(int x1 = 1; x1 <= n; x1++)
{
for(int y1 = 1; y1 <= n; y1++)
{
for(int x2 = x1; x2 <= n; x2++)
{
for(int y2 = y1; y2 <= n; y2++)
{
ret = max(ret, dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1]);
}
}
}
}
cout << ret << endl;
return 0;
}
3、小葱的01串
3.1、题目
3.2、思路
读完题知道,对于一个长度为偶数的0/1环形字符串中,进行连续区间的染色操作,使其满足使得红色的字符’0’数量等于白色的字符’0’数量,红色的字符’1’数量等于白色的字符’1’数量。问有多少种不同的染色方法?那么根据题目和示例分析,对于这个连续的区间,是能够确定其大小的为字符串大小的一半,否则是不可能满足0/1数量相等的条件的,如图所示:
然后按照蛮力法,依次在half一半的区间中,遍历统计0/1的数量,如图所示:
那么接下来,就是程序实现。
3.3、程序实现 – 滑动窗口
首先根据思路分析总结为以下步骤:
(1)、统计输入字符串中0/1的个数;用于判定是否满足染色数量是否相等;
(2)、定义滑动窗口:
a、窗口大小经过示例分析,为长度的一半;
b、进窗口(right指针0/1进窗口);
c、判断窗口(超出窗口就left指针0/1出窗口,滑动窗口left++);
d、更行结果(判定数量相等,更新染色方案+2)
#include <iostream>
#include <string>
using namespace std;
int main()
{
int n;
cin >> n;
string str;
cin >> str;
//统计字符串中0/1的个数
int sum[2] = {0};
for(auto ch : str)
{
sum[ch - '0']++;
}
int len = str.size();
int left = 0;
int right = 0;
int half = len/2;//窗口大小
int count[2] = {0};//统计窗口中0/1个数
int ret = 0;
while(right < len - 1)
{
//进窗口
count[str[right] - '0']++;
//判断窗口,出窗口,滑动窗口
while(right - left + 1 > half)
{
count[str[left] - '0']--;
left++;
}
//更新结果
if(right - left + 1 == half)
{
if(count[0]*2 == sum[0] && count[1]*2 == sum[1])
ret += 2;
}
right++;
}
cout << ret << endl;
return 0;
}
4、题目链接
🌟判断是不是平衡二叉树
🌟 最大子矩阵
🌟小葱的01串