算法之逆序对
时间:2022-05-04
本文章向大家介绍算法之逆序对,主要内容包括算法之逆序对、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
算法之逆序对
逆序对问题
假设A[1..n]是一个有n个不同数的数组。若iA[j],则对偶(i, j)称为A的一个逆序对(inversion)。
- 列出数组{2, 3, 8, 6, 1}的5个逆序对
- 由集合{1, 2, ..., n}中的元素构成的什么数组具有最多的逆序对?它有多少逆序对?
- 插入排序的运行时间与输入数组中的逆序对的数量有什么关系?
- 给出一个求在n个元素的任何排列中逆序对数量的算法,最坏时间复杂度为: (Theta)(nlgn)
- 根据定义易得,逆序对为:(2, 1)、(3, 1)、(8, 6)、(8, 1)、(6, 1)
- 当数组元素恰好为n个元素从大到小排列时,拥有最多的逆序对。此时逆序对为: (n - 1) + (n -2) + (n - 3) + ... + 1 = n(n - 1) / 2
- 根据插入排序的实现过程,不难得出每次从未排序数组选择一个值arr[j]插入已排序数组的时候,所需要的移动次数,即为以arr[j]为右侧值的逆序对的个数。这个特性也可以设计出一个时间复杂度为: (Theta)((n^2))的算法。当然这种指数级别复杂度的算法我们直接PASS
- 不难想到(Theta)(nlgn)算法复杂度的归并排序。其实归并排序在分治的时候不会改变逆序对的个数。只有在合并的时候,才会因为逆序对的出现导致右侧提前被合入原数组。其实修改点主要在两个方面:
- 声明一个全局变量用来存储总的次数
- 在右侧提前被合入原数组的时候对总次数进行累加(只需要改归并排序的merge方法, 归并排序请参照http://www.cnblogs.com/Kidezyq/p/8379267.html)
具体源代码如下:
private static int count;
private static void merge(int[] arr, int startIndex, int midIndex, int endIndex) {
/**
* 合并策略:
* 1. 新建两个数组,分别存取左半部分排好序的数组和右半部分排好序的数组
* 2. 分别从左右两个数组最开始下标开始遍历,选取较小的依次放入原数组对应位置
* 3. 最终如果左右数组中有一个已经遍历完成,另一个数组所剩的元素直接放入元素组后面部分即可
*/
// STEP1
int[] leftArr = new int[midIndex - startIndex];
int[] rightArr = new int[endIndex - midIndex];
System.arraycopy(arr, startIndex, leftArr, 0, leftArr.length);
System.arraycopy(arr, midIndex, rightArr, 0, rightArr.length);
// STEP2
int k = startIndex; // 存储原数组中的下标
int i = 0; // 存储左边数组的下标
int j = 0; // 存储右边数组的下标
while (i < leftArr.length && j < rightArr.length) {
// 将较小的元素复制到元素组对应下标k,并且移动较小元素所在数组下标
if (leftArr[i] < rightArr[j]) {
arr[k++] = leftArr[i++];
} else {
// 此时说明 arr[i]到arr[leftArr.length - 1]的值都比arr[j]大,即此时以arr[j]结尾的逆序对个数为:
count += leftArr.length - i;
arr[k++] = rightArr[j++];
}
}
// STEP3
if (i < leftArr.length) {
System.arraycopy(leftArr, i, arr, k, leftArr.length - i);
} else if (j <= rightArr.length) {
System.arraycopy(rightArr, j, arr, k, rightArr.length - j);
}
}
- TensorFlow核心使用要点
- “搜一搜”直达生活服务 微信连接移动消费新场景
- Linux服务器安全登录设置记录
- Linux系统下的ssh使用(依据个人经验总结)
- 从MapX到MapXtreme2004[4]-标注AutoLabel
- Linux下锁定账号,禁止登录系统的设置总结
- 深度解析 TypeConverter & TypeConverterAttribute (一)
- 从MapX到MapXtreme2004[7]-对Table、Feature等的理解
- 互联网赋能传统装企 “科技撬动力巨大”
- Python接口自动化-8-测试报告
- http应用优化和加速说明-负载均衡
- linux负载均衡总结性说明(四层负载/七层负载)
- 从MapX到MapXtreme2004[6]-标点心得
- silverlight3中的"伪"3D
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法