每日一题分享

这篇博客探讨了如何确定一个数组中能完成排序的最多块数。通过分析题目,将问题转化为寻找最短的排序子数组。作者提出两种方法,一种是通过排序和滑动窗口,另一种是利用前缀最大值和后缀最小值。这两种方法分别具有不同的时间复杂度,旨在找到满足整体排序条件的边界。博客中提供了详细的代码实现和进阶分析。
摘要由CSDN通过智能技术生成

@每日一题分享
今天分享的题目是Lec. 768 最多能完成排序的块 ||
题目如下:嫌麻烦的小伙伴可以先看看题目,然后在去lec做题
题目
题目用例

思路

题目分析

题目要求我们获得最多的能过完成排序的块,实际上问题可以转换为能够符合题中整体排序要求的前提下,找到数组中每一个部门最短的排序子数组,可能这么说有点不太清晰,请看用例 [2,1,3,4,4];
我们首先确定,整体的排序数组为[1,2,3,4,4] (这点如何确定,后续会提到);另外,最短无序子数组中有提到,如何确定最短的需要排序的子数组:最短的排序的子数组的边界一定是需要调换位置的,因此,只需要找到两个最需要调换的边界即可。在上个例子中,[2,1] 即为两个两个边界,那么这个既能够构成最短的子数组。

那么,如何确定整体的排序顺序呢? 最暴力的方法就是通过排序;另外,利用滑动窗口的机制也能够找到最短的子数组,如上个例子中
显然,[2,1,3,4,4]和[1,2,3,4,4]中的[2,1]和[1,2]能够满足完成排序的块,并且是最短的,因为在他们都处于上边提到的边界。

代码

int maxChunksToSorted(vector<int>& arr) 
{
    vector<int> expected = arr;   // 复制数组,通过排序获得整体有序的数组,然后在与原数组进行比对
    sort(expected.begin(), expected.end());
    
    unordered_map<int, int> count; // 记录每个元素出现的个数
    int occurrence = 0; // 记录两个数组到 i 位置时,有多少种元素差异
    int res = 0;
    for (int i = 0; i < arr.size(); ++i)
    {
    	// 利用滑动窗口的机制
        int x = arr[i], y = expected[i];
        
        ++count[x];
        if (count[x] == 1) ++occurrence;
        if (count[x] == 0) --occurrence;
        
        --count[y];
        if (count[y] == -1) ++occurrence;
        if (count[y] == 0) --occurrence;
        
        // 如果当前没有元素差异,说明已经处于边界
        if (occurrence == 0) ++res;
    } 
    
    return res;
}

进阶分析

上述提到的方法调用了一次系统排序API,时间复杂度为 O ( N ⋅ l o g N ) O(N \cdot \rm{log}N) O(NlogN)
最多能完成排序的块中可以了解到,一个块能够满足整体有序的条件一定是 当前包括 i 位置以前的最大值一定小于 i 位置以后的最小值;
换成表达式即为 preMAX[i] <= sufMIN[i+1]

代码:

int maxChunksToSorted(vector<int>& arr) 
{
    int n = arr.size();
    vector<int> preMAX(n,0);
    vector<int> sufMIN(n,0);

    preMAX[0] = arr[0];
    sufMIN[n-1] = arr[n-1];
    for (int i = 1; i < n; ++i) {
        preMAX[i] = max(preMAX[i-1],arr[i]);
        sufMIN[n-i-1] = min(sufMIN[n-i],arr[n-i-1]); 
    }

    int ans = 0;
    for (int i = 0; i < n-1; ++i) {
        if (preMAX[i] <= sufMIN[i+1]) { // 一定不能同时包括 i 元素, 因为自称一块是需要包括当前元素的。
            ++ans;
        }
    }

    return ans+1; // 上边的遍历中,没有考虑最后一段,需要加上最后一段
}

上述均为学习过程中的思考,简单记录一下

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值