博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【算法概论】分治算法:计算数组中的逆序对
阅读量:4175 次
发布时间:2019-05-26

本文共 4579 字,大约阅读时间需要 15 分钟。

题目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;}

 

转载地址:http://evtai.baihongyu.com/

你可能感兴趣的文章
牛客网 跳石板
查看>>
牛客网 最大的奇约数
查看>>
python大坑:AttributeError: 'module' object has no attribute 'Workbook'
查看>>
python 协程
查看>>
在写计算器时学到的
查看>>
小Q的歌单
查看>>
牛客网 计算机网络 选择题及知识点 (1)
查看>>
0-1背包问题
查看>>
TCP-IP详解卷1:协议 学习笔记(5) RARP ICMP
查看>>
Java核心技术 卷I 基础知识 学习笔记(3)
查看>>
TCP-IP详解卷1:协议 学习笔记(6) Ping
查看>>
Java核心技术 卷I 基础知识 学习笔记(4)
查看>>
Java核心技术 卷I 基础知识 学习笔记(5)
查看>>
Java核心技术 卷I 基础知识 学习笔记(6)
查看>>
微服务架构与实践 学习笔记(1)
查看>>
Java核心技术 卷I 基础知识 学习笔记(7)
查看>>
IDEA使用之让maven项目自动依赖jar包
查看>>
Java核心技术 卷I 基础知识 学习笔记(8)
查看>>
Java核心技术 卷I 基础知识 学习笔记(9)
查看>>
Intellij IDEA 创建资源文件夹 source folder
查看>>