簡體   English   中英

查找數組中k個最小數字的索引的算法

[英]Algorithm to find indexes of k smallest numbers in an array

考慮如下情況:

數組amounts跟蹤一些統計數量:

int[] amounts = { 1, 2, 5, 7, 2, 4, 8, 6 };

已知數組將始終具有固定大小(在這種情況下為8 )。 我需要找到的是給定數量k的最小元素的索引

int   k = 5;
int[] smallest = { 1, 2, 2, 4, 5 };
int[] smallestIndices = { 0, 1, 4, 5, 2 };

我以為可以使用選擇算法 ,但是我意識到這可以對數組重新排序,從而返回錯誤的索引。

我想到的另一個想法是創建一個由索引和值組成的元組數組,並按元組值對該數組進行排序。 然后,我可以提取排序數組中前k元組的索引。 但是,這似乎是一種浪費的解決方案,可能可以更輕松地完成。

有沒有一種算法可以讓我找到大小為n的數組中k最小數字的索引?

  • 請注意,我並不是在尋找最小的k 這個問題已經回答了。 我正在尋找他們的索引 因此,此問題不應標記為重復。

創建一個索引數組,即數組[0,n) ,該排序由位於主數組中該索引處的元素進行的排序。 現在,您需要做的就是從索引數組中獲取最重要的元素。

無需使用配對。

具體示例:

//imports
import static java.util.Comparator.comparing;

//method
public static int[] bottomN(final int[] input, final int n) {
    return IntStream.range(0, input.length)
            .boxed()
            .sorted(comparing(i -> input[i]))
            .mapToInt(i -> i)
            .limit(n)
            .toArray();
}

用法:

public static void main(String[] args) throws Exception {
    int[] amounts = {1, 2, 5, 7, 2, 4, 8, 6};
    System.out.println(Arrays.toString(bottomN(amounts, 5)));
}

輸出:

 [0, 1, 4, 5, 2]

因此,我們要做的只是獲取數組[0,1,2,3,4,5,6,8] ,然后根據輸入數組中元素的值在等於當前數組元素的索引處對其進行排序。 這樣就可以使用按輸入中它們的值排序的索引數組。

最后,我們修剪頂部的n並將其返回。

它取決於元素數量與要查找的元素數量的比率。 您將能夠在O(n*log(n))中將n個元素作為元組排序。 但是,如果要查找的元素數量( m< log(n) ,則可以考慮簡單地遍歷列表並收集m最小的元素。 這樣就可以保證您的復雜度為O(n*log(n)) 如果要查找的元素數量恆定,則后一種方法的復雜度將為O(n)

您需要做的是保持元素索引。 對於您給出的示例,

int[] amounts = { 1, 2, 5, 7, 2, 4, 8, 6 }

使用成對的數組重新存儲該數組,然后,

pair<int,int>[] pairs = {{0, 1},{1, 2}, {2, 5}, {3, 7}, {4, 2}, {5, 4}, {6, 8}, {7, 6}}

然后將選擇算法應用於對數組。 請注意,您應該使用該對的第二個值作為主鍵來比較一個對。 您可以返回k個最小的值及其索引。 復雜度與選擇算法相同。

如果數組大小始終很小,則可以采用更簡單的算法,但這對於較大的數組也應該很快起作用。 想法是將索引值對放入堆中,然后取出k個具有最小值的對。

public class SmallestIndicesTest {
    public static void main(String[] args) {
        // main method here as an example use case
        int[] amounts = { 1, 2, 5, 7, 2, 4, 8, 6 };
        int k = 5;
        ArrayList<Integer> smallestIndices = getNsmallestIndices(amounts, k);
        for (int i : smallestIndices) {
            System.out.println("Index i=" + i + " contains value " + amounts[i]);
        }
    }

    private static ArrayList<Integer> getNsmallestIndices(int[] t, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        PriorityQueue<Pair> queue = new PriorityQueue<>();
        for (int i=0; i<t.length; i++) {
            queue.add(new Pair(t[i], i));
        }
        for (int i=0; i<k; i++) {
            Pair nextSmallest = queue.poll();
            list.add(nextSmallest.index);
        }
        return list;
    }
}


class Pair implements Comparable<Pair> {
    public int value;
    public int index;

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

    @Override
    public int compareTo(Pair o) {
        return (this.value - o.value);
    }
}

我不確定我是否收到您的問題。 您想從數組中獲取元素的多個索引嗎? 如果是這樣,您可以嘗試創建一個新數組並在其中添加索引,因此,最小數組的第一個元素位於索引數組的第一個元素的索引處。 就像是

public static void main(String[] args) {
    int[] amounts = { 1, 2, 5, 7, 2, 4, 8, 6 };
    int   k = 5;
    int[] smallest = { 1, 2, 2, 4, 5 };
    int[] indexes = new int[k];
    for (int i = 0; i<k; i++) {
        int searchable = smallest[i];
        for (int j = 0; j<amounts.length; j++) {
            if (searchable == amounts[j]) {
                indexes[i] = j;
            }
        }
        }           
    for (int n = 0; n<indexes.length; n++) {
            System.out.println("Number " + smallest[n] + " index is " + indexes[n]);
    }
}

對於n和k的任何大小,您都可以在非常接近線性時間(技術上為O(nk),但根據您的描述n小)上執行此操作,無需花哨。

如果您要處理長列表並且擔心高速緩存的一致性,那么可以創建一個包含兩個整數作為索引和值的對象(您可以只使用一個元組,但是一個對象更干凈)。 創建一個容量為n的ArrayList,最初為空。 如果不擔心在內存中跳來跳去,那么僅由n個整數組成的數組就可以了,每個整數都保存索引並初始化為-1。 每次只需從索引中查找值即可。

遍歷k的元素。 對於每個元素

  • 檢查該值是否小於列表中的最后一個值,如果不移至下一個元素,並且對該元素完全不需要處理。
  • 如果列表長度為n,則從列表中刪除最后一項,以便為新項騰出空間。
  • 創建具有索引和值集的新對象。 (僅在使用對象時需要)
  • 掃描ArrayList直到找到較小的對象或列表的開始
  • 在定位點插入新對象(或索引)

在對主列表的單次掃描結束時,ArrayList將包含最小n個值的排序列表。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM