(刷题笔记) 牛客网 腾讯2020校园招聘-后台 3.逆序对

这篇博客记录了作者在解决牛客网腾讯2020校园招聘后台面试的逆序对问题时的学习过程。最初采用暴力法,但时间复杂度较高,后通过学习归并排序来优化解题思路。文章提到了LeetCode的912. 排序数组作为参考,并详细描述了如何运用归并排序计算逆序对。作者还讨论了翻转数组时的切片问题,尤其是2^n数据量下产生n+1种切片方式的疑惑。最后给出了暴力法和归并排序法的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

复制过来会乱码,题目是截图截过来的,可能会不清楚,可以通过下面的连接去看题目
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述

链接:https://www.nowcoder.com/questionTerminal/8fe007e54fc04b5e82089aaa71ba3553
来源:牛客网

解题思路

这道题我一开始只会用暴力法写,也就是按照题目意思翻转一次计算一次逆序对,时间复杂度O(MNN),M是翻转次数,N是数据规模,只能通过50%的测试用例。

然后看了一下解答区的答案,需要用到归并排序的思想,我不懂,然后去学了一下,可以看Leetcode 912. 排序数组
这是只用归并排序就能解决的题目,也是我学习归并排序的过程。

这道题的思路是参照的这一篇:腾讯笔试题——逆序对

我学习了三天,其实也不是特别懂这个思想。

  1. 首先是利用归并计算出来的是将数组切割成不同数据长度所产生的逆序对。
  2. 然后将数组反转,求逆序对,也就是与第1步相反的求的不同数据长度的顺序对。
  3. 最后是根据划分的翻转长度,将小于等于翻转长度的逆序对和顺序对交换,交换完成后的逆序对数组的数据累积起来就是结果。

我目前还没搞懂的就是为什么2^n的数据量,为什么会产生n+1种切片方式?
难道是因为只考虑2的次方的切片方式?好像也是,后面翻转的q也是2的q次方的翻转。
后面有机会再自己测试一下好了。

代码(C++)

暴力法

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
 
using namespace std;
int main(){
     
    //输入n和n序列
    int n=0;
    cin>>n;
    int len=pow(2,n);
    vector<int> vec(len);
    for(int i=0;i<len;++i){
        cin>>vec[i];
    }
     
    //输入m和m序列
    int m=0;
    cin>>m;
    vector<int> mvec(m);
    for(int i=0;i<m;++i){
        cin>>mvec[i];
    }
     
    //翻转数据
    for(int i=0;i<m;++i){
        auto ite=vec.begin();
        int gap=pow(2,mvec[i]);
        for(int j=0;j<len;j+=gap){
            reverse(ite+j,ite+j+gap);    
        }
         
         
         //计算逆序数对的数目
        int ans=0;
        for(int i=1;i<len;++i){
            for(int j=0;j<i;++j){
                if(vec[i]<vec[j]) ans++;
            }
        }
         
        cout<<ans<<endl;
    }
     
    
    return 0;
     
         
}

归并排序

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>

using namespace std;

typedef long long  ll;

void merge(vector<ll>& vec,ll L,ll R,vector<ll>& cnt,ll index){
    
    ll mid=L+(R-L)/2;
    ll p1=L;
    ll p2=mid+1;
    vector<ll> temp(R-L+1);
    ll i=0;
    ll pairCnt=0;
    
    while(p1<=mid&&p2<=R){
        if(vec[p1]<=vec[p2]){
            temp[i++]=vec[p1++];
        }
        else{
            pairCnt += mid-p1+1; //左右都是有序的,有一个vec[p2]<vec[p1],
                                   // 就说明p1右边的都比该p2位置的值大;
            temp[i++]=vec[p2++];
        }
    }
    
    while(p1<=mid){
        temp[i++]=vec[p1++];
    }
    
    while(p2<=R){
        temp[i++]=vec[p2++];
    }
    
    for(ll i=0;i<temp.size();++i){
        vec[L+i]=temp[i];
    }
    
    cnt[index]+=pairCnt; 
}

void mergeSort(vector<ll>& vec,ll L,ll R,vector<ll>& cnt,ll index){
    
    if(L==R) return;
    ll mid=L+(R-L)/2;
    mergeSort(vec,L,mid,cnt,index-1);
    mergeSort(vec,mid+1,R,cnt,index-1);
    merge(vec,L,R,cnt,index);
}




int main(){
     //输入n和n序列
    int n=0;
    cin>>n;
    ll len=pow(2,n);
    vector<ll> vec(len);
    for(ll i=0;i<len;++i){
        cin>>vec[i];
    }
     
    //输入m和m序列
    
    
    //记录不同长度逆序对和顺序对
    vector<ll> cnt(n+1,0);
    vector<ll> reCnt(n+1,0);
    vector<ll> reVec(vec.rbegin(),vec.rend());
    mergeSort(vec,0,vec.size()-1,cnt,n);
    mergeSort(reVec,0,reVec.size()-1,reCnt,n);
    
    
    ll m=0;
    cin>>m;
    
    for(ll i=0;i<m;++i){
        int q=0;
        cin>>q;
        ll ans=0;
        
        for(int i=1;i<=q;++i){
            swap(cnt[i],reCnt[i]);
        }
        
        for(int i=1;i<=n;++i){
            ans+=cnt[i];
        }
        
        cout<<ans<<endl;
        
    }
      
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值