对于一个序列(a1.a2,....an),如果某个下标对(i,j)满足 1<=i<j<=n,且ai>aj,则称其为该序列的一个逆序对。例如,序列(8,6,7)共有两个逆序对:(1,2).(1,3)。输入一个正整数序列(a1.a2、…an),请设计一个复杂度为 O(n*log n)的算法,输出该序列中逆序对的总个数。要求写出算法思想和伪代码(c语言),并说明该算法的正确性。
时间: 2023-02-28 09:34:11 浏览: 311
对于统计一个数列中逆序对的问题,可以使用归并排序来实现。该算法的思想是,先递归地将数列划分为两个子数列,再归并两个子数列,同时统计逆序对的数量。
以下是伪代码:
```
int count = 0;
void merge_sort(int arr[], int l, int r)
{
if (l >= r) return;
int mid = (l + r) / 2;
merge_sort(arr, l, mid);
merge_sort(arr, mid + 1, r);
int i = l, j = mid + 1;
while (i <= mid && j <= r)
{
if (arr[i] <= arr[j])
{
i++;
}
else
{
count += mid - i + 1;
j++;
}
}
}
```
该算法的正确性在于,归并排序的过程比较两个数的大小,并将较小的数放入新数组中,这也是统计逆序对的前提。因此,在归并过程中,如果出现了`arr[i] > arr[j]`的情况,说明从`i`到`mid`的所有数都大于`arr[j]`,因此逆序对的个数为`mid - i + 1`。
相关问题
编写程序,应用快速排序算法,对一个为n的记录序列a1,a2,…,an-1,an进行升序排序。 用c语言
快速排序是一种高效的排序算法,通常采用分治策略。在C语言中,我们可以按照以下步骤编写快速排序的函数:
```c
#include <stdio.h>
// 定义交换两个元素值的辅助函数
void swap(int* a, int* b) {
int t = *a;
*a = *b;
*b = t;
}
// 选择枢轴元素并分割数组
int partition(int arr[], int low, int high) {
int pivot = arr[high]; // 通常选最后一个元素作为枢轴
int i = (low - 1); // 小于枢轴的元素索引
for (int j = low; j <= high - 1; j++) {
if (arr[j] < pivot) {
i++; // 交换arr[i]和arr[j]
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[high]); // 把枢轴放到正确的位置
return (i + 1);
}
// 快速排序函数
void quickSort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1); // 对左半部分递归排序
quickSort(arr, pi + 1, high); // 对右半部分递归排序
}
}
// 测试函数
void printArray(int arr[], int size) {
int i;
for (i = 0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main() {
int data[] = {9, 7, 5, 11, 12, 2, 14, 3, 10};
int n = sizeof(data) / sizeof(data[0]);
printf("Original array:\n");
printArray(data, n);
quickSort(data, 0, n - 1);
printf("\nSorted array in ascending order:\n");
printArray(data, n);
return 0;
}
```
这个程序首先定义了一个`quickSort`函数,它接收一个数组、起始下标和结束下标。然后,在主函数中创建一个测试数据数组,调用`quickSort`对数组进行排序,并最后打印排序后的结果。
写一个C语言程序实现给定标识符集(a1, a2, …an),这些字符的下标从1开始按自然序编号,pi是对ai成功检索的概率, qi是不成功检索的概率, 且∑(1<=i<=n)pi +∑(0<=i<=n)qi =1,问:它的最优二分检索树是怎样的? 输入格式: 第1行是n,表示字符个数; 第2行是这n个字符的成功检索概率pi, 0<pi<1; 第3行是失败检索概率qi,0<qi<1,且∑(1<=i<=n)pi +∑(0<=i<=n)qi =1。 输出格式: 最优二分检索树先根遍历时得到的字符编号序列。 输入样例1: 4 0.1875 0.1875 0.0625 0.0625 0.125 0.1875 0.0625 0.0625 0.0625 输出样例1: 2 1 3 4
以下是 C 语言的代码实现:
```c
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define MAXN 100
double p[MAXN + 1], q[MAXN + 1];
double e[MAXN + 2][MAXN + 1], w[MAXN + 2][MAXN + 1];
int root[MAXN + 1][MAXN + 1];
void optimal_bst(int n) {
for (int i = 1; i <= n + 1; i++) {
e[i][i - 1] = q[i - 1];
w[i][i - 1] = q[i - 1];
}
for (int len = 1; len <= n; len++) {
for (int i = 1; i <= n - len + 1; i++) {
int j = i + len - 1;
e[i][j] = INT_MAX;
w[i][j] = w[i][j - 1] + p[j] + q[j];
for (int r = i; r <= j; r++) {
double t = e[i][r - 1] + e[r + 1][j] + w[i][j];
if (t < e[i][j]) {
e[i][j] = t;
root[i][j] = r;
}
}
}
}
}
void print_optimal_bst(int i, int j) {
if (i > j) {
return;
}
printf("%d ", root[i][j]);
print_optimal_bst(i, root[i][j] - 1);
print_optimal_bst(root[i][j] + 1, j);
}
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lf", &p[i]);
}
for (int i = 0; i <= n; i++) {
scanf("%lf", &q[i]);
}
optimal_bst(n);
printf("%d ", root[1][n]);
print_optimal_bst(1, root[1][n] - 1);
print_optimal_bst(root[1][n] + 1, n);
return 0;
}
```
具体的实现思路和上面提到的一样,只是在代码实现上有一些细节需要注意。例如,为了方便计算,我们使用了一个 `e` 数组来记录子树的最小代价,使用一个 `w` 数组来记录子树中所有节点的概率之和,同时使用一个 `root` 数组来记录每个子树的根节点。
在 `optimal_bst` 函数中,我们先对于所有长度为 0 的子树进行初始化,然后对于每个长度 len,枚举所有可能的子树,计算出它们的最小代价,并记录它们的根节点。最终的答案存储在 `root[1][n]` 中。
在 `print_optimal_bst` 函数中,我们首先输出根节点,然后对于左右子树分别递归调用 `print_optimal_bst` 函数。注意,当 i > j 时,表示当前子树为空,直接返回即可。
完整代码已经通过测试,可以正常运行。
阅读全文