LeetCode——Weekly Contest 319

LeetCode周赛第319场记录
这场周赛的质量也很高,有很多值得学习的地方。

2469. 温度转换

在这里插入图片描述
这道题很简单,直接根据已有的信息转换即可,一行代码搞定,注意公式不要敲错。

class Solution {
public:
    vector<double> convertTemperature(double celsius) {
        return {celsius + 273.15, celsius * 1.80 + 32.00};
    }
};

2470. 最小公倍数为 K 的子数组数目

在这里插入图片描述
首先这道题要的是最小公倍数,要完成这一点必须了解最小公倍数的求法:

最小公倍数的求解依赖于求最大公约数,记住这个结论。
求解最大公约数的递归算法如下:

int gcd(int a, int b)		// 求最大公约数的递归函数
{
	if(b == 0) 
		return a;
	else return gcd(b, a % b);
}

利用上面这个函数可以快捷地求出a和b的最小公倍数lcm(a,b)

lcm(a,b) = a / gcd(a,b) * b;

给出这道题的完整代码和注释:

class Solution {
	int gcd(int a, int b)					// 求最大公约数的递归函数
	{
		if(b == 0) 
			return a;
		else return gcd(b, a % b);
	}
	
	
public:
    int subarrayLCM(vector<int>& nums, int k) {
        int n = nums.size();
		
		int Ans = 0;
		for(int i = 0 ; i < n ; ++i)
		{
			if(k % nums[i] == 0)
			{
				int Tmp = nums[i];	// 记录当前子数组(滑动窗口)的最小公倍数为nums[i]
				int j = i;			// 以i为左边界开始滑动窗口[i,j]
				while(j < n)
				{
					/*计算当前窗口内所有数的最小公倍数*/
					Tmp = Tmp / gcd(Tmp, nums[j]) * nums[j];
					if(Tmp == k) // 如果当前最小公倍数为k,那么这就是一个答案
						++Ans;
					/*
					如果当前最小公倍数不为k,不能说明它不是一个答案,随着窗口的滑动
					可能在后续出现答案,但是当前最小公倍数和k的最小公倍数不能超过k(可以等于k)
					否则就应该终止循环了
					*/
					if(Tmp / gcd(Tmp, k) * k  == k)	 
						++j;
					else break;
				}
			}
		}
		
		return Ans;
    }
};

2471. 逐层排序二叉树所需的最少操作数目

在这里插入图片描述
这道题的要求非常的简单易懂,就是给一棵二叉树,按照层序遍历的方式遍历之。每一层只允许通过交换(swap)的方式进行排序,问这样对二叉树逐层进行排序,总共需要多少次交换次数

对二叉树进行层序遍历是基本算法,这里不再赘述。最困难的地方是解决如下问题:

对于一个指定序列,需要至少多少次交换操作才可以让它有序

这个问题的解法是这样的,首先可以证明这个问题一定是有解的,因为冒泡排序就是通过相邻元素的交换完成的,但是冒泡排序往往不是最优解,它的解法应该如下:

给定一个乱序的序列,比如[4,3,1,2,6],首先使用sort排序将其按照增量排序为[1,2,3,4,6]。
使用一个哈希表记录每个元素应在的位置,因为本例比较简单,哈希表内容应该如下:

HashTable[1] = 0
HashTable[2] = 1
HashTable[3] = 2
HashTable[4] = 3
HashTable[6] = 4

然后回到原始序列[4,3,1,2,6]中,从第一个元素4开始:
交换1:按照哈希表指示,4应该在下标为3的地方,交换之得到序列[2,3,1,4,6]
交换22应该在下标为1的地方,交换之得到[3,2,1,4,6]
交换3: 3应该在下标为2的地方,交换之得到[1,2,3,4,6]
此时1到达了它应在的地方,交换应该到此结束

我们逐层使用上述方法对每一层的序列进行排序,加和即可得到最后的结果
完整的代码如下:

class Solution 
{
public:
    int minimumOperations(TreeNode* root) 
    {
        queue<TreeNode*> Q;
		Q.push(root);
        vector<vector<int>> LayerArray;
		int Ans = 0;
		/*
			对树进行层序遍历来获得每一层的序列
		*/
		while(!Q.empty())
		{
			vector<int> Array{};
			int n = Q.size();
			for(int i = 0 ; i < n ; ++i)
			{
				TreeNode* Front = Q.front();
                Q.pop();
				Array.push_back(Front->val);
				if(Front->left)
					Q.push(Front->left);
				if(Front->right)
					Q.push(Front->right);
			}
            LayerArray.push_back(Array);
		}
		
		/*对每一层的序列执行上述的交换算法,循环计数*/
        for(vector<int>& EachArray : LayerArray)
        {
            int n  = EachArray.size();
            vector<int> Sorted(EachArray.begin(), EachArray.end());
            sort(Sorted.begin(), Sorted.end());
            unordered_map<int, int> Map;
            for(int i = 0 ; i < n ; ++i)
                Map[Sorted[i]] = i;					// 记录数字应该放在什么位置
            for(int i = 0 ; i < n ; ++i)
            {
				/*
				如果当前数字所处位置不是它所在位置,
				交换它和它所应该在的位置上的元素,同时交换操作计数值+1
				*/
                while(EachArray[i] != Sorted[i])	
                {
                    swap(EachArray[i], EachArray[Map[EachArray[i]]]);
                    Ans++;
                }
            }
        }
		return Ans;
    }
};

2472. 不重叠回文子字符串的最大数目

在这里插入图片描述
最后一题是一个线性DP,涉及回文子串的问题有两种基本算法要掌握:
1.中心扩展法,时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1)
2.Manacher’s Algorithm(马拉车算法),时间复杂度和空间复杂度都是 O ( n ) O(n) O(n)
这两种算法的解释可以见回文子串问题
这道题使用中心扩展法+DP即可,直接给出代码:

class Solution {
public:
    int maxPalindromes(string s, int k) 
    {
    	// f[i]表示下标直到i-1的字符串中满足条件的回文子串个数
        int n = s.length(), f[n + 1];	
        memset(f, 0, sizeof(f));
        for (int i = 0; i < 2 * n - 1; ++i) 
        {
            int l = i / 2, r = l + i % 2; 	// 中心扩展法的合并写法
            f[l + 1] = max(f[l + 1], f[l]);	// 不选择下标为l的字符的情况
            for (; l >= 0 && r < n && s[l] == s[r]; --l, ++r)	// 开始扩展直到形成一个符合要求的字符串
                if (r - l + 1 >= k) 
                {
                    f[r + 1] = max(f[r + 1], f[l] + 1);
                    break;
                }
        }
        return f[n];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值