您要求提供O(n log n)
,我在這裏給你一個技術上的O(n)
解決方案。
正如@SayonjiNakate所暗示的那樣,使用分段樹的解決方案(我在我的實現中使用了Fenwick樹)在O(n log M)
時間運行,其中M
是數組中最大的可能值。假設M
是恆定的(嘿它是以int的大小爲界!),算法運行在O(n)
。對不起,如果你覺得我在報道複雜性時作弊,但嘿,從技術上講這是真的! = D
首先,請注意,通過反轉和取反數組,問題「左側的較小元素的數量」等同於「右側的較大元素的數量」問題。所以,在我的下面的解釋中,我只描述了「左側較小元素的數量」,我稱其爲「lesser_left_count」。
算法爲lesser_left_count:
的想法是能夠找到總比一個具體的數字更小的數字。
定義的陣列tree
與尺寸高達MAX_VALUE
,將存儲值1
爲看到號碼和0
否則。
然後,當我們遍歷陣列中,當我們看到了許多num
,只是分配值1
到tree[num]
(更新操作)。然後,數字num
的lesser_left_count是從1
到num-1
(總和操作)的總和,因爲當前位置左側的所有較小數字將被設置爲1
。
簡單吧?如果我們使用Fenwick tree,則可以在O(log M)
時間內完成更新和求和操作,其中M
是數組中的最大可能值。由於我們遍歷數組結束,總時間爲O(n log M)
,或者乾脆O(n)
如果我們把M
爲常數(在我的代碼,我將它設置爲2^20-1 = 1048575
,所以它的O(20n)
,這是O(n)
)
的唯一的缺點天真的解決方案是,它使用了大量的內存,因爲M
變得更大(我在我的代碼中設置了M=2^20-1
,這佔用了大約4MB的內存)。這可以通過將數組中的不同整數映射爲更小的整數(以保持順序的方式)來改進。通過對數組進行排序,可以簡單地在O(n log n)
中完成映射(好吧,這使得複雜度爲O(n log n)
,但是因爲我們知道n < M
,您可以將其視爲O(n)
)。因此M
的數字可以重新解釋爲「陣列中不同元素的數量」。
所以內存也不會有任何問題了,因爲如果這種改進後,你確實需要大量的內存,這意味着有數組中許多個不同的數字,和O(n)
的時間複雜度將已經過無論如何都是在普通機器中計算的高。
爲了簡單起見,我沒有在代碼中加入這種改進。
哦,既然Fenwick樹只適用於正數,我將數組中的數字轉換爲最小值1.請注意,這不會改變結果。
Python代碼:
MAX_VALUE = 2**20-1
f_arr = [0]*MAX_VALUE
def reset():
global f_arr, MAX_VALUE
f_arr[:] = [0]*MAX_VALUE
def update(idx,val):
global f_arr
while idx<MAX_VALUE:
f_arr[idx]+=val
idx += (idx & -idx)
def cnt_sum(idx):
global f_arr
result = 0
while idx > 0:
result += f_arr[idx]
idx -= (idx & -idx)
return result
def count_left_less(arr):
reset()
result = [0]*len(arr)
for idx,num in enumerate(arr):
cnt_prev = cnt_sum(num-1)
if cnt_sum(num) == cnt_prev: # If we haven't seen num before
update(num,1)
result[idx] = cnt_prev
return result
def count_left_right(arr):
arr = [x for x in arr]
min_num = min(arr)
if min_num<=0: # Got nonpositive numbers!
arr = [min_num+1+x for x in arr] # Convert to minimum 1
left = count_left_less(arr)
arr.reverse() # Reverse for greater_right_count
max_num = max(arr)
arr = [max_num+1-x for x in arr] # Negate the entries, keep minimum 1
right = count_left_less(arr)
right.reverse() # Reverse the result, to align with original array
return (left, right)
def main():
arr = [1,1,3,2,4,5,6]
(left, right) = count_left_right(arr)
print 'Array: ' + str(arr)
print 'Lesser left count: ' + str(left)
print 'Greater right cnt: ' + str(right)
if __name__=='__main__':
main()
會產生:
Original array: [1, 1, 3, 2, 4, 5, 6]
Lesser left count: [0, 0, 1, 1, 3, 4, 5]
Greater right cnt: [5, 5, 3, 3, 2, 1, 0]
,或者如果你想Java代碼:
import java.util.Arrays;
class Main{
static int MAX_VALUE = 1048575;
static int[] fArr = new int[MAX_VALUE];
public static void main(String[] args){
int[] arr = new int[]{1,1,3,2,4,5,6};
System.out.println("Original array: "+toString(arr));
int[][] leftRight = lesserLeftRight(arr);
System.out.println("Lesser left count: "+toString(leftRight[0]));
System.out.println("Greater right cnt: "+toString(leftRight[1]));
}
public static String toString(int[] arr){
String result = "[";
for(int num: arr){
if(result.length()!=1){
result+=", ";
}
result+=num;
}
result+="]";
return result;
}
public static void reset(){
Arrays.fill(fArr,0);
}
public static void update(int idx, int val){
while(idx < MAX_VALUE){
fArr[idx]+=val;
idx += (idx & -idx);
}
}
public static int cntSum(int idx){
int result = 0;
while(idx > 0){
result += fArr[idx];
idx -= (idx & -idx);
}
return result;
}
public static int[] lesserLeftCount(int[] arr){
reset();
int[] result = new int[arr.length];
for(int i=0; i<arr.length; i++){
result[i] = cntSum(arr[i]-1);
if(cntSum(arr[i])==result[i]) update(arr[i],1);
}
return result;
}
public static int[][] lesserLeftRight(int[] arr){
int[] left = new int[arr.length];
int min = Integer.MAX_VALUE;
for(int i=0; i<arr.length; i++){
left[i] = arr[i];
if(min>arr[i]) min=arr[i];
}
for(int i=0; i<arr.length; i++) left[i]+=min+1;
left = lesserLeftCount(left);
int[] right = new int[arr.length];
int max = Integer.MIN_VALUE;
for(int i=0; i<arr.length; i++){
right[i] = arr[arr.length-1-i];
if(max<right[i]) max=right[i];
}
for(int i=0; i<arr.length; i++) right[i] = max+1-right[i];
right = lesserLeftCount(right);
int[] rightFinal = new int[right.length];
for(int i=0; i<right.length; i++) rightFinal[i] = right[right.length-1-i];
return new int[][]{left, rightFinal};
}
}
這將產生相同的結果。
問題在哪裏? –
(1)請發佈您正在使用的實際代碼;明確地理解比敘述更容易理解。 (2)這可能比Stack Overflow更適合Code Review。 (3)儘管你可能能夠做出重大改進,但從N^2到N^2/2是一個線性因素,所以你仍然是O(N^2)。 – chrylis
這個問題在我看來很清楚,他要求的是一個比蠻力更好的算法 – arynaq