题目1:
在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
例如在数组{7,5,6,4}中,共存在5组逆序对。
最直观的方法就是直接求解了,依次扫描数组中的各个数字,并将其与其后的数字作比较。
还有一种方法就是,利用分治的思想:
比如数组data = {7, 5, 6, 4};我们可以将data分解,一直分解到每个子数组只有一个元素,然后,一边比较一边归并,如下图所示:
1、先来复习一下归并排序:
#include
using namespace std;
void mergeSort(int data[], int head, int tail);
void merge(int data[], int head, int mid, int tail);
int main()
{
//计算逆序对的数组
int data[4] = { 7, 5, 6, 4 };
int len = 4;
mergeSort(data, 0, len - 1);
for (int i = 0; i < 4; ++i)
{
cout << data[i] << ' ';
}
cout << endl;
return 0;
}
void mergeSort(int data[], int head, int tail)
{
if (head < tail)
{
int mid = (head + tail) / 2;
mergeSort(data, head, mid);
mergeSort(data, mid + 1, tail);
merge(data, head, mid, tail);
}
return;
}
void merge(int data[], int head, int mid, int tail)
{
//len1和len2分别表示左边序列和右边序列的长度
int len1 = mid - head + 1;
int len2 = tail - mid;
int *L = new int[len1];
int *R = new int[len2];
//分别给数组L和R赋值
for (int i = 0, m = head; i < len1; ++i, ++m)
{
L[i] = data[m];
}
for (int j = 0, n = mid + 1; j < len2; ++j, ++n)
{
R[j] = data[n];
}
//比较大小,从小到大排列
int i = 0, j = 0, k = head;
for ( ; i < len1 && j < len2; ++k) //i和j分别指向数组L和R的下标,k为指向数组data的下标
{
if (L[i] > R[j])
{
data[k] = R[i];
++j;
}
else if (L[i] < R[j])
{
data[k] = L[i];
++i;
}
else
{
data[k] = L[i];
i += 1;
k += 1;
data[k] = R[j];
j += 1;
}
}
//将剩下的元素存入data中
if (i == len1)
{
while (j < len2)
{
data[k++] = R[j++];
}
}
if (j == len2)
{
while (i < len1)
{
data[k++] = L[i++];
}
}
delete L; //new的空间最后记得delete掉!
delete R;
return;
}
2、在其基础上补充计算逆序对的代码:
data下标从小到大填充:
#include
using namespace std;
void mergeSort(int data[], int head, int tail);
void merge(int data[], int head, int mid, int tail);
int cnt = 0; //用来记录逆序对的个数
int main()
{
//计算逆序对的数组
int data[4] = { 7, 5, 6, 4 };
int len = 4;
mergeSort(data, 0, len - 1);
for (int i = 0; i < 4; ++i)
{
cout << data[i] << ' ';
}
cout << endl;
cout << cnt << endl;
return 0;
}
void mergeSort(int data[], int head, int tail)
{
if (head < tail)
{
int mid = (head + tail) / 2;
mergeSort(data, head, mid);
mergeSort(data, mid + 1, tail);
merge(data, head, mid, tail);
}
return;
}
void merge(int data[], int head, int mid, int tail)
{
//len1和len2分别表示左边序列和右边序列的长度
int len1 = mid - head + 1;
int len2 = tail - mid;
int *L = new int[len1];
int *R = new int[len2];
//分别给数组L和R赋值
for (int i = 0, m = head; i < len1; ++i, ++m)
{
L[i] = data[m];
}
for (int j = 0, n = mid + 1; j < len2; ++j, ++n)
{
R[j] = data[n];
}
//比较大小,从小到大排列
int i = 0, j = 0, k = head;
for (; i < len1 && j < len2; ++k) //i和j分别指向数组L和R的下标,k为指向数组data的下标
{
if (L[i] > R[j])
{
data[k] = R[i];
cnt += (len1-1-i+1); //L[i] > R[i],这意味着L数组中下标>=i的元素都大于R[i],因为R[i]被储存到data中后,就没有再和L数组中元素比较的机会,因此这里要加len1-1-i+1。
++j;
}
else if (L[i] < R[j])
{
data[k] = L[i];
++i;
}
else
{
data[k] = L[i];
i += 1;
k += 1;
data[k] = R[j];
j += 1;
}
}
//将剩下的元素存入data中
if (i == len1)
{
while (j < len2)
{
data[k++] = R[j++];
}
}
if (j == len2)
{
while (i < len1)
{
data[k++] = L[i++];
}
}
delete L; //new的空间最后记得delete掉!
delete R;
return;
}
data下标是从大到小填充(和上面data下标从小到大填充相比,可能会有点绕?):
#include
using namespace std;
void mergeSort(int data[], int head, int tail);
void merge(int data[], int head, int mid, int tail);
int cnt = 0; //记录逆序对的个数
int main()
{
//计算逆序对的数组
int data[4] = { 7, 5, 6, 4 };
int len = 4;
mergeSort(data, 0, len - 1);
for (int i = 0; i < 4; ++i)
{
cout << data[i] << ' ';
}
cout << endl;
cout << cnt << endl;
return 0;
}
void mergeSort(int data[], int head, int tail)
{
if (head < tail)
{
int mid = (head + tail) / 2;
mergeSort(data, head, mid);
mergeSort(data, mid + 1, tail);
merge(data, head, mid, tail);
}
return;
}
void merge(int data[], int head, int mid, int tail)
{
//len1和len2分别表示左边序列和右边序列的长度
int len1 = mid - head + 1;
int len2 = tail - mid;
int *L = new int[len1];
int *R = new int[len2];
//分别给数组L和R赋值
for (int i = 0, m = head; i < len1; ++i, ++m)
{
L[i] = data[m];
}
for (int j = 0, n = mid + 1; j < len2; ++j, ++n)
{
R[j] = data[n];
}
//比较大小,从小到大排列
int i = len1-1, j = len2-1, k = tail;
for ( ; i >= 0 && j >= 0; --k) //i和j分别指向数组L和R的下标,k为指向数组data的下标
{
if (L[i] > R[j])
{
data[k] = L[i];
cnt += (j+1); //第一个子数组中的数字大于第二个子数组中的数字,则构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的个数
--i;
}
else if (L[i] < R[j])
{
data[k] = R[j];
--j;
}
else
{
data[k] = L[i];
i -= 1;
k -= 1;
data[k] = R[j];
j -= 1;
}
}
//将剩下的元素存入data中
if (i < 0)
{
while (j >= 0)
{
data[k--] = R[j--];
}
}
if (j < 0)
{
while (i >= 0)
{
data[k--] = L[i--];
}
}
delete L; //new的空间最后记得delete掉!
delete R;
return;
}