代码运行优化及用C++标准库进行程序运算时间统计

前言

这篇博客是用来记录在牛客网上的一道编程题时遇到的问题,就是这道题: 求数组中的逆序对,这道题需要统计一个数组中的逆序对的数量,比如[2, 1],逆序对就是(2, 1),数量为1,[3, 2, 1]逆序对就是(3, 1),(2, 1),(3, 2),数量为3 。这道题的解法用到了归并排序的思想,基本思想就是分治的思想。将数组分成前后两段,先统计前段内的逆序数对,再统计后段内的逆序数对,再将排序后的得到的有序的前后段归并起来,在归并的过程中,可以统计得到前段与后段中的逆序数对。但是同一种思想不同的代码。我的代码总是有50%例子会超时。于是我统计了我的代码的运行时间和别人的代码的运行时间,差别非常大。

C++程序运行时间统计

#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <math.h>
#include <string>
#include <vector>
#include <stack>
#include <chrono>
class Solution {
public:
    long long count = 0;
    void MSort(vector<int> input, vector<int> &output, long long s, long long m)
    {
        if (s > m)
            return;
        else if (s == m)
        {
            output[s] = input[s];
        }
        else {
            long long center = (s + m) / 2;
            vector<int> temp = input;
            MSort(input, temp, s, center);
            MSort(input, temp, center + 1, m);
            long long i = center, j = m, k = m;

            for (; i >= s&&j >= center + 1; k--)
            {
                if (temp[i] > temp[j])
                {
                    output[k] = temp[i];
                    i--;
                    count += j-center;
                }
                else
                {
                    output[k] = temp[j];
                    j--;
                }
            }
            while (i >= s) {
                output[k] = temp[i];
                i--;
                k--;
            }
            while (j >= center + 1) {
                output[k] = temp[j];
                j--;
                k--;
            }

        }
    }
    int InversePairs(vector<int> data)
    {
        vector<int> t(data);
        MSort(data, t, 0, data.size() - 1);
        int s = count % 1000000007;
        return s;
    }

	// 后面是别人的代码
    int InversePairs1(vector<int> data) {
        //用long long类型否则会超限
        long long num = 0;
        mergesort(data, num, data.begin(), data.end() - 1);
        return num % 1000000007;
    }
    //归并排序
    void mergesort(vector<int> &data, long long &num, vector<int>::iterator start, vector<int>::iterator end) {
        if (start == end) return;
        vector<int>::iterator mid = start + (end - start) / 2;
        mergesort(data, num, start, mid);
        mergesort(data, num, mid + 1, end);
        merge(data, num, start, end);
    }
    //合并
    void merge(vector<int> &data, long long &num, vector<int>::iterator start, vector<int>::iterator end) {
        if (start == end) return;
        vector<int>::iterator mid = start + (end - start) / 2;
        //把要合并的两个子数组保存出来
        vector<int> data1(start, mid + 1);
        vector<int> data2(mid + 1, end + 1);
        //两个迭代器分别指向两个数组的末尾
        vector<int>::iterator p = data1.end() - 1;
        vector<int>::iterator q = data2.end() - 1;
        vector<int>::iterator m = end;
        int sz1 = data1.size();
        int sz2 = data2.size();
        //合并,当前面数组的元素大于后面时计数加一
        while (sz1>0 && sz2>0) {
            if (*q<*p) {
                num = q - data2.begin() + 1 + num;//1、迭代器相减注意顺序
                *m = *p;
                --sz1;
                --m;
                if (sz1>0) --p;//2、有效迭代器的范围是[begin(),end()),begin()位置已经不可以再自减了
            }
            else {
                *m = *q;
                --sz2;
                --m;
                if (sz2>0)  --q;
            }
        }
        //3、把剩余的放入数组中,一开始分析出来其中一个数组最多只剩一个元素,而没有让迭代器自减
        while (sz1--) {
            *m = *p;
            if (sz1 > 0) {
                --m;
                --p;
            }
        }
        while (sz2--) {
            *m = *q;
            if (sz2 > 0) {
                --m;
                --q;
            }
        }
    }

};

int main()
{
    srand((unsigned)time(NULL));
    int a[10000];
    for (int i = 0; i < 10000; i++)
        a[i] = rand();

    Solution s;
    // 手工填写的数据
    vector<int> dat(a, a+sizeof(a)/sizeof(long long));
    vector<int> dat1 = {1,2,3,4,5,6,7,0};
    vector<int> dat2 = { 364,637,341,406,747,995,234,971,571,219,993,407,416,366,315,301,601,650,418,355,460,505,360,965,516,648,727,667,465,849,455,181,486,149,588,233,144,174,557,67,746,550,474,162,268,142,463,221,882,576,604,739,288,569,256,936,275,401,497,82,935,983,583,523,697,478,147,795,380,973,958,115,773,870,259,655,446,863,735,784,3,671,433,630,425,930,64,266,235,187,284,665,874,80,45,848,38,811,267,575 };

	// 按题中的说明生成的数据
    vector<int> data50(10000, 0);
    vector<int> data75(100000, 0);
    vector<int> data100(200000, 0);

    for (int i = 0; i < 10000; i++)
    {
        data50[i] = rand()%50;
    }
    for (int i = 0; i < 100000; i++)
    {
        data75[i] = rand()%75;
    }

    for (int i = 0; i < 200000; i++)
    {
        data100[i] = rand()%100;
    }
    chrono::milliseconds t50;
    chrono::milliseconds t75;
    chrono::milliseconds t100;
    chrono::nanoseconds nano;
    std::nano nan;

    chrono::steady_clock clock;
    chrono::time_point<chrono::steady_clock, chrono::milliseconds> timep;
    auto start = chrono::steady_clock::now();
    int c50 = s.InversePairs1(data50);
    auto tp50 = chrono::steady_clock::now();
    int c75 = s.InversePairs1(data75);
    auto tp75 = chrono::steady_clock::now();
    int c100 = s.InversePairs1(data100);
    auto tp100 = chrono::steady_clock::now();

    t50 = chrono::duration_cast<chrono::milliseconds>(tp50 - start);
    t75 = chrono::duration_cast<chrono::milliseconds>(tp75 - tp50);
    t100 = chrono::duration_cast<chrono::milliseconds>(tp100 - tp75);

    long long count50 = t50.count();
    long long count75 = t75.count();
    long long count100 = t100.count();
    std::cout << "50: " << double(t50.count()) / 1000 << " 75: " << double(t75.count()) / 1000 << " 100: " << double(t100.count()) / 1000 << std::endl;

    system("pause");
	return 0;
}

得到的结果是我的代码在运行%100,size(2*10^5)的数据时需要170多秒,而别人的代码只需要5秒多,差别巨大。

代码优化的实例思考

仔细观察后发现,我的代码中,对传入的input数据在递归时进行了大量复制操作(vector temp(input)),而且这是深拷贝,在内存中复制大量数据时可能太耗时,而别人的代码中是使用下标进行合并等操作,不需要拷贝数据,可能是因为这个原因,待我改进代码后再进行观察。

改进后的代码

    void MSort(vector<int> &input, vector<int> &output, long long s, long long m, long long &num)
    {
        if (s > m)
            return;
        else if (s == m)
        {
            output[s] = input[s];
        }
        else {
            long long center = (s + m) / 2;
            MSort(input, output, s, center, num);
            MSort(input, output, center + 1, m, num);
            long long i = center, j = m, k = m;

            for (; i >= s&&j >= center + 1; k--)
            {
                if (input[i] > input[j])
                {
                    output[k] = input[i];
                    i--;
                    num += j-center;
                }
                else
                {
                    output[k] = input[j];
                    j--;
                }
            }
            while (i >= s) {
                output[k] = input[i];
                i--;
                k--;
            }
            while (j >= center + 1) {
                output[k] = input[j];
                j--;
                k--;
            }
            // 完成排序的数据存储在output中,现在将其转移到input中
            memcpy(input.data() + s, output.data() + s, (m - s+1)*sizeof(input.back()));
            // input.cop(output.begin()+s,output.begin()+m);
        }
    }
    int InversePairs(vector<int> data)
    {
        vector<int> t(data);
        long long num = 0;
        MSort(data, t, 0, data.size() - 1, num);
        int s = num % 1000000007;
        return s;
    }

此代码顺利通过了OJ,且运行时间小于上面别人的代码。主要是将构造复制操作转为了下标内存拷贝操作,运行通过时间118ms。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝域小兵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值