简体   繁体   English

获取java数组中n个最大值的索引

[英]Get indices of n maximums in java array

I have an array of size 1000. How can I find the indices (indexes) of the five maximum elements? 我有一个大小为1000的数组。如何找到五个最大元素的索引(索引)?

An example with setup code and my attempt are displayed below: 设置代码和我的尝试的示例如下所示:

Random rand = new Random();
int[] myArray = new int[1000];
int[] maxIndices = new int[5];
int[] maxValues = new int[5];

for (int i = 0; i < myArray.length; i++) {
  myArray[i] = rand.nextInt();
}

for (int i = 0; i < 5; i++) {
  maxIndices[i] = i;
  maxValues[i] = myArray[i];
}

for (int i = 0; i < maxIndices.length; i++) {
  for (int j = 0; j < myArray.length; j++) {
    if (myArray[j] > maxValues[i]) {
      maxIndices[i] = j;
      maxValues[i] = myArray[j];
    }
  }
}

for (int i = 0; i < maxIndices.length; i++) {
  System.out.println("Index: " + maxIndices[i]);
}

I know the problem is that it is constantly assigning the highest maximum value to all the maximum elements. 我知道问题是它不断地为所有最大元素分配最高的最大值。 I am unsure how to remedy this because I have to preserve the values and the indices of myArray . 我不确定如何解决这个问题,因为我必须保留myArray的值和索引。

I don't think sorting is an option because I need to preserve the indices. 我不认为排序是一种选择,因为我需要保留索引。 In fact, it is the indices that I need specifically. 事实上,这是我需要的指标。

Sorting is an option, at the expense of extra memory. 排序是一种选择,以额外的内存为代价。 Consider the following algorithm. 考虑以下算法。

1. Allocate additional array and copy into - O(n)
2. Sort additional array - O(n lg n)
3. Lop off the top k elements (in this case 5) - O(n), since k could be up to n
4. Iterate over the original array - O(n)
    4.a search the top k elements for to see if they contain the current element - O(lg n)

So it step 4 is (n * lg n), just like the sort. 所以第4步是(n * lg n),就像排序一样。 The entire algorithm is n lg n, and is very simple to code. 整个算法是n lg n,编码非常简单。

Here's a quick and dirty example. 这是一个快速而肮脏的例子。 There may be bugs in it, and obviously null checking and the like come into play. 可能存在错误,显然无效检查等等发挥作用。

import java.util.Arrays; import java.util.Arrays;

class ArrayTest {

    public static void main(String[] args) {
        int[] arr = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
        int[] indexes = indexesOfTopElements(arr,3);
        for(int i = 0; i < indexes.length; i++) {
            int index = indexes[i];
            System.out.println(index + " " + arr[index]);
        }
    }

    static int[] indexesOfTopElements(int[] orig, int nummax) {
        int[] copy = Arrays.copyOf(orig,orig.length);
        Arrays.sort(copy);
        int[] honey = Arrays.copyOfRange(copy,copy.length - nummax, copy.length);
        int[] result = new int[nummax];
        int resultPos = 0;
        for(int i = 0; i < orig.length; i++) {
            int onTrial = orig[i];
            int index = Arrays.binarySearch(honey,onTrial);
            if(index < 0) continue;
            result[resultPos++] = i;
        }
        return result;
    }

}

There are other things you can do to reduce the overhead of this operation. 您还可以采取其他措施来减少此操作的开销。 For example instead of sorting, you could opt to use a queue that just tracks the largest 5. Being int s they values would probably have to be boxed to be added to a collection (unless you rolled your own) which adds to overhead significantly. 例如,您可以选择使用仅跟踪最大5的队列而不是排序。作为int它们的值可能必须被加框以添加到集合中(除非您自己滚动),这显着增加了开销。

a bit late in answering, you could also use this function that I wrote: 回答有点晚,你也可以使用我写的这个函数:

/**
  * Return the indexes correspond to the top-k largest in an array.
  */
public static int[] maxKIndex(double[] array, int top_k) {
    double[] max = new double[top_k];
    int[] maxIndex = new int[top_k];
    Arrays.fill(max, Double.NEGATIVE_INFINITY);
    Arrays.fill(maxIndex, -1);

    top: for(int i = 0; i < array.length; i++) {
        for(int j = 0; j < top_k; j++) {
            if(array[i] > max[j]) {
                for(int x = top_k - 1; x > j; x--) {
                    maxIndex[x] = maxIndex[x-1]; max[x] = max[x-1];
                }
                maxIndex[j] = i; max[j] = array[i];
                continue top;
            }
        }
    }
    return maxIndex;
}

Sorry to answer this old question but I am missing an implementation which has all following properties: 很抱歉回答这个老问题,但我错过了一个具有以下所有属性的实现:

  • Easy to read 易于阅读
  • Performant 高性能
  • Handling of multiple same values 处理多个相同的值

Therefore I implemented it: 因此我实现了它:

    private int[] getBestKIndices(float[] array, int num) {
        //create sort able array with index and value pair
        IndexValuePair[] pairs = new IndexValuePair[array.length];
        for (int i = 0; i < array.length; i++) {
            pairs[i] = new IndexValuePair(i, array[i]);
        }

        //sort
        Arrays.sort(pairs, new Comparator<IndexValuePair>() {
            public int compare(IndexValuePair o1, IndexValuePair o2) {
                return Float.compare(o2.value, o1.value);
            }
        });

        //extract the indices
        int[] result = new int[num];
        for (int i = 0; i < num; i++) {
            result[i] = pairs[i].index;
        }
        return result;
    }

    private class IndexValuePair {
        private int index;
        private float value;

        public IndexValuePair(int index, float value) {
            this.index = index;
            this.value = value;
        }
    }

Arrays.sort(myArray), then take the final 5 elements. Arrays.sort(myArray),然后取最后的5个元素。

Sort a copy if you want to preserve the original order. 如果要保留原始订单,请对副本进行排序。

If you want the indices, there isn't a quick-and-dirty solution as there would be in python or some other languages. 如果你想要索引,就没有像python或其他语言那样的快速解决方案。 You sort and scan, but that's ugly. 你排序和扫描,但这很难看。

Or you could go objecty - this is java, after all. 或者你可能会变得粗鲁 - 毕竟这是java。 Make an ArrayMaxFilter object. 创建一个ArrayMaxFilter对象。 It'll have a private class ArrayElement, which consists of an index and a value and has a natural ordering by value. 它将有一个私有类ArrayElement,它由一个索引和一个值组成,并按值自然排序。 It'll have a method which takes a pair of ints, index and value, creates an ArrayElement of them, and drops them into a priority queue of length 5. (or however many you want to find). 它将有一个方法,它接受一对int,index和value,创建它们的ArrayElement,并将它们放入长度为5的优先级队列中(或者你想要找到多少)。 Submit each index/value pair from the array, then report out the values remaining in the queue. 从数组中提交每个索引/值对,然后报告队列中剩余的值。 (yes, a priority queue traditionally keeps the lowest values, but you can flip this in your implementation) (是的,优先级队列传统上保持最低值,但您可以在实现中将其翻转)

My quick and a bit "think outside the box" idea would be to use the EvictingQueue that holds an maximum of 5 elements. 我的快速和有点“开箱即用”的想法是使用最多包含5个元素的EvictingQueue You'd had to pre-fill it with the first five elements from your array (do it in a ascending order, so the first element you add is the lowest from the five). 您必须使用数组中的前五个元素预先填充它(按升序执行,因此您添加的第一个元素是五个中最低的元素)。

Than you have to iterate through the array and add a new element to the queue whenever the current value is greater than the lowest value in the queue. 每当当前值大于队列中的最小值时,您必须迭代数组并向队列添加新元素。 To remember the indexes, create a wrapper object (a value/index pair). 要记住索引,请创建包装器对象(值/索引对)。

After iterating through the whole array, you have your five maximum value/index pairs in the queue (in descending order). 在遍历整个数组之后,队列中有五个最大值/索引对(按降序排列)。

It's a O(n) solution. 这是一个O(n)解决方案。

Here is my solution. 这是我的解决方案。 Create a class that pairs an indice with a value: 创建一个将indice与值配对的类:

public class IndiceValuePair{
    private int indice;
    private int value;

    public IndiceValuePair(int ind, int val){
        indice = ind;
        value = val;
    }
    public int getIndice(){
        return indice;
    }
    public int getValue(){
        return value;
    }
}

and then use this class in your main method: 然后在main方法中使用此类:

public static void main(String[] args){
    Random rand = new Random();
    int[] myArray = new int[10];
    IndiceValuePair[] pairs = new IndiceValuePair[5];
    System.out.println("Here are the indices and their values:");
    for(int i = 0; i < myArray.length; i++) {
        myArray[i] = rand.nextInt(100);
        System.out.println(i+ ": " + myArray[i]);
        for(int j = 0; j < pairs.length; j++){
            //for the first five entries
            if(pairs[j] == null){
                pairs[j] = new IndiceValuePair(i, myArray[i]);
                break;
            }
            else if(pairs[j].getValue() < myArray[i]){
                //inserts the new pair into its correct spot
                for(int k = 4; k > j; k--){
                    pairs[k] = pairs [k-1];
                }
                pairs[j] = new IndiceValuePair(i, myArray[i]);
                break;
            }
        }
    }
    System.out.println("\n5 Max indices and their values");
    for(int i = 0; i < pairs.length; i++){
        System.out.println(pairs[i].getIndice() + ": " + pairs[i].getValue());
    }
}

and example output from a run: 和运行的示例输出:

Here are the indices and their values:
0: 13
1: 71
2: 45
3: 38
4: 43
5: 9
6: 4
7: 5
8: 59
9: 60

5 Max indices and their values
1: 71
9: 60
8: 59
2: 45
4: 43

The example I provided only generates ten ints with a value between 0 and 99 just so that I could see that it worked. 我提供的示例只生成10个整数,其值介于0到99之间,这样我才能看到它有效。 You can easily change this to fit 1000 values of any size. 您可以轻松更改此值以适应任何大小的1000个值。 Also, rather than run 3 separate for loops, I checked to see if the newest value I add is a max value right after I add to to myArray . 另外,我没有运行3个单独的for循环,而是在添加到myArray之后检查我添加的最新值是否为最大值。 Give it a run and see if it works for you 给它一个运行,看看它是否适合你

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM