淘先锋技术网

首页 1 2 3 4 5 6 7

👦个人主页zoroxs
image.png

小和问题

在一个数组中,每一个数左边比当前小的数累加起来,就是这数组的小和

image.png

思路一:暴力

暴力查找这个没啥好说的吧,双层for循环直接搞定,累加就可以了

int smallSum(vector<int>& v)
{
	int ans = 0;
	for (size_t i = 1; i < v.size(); i++)
	{
		for (size_t j = 0; j < i; j++)
		{
			ans += v[j] < v[i] ? v[j] : 0;
		}
	}
	return ans;
}

时间复杂度O(N^2)

思路二:归并排序解法

学过归并排序的朋友,应该都了解归并排序是如何变有序的,归并排序变有序的过程可以利用起来求很多问题

我们要找比当前数小的, 不妨换个思路,我们找它的右边有几个比它大的
我们来分析一下
image.png

code

int MergeSortSmallSum(vector<int>& nums)
{
	//不改变原数组,拷贝一个新数组,归并排序还要使用一个临时数组,所以直接开辟两个
	vector<int> data = nums;
	vector<int> tmp;
	tmp.resize(data.size());

	int rangeN = 1;

	int ans = 0;

	int n = data.size();
	while (rangeN < n)
	{
		for (int i = 0; i < n; i += 2 * rangeN)
		{
			int begin1 = i;
			int end1 = i + rangeN - 1;
			int begin2 = i + rangeN;
			int end2 = i + 2 * rangeN - 1;
			int k = begin1;
			//修正
			if (end1 >= n)
			{
				end1 = n - 1;
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n)
			{
				begin2 = n;
				end2 = n - 1;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}

			//归并
			int j = begin1;
			while (begin1 <= end1 && begin2 <= end2)
			{
				//如果是左组先放,就+=右边剩余个数 乘以当前值
				if (data[begin1] < data[begin2])
				{
					ans += data[begin1] * (end2 - begin2 + 1);
					tmp[j++] = data[begin1++];
				}
				else
				{
					//此处要注意: 如果相等,就要走右组,因为无法确定右边有多少个比它大
					tmp[j++] = data[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[j++] = data[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[j++] = data[begin2++];
			}


			for (; k < j; ++k)
			{
				data[k] = tmp[k];
			}
		}

		rangeN *= 2;
	}
	return ans;
} 

其实代码也比较简单,归并排序的逻辑, 然后累加了一下,关键是思路,归并的过程中
1个数为1组的时候,就没有小和
2个数为1组的时候,就可能有小和产生, 而产生小和累加之后,这已经变成有序了,下次归并就不用考虑这里

所以每次都把组内的小和求出 ,所以时间复杂度是O(N * logN)

对数器测试

这里我就只放测试代码,不放上面两个函数了,需要的可以去上面copy

//对数器测试--
//申请一个随机vector
vector<int>& getRandomVector(int maxsize)
{
	int size = rand() % maxsize + 2;
	vector<int>* v = new vector<int>;
	v->resize(size);

	for (int i = 0; i < size; ++i)
	{
		v->operator[](i) =(rand() % 100 - rand() % 100);
	}

	return *v;
}


int main(void)
{
	srand((unsigned int)time(nullptr));
	int maxsize = 100;

	int times = 10000;
	int i = 0;
	for (i = 0; i < times; i++)
	{
		vector<int>& v = getRandomVector(maxsize);
		
		if (smallSum(v) != MergeSortSmallSum(v))
		{
			cout << "error" << endl;
			break;
		}

		delete& v;
	}

	if (i == times)
		cout << "Nice" << endl;
	return 0;
}

逆序对问题(LeetCode链接)

image.png

这个题其实思路和上一题非常相似,逆序么,就是每个数右边有几个比它小的,就组成几对,累加起来就OK

我们来分析一下
image.png

code

class Solution {
public:
    int MergeSortReversePairs(vector<int>& nums)
    {
        //归并
        vector<int> data = nums;
        vector<int> tmp = data;

        int rangeN = 1;
        int n = data.size();
        int ans = 0;

        while(rangeN < n)
        {
            for(int i = 0; i < n; i += 2 * rangeN)
            {
                int begin1 = i;
                int end1 = i + rangeN - 1;
                int begin2 = i + rangeN;
                int end2 = i + 2 * rangeN - 1;
                //区间修正
                if(end1 >= n)
                {
                    end1 = n - 1;
                    begin2 = n;
                    end2 = n - 1;
                }
                else if(begin2 >= n)
                {
                    begin2 = n;
                    end2 = n - 1;
                }
                else if(end2 >= n)
                {
                    end2 = n - 1;
                }

                int index = begin1;
                int k = begin1;

                while(begin1 <= end1 && begin2 <= end2)
                {
                    //如果等于走右组
                    if(data[begin1] > data[begin2])
                    {
                        //加上右边有多少对
                        ans += (end2 - begin2 + 1);
                        tmp[index++] = data[begin1++];
                    }
                    else
                    {
                        tmp[index++] = data[begin2++];
                    }
                }

                while(begin1 <= end1)
                {
                    tmp[index++] = data[begin1++];
                }
                while(begin2 <= end2)
                {
                    tmp[index++] = data[begin2++];
                }

                for(; k < index; ++k)
                {
                    data[k] = tmp[k];
                }
            }
            rangeN *= 2;
        }
        return ans;
    }

    int reversePairs(vector<int>& nums) {

        return MergeSortReversePairs(nums);
    }
};

image.png

这道题就过了,不用写对数器了,直接在LeetCode上跑就OK