注意:此问题与反演计数问题不同。
数组与已排序数组的距离定义为:
dist(A)=Sumation(j-i) for any i<j that A[i]>A[j]
。简单地,它可以计算为O(n ^ 2)。我的问题是如何更改合并排序以计算O(n log n)中的距离? 例如:
input: 5 2 3 4 1
output: 16
我不想计算倒数! 此功能与反转功能不同。
input: 5 2 3 4 1
dist(A): 16
inversions(A): 7
答案 0 :(得分:0)
如果您知道如何通过合并排序计算数组inversion的数量,则可以通过更改几行代码来解决。
注意细节。
// struct to store the values
//
struct pa {
int value, index;
};
// The merge step
// A is the left part, while B is the right part
// a_cnt is the number of elements in A
void merge(pa A[], pa B[], int a_cnt, int b_cnt) {
int st = 0;
int tmp_sum = 0;
for (int i = 0; i < a_cnt; ++i) {
while (st < b_cnt && B[st].value < A[i].value) {
tmp_sum += B[st].index;
st++;
}
ans += (tmp_sum - st * A[i].index)
}
// origional merge code here
}
答案 1 :(得分:0)
您可以通过按排序顺序逐个插入数字并保留后缀和数组来解决此问题。运作方式如下:
对于数组中的每个元素,都有一对。一个包含数字本身,另一个包含数字索引。现在创建另一个数组,并尝试将其按排序顺序插入。
例如:[(5,1),(2,2),(3,3),(4,4),(1,5)]
插入5
[(5,1)]
插入2
[((2,2),(5,1)]] =>贡献(1 * 2-1-)=> 1
插入3
[(2,2),(3,3),(5,1)] =>贡献(1 * 3-1-)=> 2
插入4
[(2,2),(3,3),(4,4),(5,1)] =>贡献(1 * 4-1-)=> 3
插入1
[(1,5),(2,2),(3,3),(4,4),(5,1)] =>贡献(5 * 4-(10))=> 10 < / p>
将所有贡献加起来,您将得到16
时间复杂度:O(N * log N)
答案 2 :(得分:0)
借助二进制搜索树可以轻松完成此操作。 您需要维护右子树中存在的节点索引和右子树中存在的节点数目之和。因此,每当您插入一个新节点并且该节点朝任何节点的左侧移动时,距离都会被更新
`(no of nodes in right subtree* index of val which is to be inserted) - sum of indices of nodes present in right subtree)`
让我们逐步进行输入
5, 2, 3, 4, 1
第一个节点的值为val 5,到现在为止的距离为0;
插入2后的情况
sizeOfRightSubTree : 1
index: 1
sumOfIndicesOnRight: 0
inserted: 2, distance: 1
插入3后
sizeOfRightSubTree : 1
index: 2
sumOfIndicesOnRight: 0
inserted: 3, distance: 3
插入4后
sizeOfRightSubTree : 1
index: 3
sumOfIndicesOnRight: 0
inserted: 4, distance: 6
在插入1之后。请注意,它必须向左移动两次以到达其最终位置,因此距离更新了两次。
sizeOfRightSubTree : 1
index: 4
sumOfIndicesOnRight: 0
sizeOfRightSubTree : 3
index: 4
sumOfIndicesOnRight: 6
inserted: 1, distance: 16
以下是Java代码
public class DistanceFromSortedArray
{
class Node {
int val;
Node left;
Node right;
int index;
int sumOfIndicesOnRight;
int sizeOfRightSubTree;
Node(int num, int index)
{
this.val = num;
this.index = index;
sizeOfRightSubTree = 1;
sumOfIndicesOnRight = index;
}
void addIndexToRight(int index)
{
sizeOfRightSubTree++;
sumOfIndicesOnRight += index;
}
int distance(int index)
{
return sizeOfRightSubTree*index - sumOfIndicesOnRight;
}
}
private Node head;
private int distance;
public int calculate(int[] nums){
head = null;
distance = 0;
for(int i=0; i<nums.length; i++){
insert(nums[i], i);
}
return distance;
}
private void insert(int num, int index)
{
Node toInsert = new Node(num, index);
if(head == null){
head = toInsert;
return;
}
Node current = head;
Node previous = null;
while (current != null){
previous = current;
if(current.val > num){
distance += current.distance(index);
current = current.left;
}
else {
current.addIndexToRight(index);
current = current.right;
}
}
if(previous.val > num){
previous.left = toInsert;
}
else {
previous.right = toInsert;
}
}
}
这里有几个测试用例
@Test
public void calculate()
{
int[] nums = {5, 2, 3, 4, 1};
assertEquals(16, new DistanceFromSortedArray().calculate(nums));
}
@Test
public void reverseCalculate()
{
int[] nums = {5, 4, 3, 2, 1};
assertEquals(20, new DistanceFromSortedArray().calculate(nums));
}
@Test
public void SizeTwoCalculate()
{
int[] nums = {4, 5};
assertEquals(0, new DistanceFromSortedArray().calculate(nums));
int [] nums2 = {5, 4};
assertEquals(1, new DistanceFromSortedArray().calculate(nums2));
}
@Test
public void twistedCalculate()
{
int[] nums = {8, 3, 6, 5, 7, 1};
assertEquals(26, new DistanceFromSortedArray().calculate(nums));
}
@Test
public void AllSameCalculate()
{
int[] nums = {1, 1, 1, 1, 1, 1};
assertEquals(0, new DistanceFromSortedArray().calculate(nums));
}