一、判断是不是平衡二叉树
题目解析

这道题,题目描述很简单,就是给定一个二叉树,让我们判断它是不是平衡二叉树(不考虑是不是排序二叉树)
平衡二叉树:以任何一个节点为根节点的子树是
空树
或者左右子树的高度差的绝对值不超过1
,且左右子树都是平衡子树。
算法思路
这里要想判断一个树不是平衡二叉树,肯定是逃不了递归的;那该如何去递归实现呢?
遍历每一个节点,然后获取它左右子树的高度,判断这个子树是不是平衡二叉树,然后再遍历它的左子树和右子树?
可是,这样我们遍历到一个节点就要求它左右子树的高度,再遍历其左右子树,这样的消耗是不是太大了
这里我们整个二叉树,遍历每一个子树时,我们不仅要拿到其左右子树的高度,而且还要知道左右子树是不是平衡二叉树;
那我们如何如果函数的返回值来拿到这两个信息呢?
我们方法能解决这个问题
方法一:我们让函数返回一个
struct
结构体,这个结构体中存储的子树的高度和子树是否是平衡二叉树。我们可以直接使用
pair<int, bool>
来存储这两个信息。方法二:我们直到一个子树的高度是大于等于
0
的,那我们可以提供一个特殊的数字来表示子树表示平衡二叉树;我们就可以使用
-1
来表示这个数不是平衡二叉树,如果返回值>=0
就表示子树的高度,如果返回值== -1
就表示这个子树不是平衡二叉树。

代码实现
cpp
class Solution {
public:
int dfs(TreeNode* root) {
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;
return (abs(left - right) > 1 ? -1 : max(left, right) + 1);
}
bool IsBalanced_Solution(TreeNode* pRoot) {
return dfs(pRoot) != -1;
}
};
二、最大子矩阵
题目解析

这道题,题目给定一个
n
阶矩阵,让我们找子矩阵中所有数的和最大的子矩阵,最后输出最大子矩阵的大小(矩阵中所有数的和)。
算法思路
对于这道题,我们要求找子矩阵,那第一个问题来了:如何去找到一个子矩阵?
要找一个子矩阵,我们可以去找这个子矩阵在整个矩阵中的位置(第几行到第几行、第几列到第几列),换一种说法就是,找到矩阵
左上角的坐标
和右下角的坐标
(或者左下角的坐标
和右上角的坐标
);那我们如何去枚举所有的子矩阵呢?
很简单,就是暴力枚举:
先枚举
x1
的所有取值,再枚举y1
的所有取值(这样就枚举出来了子矩阵左上角的坐标
);再枚举x2
的所有取值,最后枚举y2
的所有取值。(这里就枚举出来了子矩阵右下角的坐标
)。
如上述,我们枚举所有的子矩阵就用到了4
层for
循环,时间复杂度就是4^n
。
那现在我们枚举出来了所有的子矩阵,该如何去获得这个子矩阵中所有数的和呢?
首先,暴力枚举子矩阵中的所有数,然后统计和。(但是,这样使用了两层
for
循环,时间复杂度为2^n
再结合上枚举所有子矩阵的4^n
,时间复杂度就来到了恐怖的6^n
)所以现在,我们要想出来一种办法,让我们在
O(1)
的时间复杂度下就拿到子矩阵中所有数的和。
这里就直奔主题,我们要使用前缀和,来让我们在O(1)
下,就能拿到子矩阵中所有数的和。
前缀和和动态规划很相似,可以说前缀和是动态规划的一个分支;现在来看我们如何通过前缀和来拿到子矩阵中所有数的和。
这里我们之前在做一维前缀和时,
dp[i]
表示的就是前i
个数的和,这样我们通过简单运算就可以直接拿到一个区间内所有数的和。那这里二维前缀和:
dp[i][j]
就表示[1,1]
,[i,j]
(第1
行到第i
行、第1
列到第j
列)子矩阵的和。
那我们如何通过运算来拿到子矩阵的和呢?
现在子矩阵
[x1,y1]
、[x2,y2]
我们要提供运算来求这个子矩阵中所有数的和。这里就像我们数学中的求一个图形的面积一样,我们通过整个的矩形减去四周的矩形就求出了我们想要的矩形面积;
这里也类似,我们想要的是
[x1,y1]
、[x2,y2]
的所有数的和,我们知道[1,1]
、[x2,y2]
中所有数的和dp[x2][y2]
;
[1,1]
、[x1-1][y2]
中所有数的和dp[x1-1][y2]
;[1,1],[x2][y1-1]
中所有数的和dp[x2][y1-1]
;这里
dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1]
,这样计算的结果是多减去了一个[1,1]
、[x1-1][y1-1]
在所有数的和,我们就要在加上一个dp[x1-1][y1-1]
;所有我们子矩阵
[x1,y1]
、[x2,y2]
中所有数的和sum = dp[x2][y2] - dp[x1-1][y2] - dp[x1][y1-1] + dp[x1-1][y1-1];
这样我们再记录一下所有子矩阵的和的最大值,然后输出即可。

代码实现
cpp
#include <iostream>
using namespace std;
const int N = 101;
int dp[N][N];
int n;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int x;
cin >> x;
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + x;
}
}
//遍历所有子数组
int ret = -127 * N * 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++) {
//获取子矩阵的和
int sum = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
ret = max(ret, sum);
}
}
}
}
cout << ret << endl;
return 0;
}
三、小葱的01串
题目解析

现在我们有一个只包含
0
和1
的字符串(环形,最后一个和第一个是相邻的 ),字符串中每一个字符初始都是白色,我们要选择一段连续区间,让这段区间的字符串变成红色,使红色的0
和白色的0
数量相等,红色的1
和白色的1
数量相等;选择我们要求有多少种染色方案。
算法思路
在想这道题的思路之前,我们先来思考一个问题:
字符串的长度为
n
,红色的0
和白色的0
的数量相等、红色的1
和白色的1
的数量相等,那白色的0
和1
的数量之和是不是等于红色的0
和1
的数量之和。我们染色是对一段连续的区间进行染色,那也就是说我们要做染色区间的长度等于没有染色区间的长度时,才可能满足条件。
要想长度为
n/2
的连续区间满足条件,我们还要保证区间内0
的数量和区间外0
的数量、区间内1
的数量和区间外1
的数量相等。
那也即是说,我们现在要找一段连续的区间,这段区间的长度为n/2
,并且这段区间中0
的数量等于整个字符串中0
数量的一半,1
的数量等于整个字符串中1
数量的一半。
那这样这道题不就是一到找满足条件的连续子数组的数量的问题吗
这里,题目说最后一个位置和第一个位置是连续的,我们还要考虑这种情况;
我们可以发现,我们寻找到一个满足条件的子数组以后,剩下的也是满足条件的一个子数组;所以我们可以每次让
ret+=2
,这这样就无需考虑如何让最后一个位置和第一个位置连续的问题了。但是这样问题就来了:
[0,n/2-1]
和[n/2,n-1]
,如果我们还是遍历到n-1
,那这两种情况是会计算两次的,所以我们要遍历到n-2
位置
首先我们要记录一下整个字符串中0
和1
的数量,用hash[2]
来记录;然后要记录区间内0
和1
的数量,使用cnt[2]
来记录。然后就是寻找满足条件的连续子数组问题了
- 入窗口:让
right
位置入窗口,更新cnt
;- 出窗口:当区间的长度大于整个字符串长度的一半时,执行出窗口操作。
- 更新结果:当区间长度等于整个字符串长度的一半,且区间内
0
和1
的数量都等于整个字符串中0
和1
数量的一半时ret+=2
。
代码实现
cpp
#include<iostream>
using namespace std;
const int N = 300001;
int arr[N];
int main()
{
int n;
string s;
cin>>n>>s;
int hash[2] = {0,0};
for(auto& e:s)
{
hash[e-'0']++;
}
int left = 0, right = 0,ret = 0;
int cnt[2] = {0,0};
while(right < n-1)
{
//进
cnt[s[right]-'0']++;
while(right - left + 1 > n/2)
{
cnt[s[left]-'0']--;
left++;
}
if(right - left + 1 == n/2 && cnt[0] * 2 == hash[0] && cnt[1] * 2 == hash[1])
ret+=2;
right++;
}
cout<<ret<<endl;
return 0;
}
[left]-'0']--;
left++;
}
if(right - left + 1 == n/2 && cnt[0] * 2 == hash[0] && cnt[1] * 2 == hash[1])
ret+=2;
right++;
}
cout<<ret<<endl;
return 0;
}
到这里本篇文章内容就结束了
感谢各位的支持