[英]Efficient algorithm to find number of elements less than a query
我有兩個未排序的數組a
和b
。 對於每個元素a[i]
我需要找到元素b[j]
的數量,使得b[j] < a[i]
。 另外, b
可能包含不應計入的重復項。 兩個陣列都可能非常大。
我嘗試了(對於單個查詢x
)
public static void main(String arg[]) {
int x = 5;
int b[] = {2, 4, 3, 4, 11, 13, 17};
Arrays.sort(b);
int count = 0;
for(int i = 0; i < b.length; ++i) {
if(b[i] < x) {
if(i == 0)
++count;
else {
// this check avoids counting duplicates
if(b[i - 1] != b[i])
++count;
}
} else {
break;
}
}
System.out.println(count);
}
我的問題是,查詢中的所有元素時,這種不執行不夠好a
迭代。 我該怎么做才能加快速度?
編輯:考慮到以后的評論,我剛開始就提出了一些更新; 將我的第一句話留在底部。
因此,這里的核心方面是:
好,關於您的問題。 事實是:實際上,這只是“工作”。 那里沒有魔術。 由於您有兩個非常大的數組,因此對未排序的數據進行操作絕對是不行的。
因此,首先對兩個數組進行排序。
然后,您遍歷第一個數組,在執行此操作的同時,還要查看第二個數組:
int indexWithinB = 0;
int counterForCurrentA = 0; // and actually ALL values from a before
for (int i=0; i<a.length; i++) {
int currentA = a[i];
while (b[indexWithinB] < currentA) {
if (indexWithinB > 0) { // check required to avoid using 0-1
if (b[indexWithinB-1] != b[indexWithinB] { // avoid counting duplicates!
counterForCurrentA++;
}
}
indexWithinB++;
}
// while loop ended, this means: b[indexWithinB] == or > currentA
// this also means: counterForCurrentA ... should have the correct value
}
上面顯然是偽代碼。 它旨在使您繼續前進; 那里很可能有細微的錯誤。 例如,正如安德里亞斯(Andreas)所指出的:還需要對上述內容進行增強以檢查b.length。 但這留給讀者練習。
這就是我所說的“正常工作”的意思:您只需要坐下來,編寫測試用例並完善我的算法草稿,直到它為您完成工作即可。 如果您發現很難一開始就編寫程序,則拿一張紙,放下兩個帶有數字的數組...,然后手動進行計數。
最后提示:我建議編寫大量的單元測試來測試您的算法(這類內容非常適合單元測試); 並確保您在此類測試中擁有所有重要案例。 您想要在使用10 ^ 5元素數組之前100%確保算法正確!
和這里一樣,原始的答案:
簡單地說:迭代和計數是解決此問題的最有效方法。 因此,在上述情況下,不進行排序可能會縮短整體執行時間。
那里的邏輯真的很簡單:為了知道小於x的數字計數,您必須查看所有這些數字。 因此,您必須迭代整個數組(當該數組未排序時)。
因此,給定您的初始聲明,沒有其他事情了:迭代並計數。
當然,如果您必須多次進行計數...可能值得一開始對數據進行排序。 因為這樣您就可以使用二進制搜索 ,並且獲得該計數就可以在不迭代所有數據的情況下尋找工作。
並且:是什么讓您認為迭代具有10 ^ 5個元素的數組是一個問題? 換句話說:您只是擔心潛在的性能問題,還是真正的性能問題? 您會看到,有時可能必須創建並填充該數組。 當然,這比簡單的for循環對條目進行計數要花費更多的時間(和資源)。 老實說:除非我們使用的是小型嵌入式設備... 10 ^ 5個元素...甚至在使用稍微陳舊的硬件時也幾乎沒有 。
最后:當您擔心運行時時 ,簡單的答案是:對輸入數據進行切片,並使用2,4、8 ...線程並行計算每個切片! 但是如前所述:在編寫該代碼之前,我將進行一些性能分析,以確保您確實必須為此花費寶貴的開發時間。 不要解決假設的性能問題; 專注於對您或您的用戶真正重要的內容!
將數組中的每個項目與x共同映射將花費O(n)時間。 對數組進行排序將得到O(n log n),然后可以使用二進制搜索,即O(log n),則總數為O(n log n)。 因此,最有效的方法也是簡單的方法-只需遍歷數組並將每個項目與x進行比較。
public static void main(String arg[] ){
int b[]={2, 4, 3, 4, 11, 13, 17};
int x=5;
int count=0;
for(int i=0;i<b.length;i++){
if(b[i]<x){
count++;
}
}
System.out.println(count);
}
我建議您考慮使用以下方法,但是僅當b
數組具有非負數時,該方法才有效。 即使未對輸入數組( a
和b
)進行排序,該算法也有效。
偽碼
b
的max
元素。 max + 1
的新數組c
,並將1
放在位置c[b[i]]
。 創建一個大小為max + 1
的新數組d
,並將其填充如下:
d[0]=0;
d[i]=d[i-1] + c[i];
創建一個大小為n
的新數組e
,並將其填充如下:
if(a[i] > max) then e[i] = last(d)
otherwise e[i]=d[a[i]-1];
e
數組表示解決方案:它在第i個位置包含b
數組的編號計數器,其數量低於數組a
的第i個元素。 此示例應比偽代碼更具解釋性:
a = [5, 1, 4, 8, 17, 12, 22]
b = [2, 4, 3, 4, 11, 13, 17]
c = [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1]
d = [0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5, 5, 6]
e = [3, 0, 2, 3, 5, 4, 6]
復雜
Steps 1, 2 and 4 are O(n).
Step 3 is O(max(b))
如果輸入數組b
僅包含“短”數字(max(b)的大小為n
的相同順序),則算法以O(n)
執行。 可以對算法進行優化,以創建大小為max-min+1
的數組,並為小於min(b)
a
數組的所有元素考慮計數器0
。
一個簡單的java實現:
int a[] = {5, 1, 4, 8, 17, 12, 22};
int b[] = {2, 4, 3, 4, 11, 13, 17};
int max = Arrays.stream(b).max().getAsInt();
int c[] = new int[max+1];
int d[] = new int[max+1];
int e[] = new int[a.length];
for(int i=0;i<b.length;i++){
c[b[i]]=1;
}
for(int i=1;i<c.length;i++){
d[i] = d[i-1] + c[i];
}
for (int i = 0; i<a.length;i++){
e[i]=(a[i]>max)?d[d.length-1]:d[a[i]-1];
}
System.out.println(Arrays.toString(a));
System.out.println(Arrays.toString(b));
System.out.println(Arrays.toString(c));
System.out.println(Arrays.toString(d));
System.out.println(Arrays.toString(e));
對於更大的排序集,我們需要使用分而治之原理來加快搜索速度。這是我的解決方案,具有O(logn)時間復雜度和O(n)空間復雜度。
public static void main(String arg[]) {
int x = 5;
int b[] = {2, 4, 3, 4, 11, 13, 17};
int high = b.length - 1;
int low = 0;
while (high >= low) {
int mid = (high + low) / 2;
if (b[mid] < x)
low = mid + 1;
else
high = mid - 1;
}
System.out.println(low);
}
這應該是一個可能的解決方案。 “昂貴”的任務是對列表進行排序。 Bost列表必須在for循環之前排序。 確保使用快速機制執行排序。 解釋說,對數組/數組列表進行排序是一項非常昂貴的操作,尤其是當您必須對許多值進行排序時。
public static void main(String[] args) throws IOException {
// int x = 5;
int a[] = { 1, 2, 3, 4, 5 };
int b[] = { 2, 4, 3, 4, 11, 13, 17 };
List<Integer> listA = new LinkedList<>();
for (int i : a) {
listA.add(i);
}
List<Integer> listB = new LinkedList<>();
for (int i : b) {
listB.add(i);
}
Collections.sort(listA);
Collections.sort(listB);
int smallerValues = 0;
int lastValue = 0;
Iterator<Integer> iterator = listB.iterator();
int nextValue = iterator.next();
for (Integer x : listA) {
while (nextValue < x && iterator.hasNext()) {
lastValue = nextValue;
nextValue = iterator.next();
if (nextValue > lastValue) {
smallerValues++;
}
}
System.out.println(x + " - " + smallerValues);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.