给定一个数组,为每个元素找出小于它的元素总数,它们显示在它的右边

时间:2012-02-29 08:35:56

标签: arrays algorithm

我之前发过了一个问题,Given an array, find out the next smaller element for each element 现在,我试图知道,如果有任何方法可以找出“给定一个数组,对于每个元素,找出比它更小的元素总数,它出现在它的右边” 例如,阵列[4 2 1 5 3]应该产生[3 1 0 1 0] ??

[编辑] 我已经找到了解决方案,请看一下,如果有任何错误,请告诉我。

1制作一个平衡的BST插入元素,从右到左遍历数组

2 BST的制作方式是每个元素都保持以该元素为根的树的大小

3现在,当您搜索插入任何元素的正确位置时,如果向右移动,请考虑根据左侧兄弟+ 1(对于父级)生成的子树的总大小 现在,计数是在插入元素时计算的,并且我们正在从右向左移动,我们得到的元素的精确数量小于它后面出现的给定元素。

10 个答案:

答案 0 :(得分:10)

它可以在O(n log n)中解决。

如果在BST中存储了在搜索节点时从该节点生根的子树的元素数(从根节点到达),则可以计算大于/小于路径中元素的数量:

int count_larger(node *T, int key, int current_larger){
    if (*T == nil)
        return -1;
    if (T->key == key)
        return current_larger + (T->right_child->size);
    if (T->key > key)
        return count_larger(T->left_child, key, current_larger + (T->right_child->size) + 1);
    return count_larger(T->right_child, key, current_larger)
}

**例如,如果this是我们的树而我们正在搜索键3,则会调用count_larger:

- > (节点2,3,0)
- > (节点4,3,0)
---> (节点3,3,2)

,最终答案是预期的2。

答案 1 :(得分:3)

假设数组是6,-1,5,10,12,4,1,3,7,50

步骤

1.我们开始从数组的右端构建一个BST。因为我们关注任何元素的所有元素。

2.假设我们已经形成了部分解决方案树到10。

enter image description here

3.当插入5时,我们进行树遍历并插入4的右侧。 请注意,每次遍历任何节点的右侧时,我们都会增加1并添加no。该节点的左子树中的元素。 例如:
50为0     7为0         对于12,它是1个右侧traversel + leftsubtree大小7 = 1 + 3 = 4
        与上述相同的10 对于4,它是1 + 1 = 2

在构建bst时,我们可以通过简单地维护与其对应的变量并在每次节点向左移动时将其递增1来轻松地维护每个节点的左子树大小。
因此解决方案平均情况为O(nlogn)。

我们可以使用其他优化,例如预先确定数组是否按递减顺序排序 按降序查找元素组,将它们视为单个元素。

答案 2 :(得分:2)

我认为可以使用quicksort的修改版本在O(nlog(n))中执行此操作。基本上每次将元素添加到less时,都要检查原始数组中的元素等级是否优于当前数据透视表的等级。它可能看起来像

oldrank -> original positions 
count -> what you want
function quicksort('array')
  if length('array') ≤ 1
      return 'array'  // an array of zero or one elements is already sorted
  select and remove a pivot value 'pivot' from 'array'
  create empty lists 'less' and 'greater'
  for each 'x' in 'array'
      if 'x' ≤ 'pivot' 
         append 'x' to 'less'
         if oldrank(x) > = oldrank(pivot)  increment count(pivot)
      else 
         append 'x' to 'greater'
         if oldrank(x) <  oldrank(pivot)  increment count(x) //This was missing
  return concatenate(quicksort('less'), 'pivot', quicksort('greater')) // two recursive calls

修改

实际上可以使用任何基于比较的排序算法来完成。每当你比较两个元素,使两者之间的相对顺序发生变化时,你就会增加更大元素的计数器。

维基百科中的原始伪代码。

答案 3 :(得分:2)

  

您也可以使用二进制索引树

int tree[1000005];
void update(int idx,int val)
{
   while(idx<=1000000)
   {
       tree[idx]+=val;
       idx+=(idx & -idx);
   }
}

int sum(int idx)
{
    int sm=0;
    while(idx>0)
    {
       sm+=tree[idx];
       idx-=(idx & -idx);
    }
    return sm;
}

int main()
{
    int a[]={4,2,1,5,3};
    int s=0,sz=6;
    int b[10];
    b[sz-1]=0;
    for(int i=sz-2;i>=0;i--)
    {
        if(a[i]!=0)
        {
            update(a[i],1);
            b[i]=sum(a[i]-1)+s;
        }
        else s++;
    }
    for(int i=0;i<sz-1;i++)
    {
       cout<<b[i]<<" ";
    }
   return 0;
}

答案 4 :(得分:1)

//some array called newarray
for(int x=0; x <=array.length;x++)
{
for(int y=x;y<array.length;y++)
{
if(array[y] < array[x])
{
newarray[x] = newarray[x]+1;
}
}
}
像这样的东西,其中array是你的输入数组,newarray你的输出数组 确保正确初始化所有内容(0代表newarrays值)

答案 5 :(得分:1)

另一种不使用树的方法。

  1. 构造另一个排序数组。例如,对于输入数组{12,1,2,3,0,11,4},它将是{0,1,2,3,4,11,12}
  2. 现在比较输入数组中每个元素与排序数组的位置。例如,第一个数组中的12个是0索引,而排序数组则是6个
  3. 完成比较后,从两个数组中删除元素

答案 6 :(得分:0)

您也可以使用数组而不是二叉搜索树。

def count_next_smaller_elements(xs):
    # prepare list "ys" containing item's numeric order
    ys = sorted((x,i) for i,x in enumerate(xs))
    zs = [0] * len(ys)

    for i in range(1, len(ys)):
        zs[ys[i][1]] = zs[ys[i-1][1]]
        if ys[i][0] != ys[i-1][0]: zs[ys[i][1]] += 1

    # use list "ts" as binary search tree, every element keeps count of
    # number of children with value less than the current element's value
    ts = [0] * (zs[ys[-1][1]]+1)
    us = [0] * len(xs)

    for i in range(len(xs)-1,-1,-1):
        x = zs[i]+1
        while True:
            us[i] += ts[x-1]
            x -= (x & (-x))
            if x <= 0: break

        x = zs[i]+1
        while True:
            x += (x & (-x))
            if x > len(ts): break
            ts[x-1] += 1

    return us

print count_next_smaller_elements([40, 20, 10, 50, 20, 40, 30])
# outputs: [4, 1, 0, 2, 0, 1, 0]

答案 7 :(得分:0)

您可以使用stl map代替BST。

从右侧开始插入。 插入元素后,找到它的迭代器:

auto i = m.find(element);

然后从m.end()中减去它。这为您提供了map中大于当前元素的元素数。

map<int, bool> m;
for (int i = array.size() - 1; i >= 0; --i) {
  m[array[i]] = true;
  auto iter = m.find(array[i])
  greaterThan[i] = m.end() - iter;
}

希望它有所帮助。

答案 8 :(得分:0)

除了使用BST之外,我们还可以通过在合并排序算法(在O(n * logn)时间)中进行一些修改来最佳地解决此问题。

如果你更仔细地观察这个问题,你可以说在问题中我们需要计算每个元素所需的反转次数,使数组按升序排序,对吗?

所以这个问题可以用Divide and Conquer范例来解决。在这里,您需要维护一个辅助数组,用于存储所需的反转计数(即,在其右侧小于它的元素)。

下面是一个python程序:

class ChildAResponse extends Response<ChildA> {}

// deserialization
HTTPClient.objectMapper.readValue(
    response, 
    ChildAResponse.class
)

时间:O(n * logn)

空间:O(n)

答案 9 :(得分:0)

修改的合并排序 :(已测试的代码)

花费O(nlogn)时间。

public class MergeSort {
    static HashMap<Integer, Integer> valueToLowerCount = new HashMap<Integer, Integer>();

    public static void main(String[] args) {
        int []                 arr = new int[] {50, 33, 37, 26, 58, 36, 59};
        int [] lowerValuesOnRight  = new int[] {4,   1,  2,  0,  1,  0,  0};

        HashMap<Integer, Integer> expectedLowerCounts = new HashMap<Integer, Integer>();
        idx = 0;
        for (int x: arr) {
            expectedLowerCounts.put(x, lowerValuesOnRight[idx++]);
        }
        
        for (int x : arr) valueToLowerCount.put(x, 0);
        
        mergeSort(arr, 0, arr.length-1);
        
        //Testing       
        Assert.assertEquals("Count lower values on right side", expectedLowerCounts, valueToLowerCount);
    }
    public static void mergeSort(int []arr, int l, int r) {
        if (r <= l) return;
        int mid = (l+r)/2;
        mergeSort(arr, l, mid);
        mergeSort(arr, mid+1, r);
        mergeDecreasingOrder(arr, l, mid, r);
    }
    public static void mergeDecreasingOrder(int []arr, int l, int lr, int r) {
        int []leftArr = Arrays.copyOfRange(arr, l, lr+1);
        int []rightArr = Arrays.copyOfRange(arr, lr+1, r+1);
        int indexArr = l;
        int i = 0, j = 0;
        while (i < leftArr.length && j < rightArr.length) {
            if (leftArr[i] > rightArr[j]) {
                valueToLowerCount.put(leftArr[i], valueToLowerCount.get(leftArr[i]) + rightArr.length - j);
                arr[indexArr++] = leftArr[i++];
            }else {
                arr[indexArr++] = rightArr[j++];
            }
        }
        while (i < leftArr.length)  { 
            arr[indexArr++] = leftArr[i++]; 
        }
        while (j < rightArr.length) {
            arr[indexArr++] = rightArr[j++];
        }
    }   
}

要查找右侧的值总数大于数组元素,只需更改一行代码即可:

if (leftArr[i] > rightArr[j])

if (leftArr[i] < rightArr[j])