😋题目描述:
给定一个整数数组A=(ao,a1,…,an-1),若岗且ai>aj,则
思路:
从第一个数开始枚举,找到后面所有小于当前的数。
比如:数组是a[5]=3 1 2 5 4
第一步:找到比3大的所有数(但必须在3后面找)
(3,5) ,(3,4)
第二步:找到比1大的所有数(必须在1后面找)
没有
第三步:找到比2大的所有数
没有
第四步:找到所有比5大的数
(5,4)
综上:该数组的所有逆序对就是:(3,5)(3,4)(5,4)
#include
#include
#include
using namespace std;
const int N = 1010;
int w[N]; // 存储数组
int n;
int main()
{
cout << "输入数组长度:";
cin >> n;
cout << "输入数组元素:";
for (int i = 1; i <= n; i++) //记录数字
cin >> w[i];
int res = 0;
for (int i = 1; i < n; i++) //枚举逆序对的第一个数
for (int j = i + 1; j <= n;j++) //枚举逆序对的第二个数
{
if (w[i] > w[j])
{
cout << "(" << w[i] << "," << w[j] << ") ";
res++;
}
}
cout << endl<< res;
return 0;
}
2.分治法
分治法的思想:分而治之,将一个大问题分解成若干个小问题。通过解决小问题,从而解决大问题。关于大问题和小问题的关系,有下面几点
对于这道题,分析是这样的:
这道题目使用暴力做法的根本原因就在于数组是“无序”的。
如果将数组一分为二,分成两个小数组,两个小数组都是有序的(从小到大)。那么问题就很简单了。具体看下面的模拟:
假设一个数组:8 9 11 14 9 10 11 13

由此可以发现,只要每次将两个子数组排好序,那么就可以找到两个子数组的所有逆序对。两个子数组合并和成为新的数组(有序)。这样就保证没有遗漏:
数组的逆序对=(左半部分的逆序对+右半部分的逆序对)+左右合并数组的逆序对(左半部分一个数,右半部分一个数组成的逆序对)
先从最后一次划分开始,[a4]和[a5]比较,如果a4>a5,那么逆序对+1,并且将a4,a5从小到大排序。那么子数组[a4 a5]排序完成且找到该区间的所有逆序对。此时可以发现[a3],[a4 a5]都是有序的,那么继续找逆序对且将两个区间进行排序。得到[a3 a4 a5]数组是有序的且该数组的所有逆序对都找到。以此类推。。。讲的不是很清楚,看代码更容易懂(个人感觉)
#include
#include
#include
using namespace std;
const int N = 1010;
int w[N]; //存储数组
int n;
int merge(int left,int mid,int right) //合并
{
int temp[1010];//临时数组
memset(temp, 0, sizeof(temp)); //数组初始化0
for (int i = left; i <= right; i++)
temp[i] = w[i];
//下面的步骤是归并排序的核心
//将两个有序的区间合并为一个,边合并的同时边累加逆序数对
int sum = 0;
int i = left, j = mid + 1;
int k = left;
while(i<=mid&&j<=right) //哪个小哪个放进数组,然后指针后移。
{
if (temp[i] > temp[j])//枚举后半部分,如果左边当前的值大于右边的值,那么从左边当前的值到后面的所有值多可以组成逆序对
{
sum += mid - i + 1;
w[k++] = temp[j];
j++;
}
else
{
w[k++] = temp[i];
i++;
}
}
while (i <= mid) //左半部分还有值没有放进数组
w[k++] = temp[i++];
while (j <= right) //右半部分还有值没有放进数组
w[k++] = temp[j++];
return sum;
}
int divide(int left,int right) //divide的中文意思是分开.为什么是int,我们想要记录多少逆序数对,那么每次返回的值都是逆序数对个数
{
//将区间分为两个部分,不停二分,最后合并
//凡是递归,先考虑边界,考虑边界的意义是什么
if (left >= right) //当区间只有一个值,就不能分了,不和规定。因为我们是需要不同的二分区间,一个单位长度无法二分。
return 0;
int mid = (left + right) >> 1; //等价(left+right)/2
int a=divide(left, mid); //左边的逆序数对
int b=divide(mid + 1, right); //右边的逆序数对
int c = merge(left, mid, right); //整个区间的逆序数对(感觉有点像区间dp的思维)
return a + b + c;
}
int main()
{
cout << "输入数组长度:";
cin >> n;
cout << "输入数组:";
for (int i = 1; i <= n; i++)
cin >> w[i];
//先分再合。从最底层合的时候就开始排序。这样就保证每一次合并数组都是有序的。
int res = 0;
res = divide(1, n);
for (int i = 1; i <= n; i++)
cout << w[i] << " ";
cout << endl;
cout << "数组的逆序对数量是:" << res << endl;
return 0;
}
😋题目描述: 思路: 排序算法很多种,其中快速排序是效率最高的🐯(反正算法里面只要和排序有关使用快排准没错) 下面的代码里面的快排函数就是百分之百没有错误的快速排序模板,可以直接背诵,效率极高 新思路! 再总结一下,假设有n(假设n是奇数)个数,只要找到一个数target,它满足下面这个性质: 说明:这个代码的细节有点难理解,建议使用老师代码,参考上面思路即可
已知由n(n≥2)个正整数构成的集合A={ak}(0=#include
我们发现:
S1=a1+a2+…an/2
S2=an/2+1+…+an
这代表什么?对于S1,我们实际上不需要一个严格的排过序的数组,而只要满足an/2大于前面所有的数即可。对于S2,不需要排过序的数组,而只要满足an大于前面所有的数即可。#include