【C++】 —— 笔试刷题day_24

一、判断是不是平衡二叉树

题目解析

这道题,题目描述很简单,就是给定一个二叉树,让我们判断它是不是平衡二叉树(不考虑是不是排序二叉树

平衡二叉树:以任何一个节点为根节点的子树是空树或者左右子树的高度差的绝对值不超过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的所有取值。(这里就枚举出来了子矩阵右下角的坐标)。

如上述,我们枚举所有的子矩阵就用到了4for循环,时间复杂度就是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串

题目解析

现在我们有一个只包含01的字符串(环形,最后一个和第一个是相邻的 ),字符串中每一个字符初始都是白色,我们要选择一段连续区间,让这段区间的字符串变成红色,使红色的0和白色的0数量相等,红色的1和白色的1数量相等;

选择我们要求有多少种染色方案。

算法思路

在想这道题的思路之前,我们先来思考一个问题:

字符串的长度为n,红色的0和白色的0的数量相等、红色的1和白色的1的数量相等,那白色的01的数量之和是不是等于红色的01的数量之和。

我们染色是对一段连续的区间进行染色,那也就是说我们要做染色区间的长度等于没有染色区间的长度时,才可能满足条件。

要想长度为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位置
首先我们要记录一下整个字符串中01的数量,用hash[2]来记录;然后要记录区间内01的数量,使用cnt[2]来记录。

然后就是寻找满足条件的连续子数组问题了

  • 入窗口:让right位置入窗口,更新cnt
  • 出窗口:当区间的长度大于整个字符串长度的一半时,执行出窗口操作。
  • 更新结果:当区间长度等于整个字符串长度的一半,且区间内01的数量都等于整个字符串中01数量的一半时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;
}

到这里本篇文章内容就结束了
感谢各位的支持

相关推荐
西柚小萌新12 分钟前
【Python爬虫实战篇】--Selenium爬取Mysteel数据
开发语言·爬虫·python
努力写代码的熊大12 分钟前
c语言中文件操作详解
c语言·开发语言
QUST-Learn3D19 分钟前
高精度并行2D圆弧拟合(C++)
开发语言·c++
肉肉不吃 肉34 分钟前
ES6 模块化 与 CommonJS 的核心概念解析
开发语言·javascript·es6
明月醉窗台42 分钟前
Qt 入门 6 之布局管理
c语言·开发语言·c++·qt
碎梦归途1 小时前
23种设计模式-结构型模式之适配器模式(Java版本)
java·开发语言·jvm·单例模式·设计模式·适配器模式
云小逸1 小时前
【C++】继承
开发语言·c++
风静雪冷1 小时前
Ubuntu中选择Python虚拟环境
开发语言·python
努力学习的小廉1 小时前
【C++】 —— 笔试刷题day_21
开发语言·c++·算法
YuforiaCode1 小时前
第十四届蓝桥杯 2023 C/C++组 冶炼金属
c语言·c++·蓝桥杯