我們給出n組不同大小的整數。每個集合也可以包含重複。我必須找到交集。如果一個元素在所有集合中出現多次,它應該被添加到結果中。查找交集
例如,考慮有三組{0,5,5,3,4} {5,2,3,5,6} {1,3,5,5,6}。所述給定集合的交集應該是{3,5,5}
我的做法是:
1.Sort陣列。
2.比較從最小數組開始的每個元素並更新計數。
是否有更有效的方法來找到交集?
我們給出n組不同大小的整數。每個集合也可以包含重複。我必須找到交集。如果一個元素在所有集合中出現多次,它應該被添加到結果中。查找交集
例如,考慮有三組{0,5,5,3,4} {5,2,3,5,6} {1,3,5,5,6}。所述給定集合的交集應該是{3,5,5}
我的做法是:
1.Sort陣列。
2.比較從最小數組開始的每個元素並更新計數。
是否有更有效的方法來找到交集?
如果你的「套」只包含小整數,然後它們可以通過計數組成的數組來表示...例如,{5,2,3,5,6}是
index 0 1 2 3 4 5 6
count 0 0 1 1 0 2 1
這種集合的交集被計數的分鐘:
index 0 1 2 3 4 5 6
-------------
{0,5,5,3,4} 1 0 0 1 1 2 0
{5,2,3,5,6} 0 0 1 1 0 2 1
{1,3,5,5,6} 0 1 0 1 0 2 1
min 0 0 0 1 0 2 0 = {3,5,5}
如果這些值不是小整數,但它們很少,只需保留一個值的數組 - 它用作值和小整數(它們是數組的索引)之間的映射。
如果有太多的值使得每個集合的計數數組過於昂貴,則使用從值到計數的映射來表示每個「集合」,以及值的數組......然後迭代該數組生成每個值,迭代地圖以獲取計數並計算其最小值。爲此,您需要一個哈希表或二叉樹庫來實現這些映射......或者使用比C更多的現代語言中的任何一種,當然這些語言都提供這樣的集合類型。
例如,您可以爲每個數組創建一個字典,遍歷每個數組添加到他們的計數器,並添加到「全局」字典中是否檢測到新數字。然後,您從「全球」字典中選擇下一個數字(至少在一個櫃檯詞典中保證存在),然後您至少獲得所有計數器。當然,如果您在單個字典中遇到空字符,則不會將此數字添加到結果中。否則,向結果數組中添加「最小找到」數量的「數字」。有了這樣的字典結構,算法的完整複雜度約爲O(n*m)
其中M是您的集合的最大尺寸,N是它們的數量,而如果您對集合進行排序,則複雜度爲O(n*m*log(m))
,如果您的集合包含每個元素超過1000個。
我認爲把最大設置容量乘以設置的數量是不對的,因爲你最終會增加更多的內容,我會說O(n)其中n:所有組中元素的數量 – 2013-03-28 05:41:08
@KhaledAKhunaifer我們必須查詢這些集合中的每個元素,以便正確地形成結果,並且它們至多是'n * m',所以我們不能得到比這更小的O()函數。 M不是「設定容量」,它是算法開始時給出的最大值。集合容量可以大到2^32,其中集合本身的大小爲5,如示例中那樣。 – Vesper 2013-03-28 05:52:49
這裏是我的代碼,編譯在C99(不要忘記實現獲取,插入,刪除功能第一):
struct MyNode { MyNode * next; int value; int frequency; }
// returns MyNode pointer when value exist
MyNode * get(MyNode * head, int val);
// insert a new value, with frequency = 1
void insert(MyNode * head, int val);
// remove an element from the linked-list
bool remove(MyNode * head, int val);
int * intersection (int ** set, int w, int * h)
{
MyNode * head = 0;
MyNode * temp = 0;
int finalSize = 0;
int k = 0;
for (int i=0; i<w; i++)
{
for (int j=0; j<h[i]; j++)
{
temp = get(head, set[i][j]);
if (temp == 0)
{
insert(head, set[i][j]);
finalSize++;
}
else
{
temp->frequency++;
}
}
}
temp = head;
while (temp != 0)
{
if (temp->frequency != w)
{
temp = temp->next;
remove(head, temp->value);
finalSize--;
}
else
temp = temp->next;
}
int * intersection = (int*)malloc(finalSize*sizeof(int));
temp = head;
while (temp != 0)
{
intersection[k++] = temp->data;
temp = temp->next;
}
return intersection;
}
我建議你的解決方案的唯一優化是將你的數組(它們不是真正的集合,因爲它們有重複項)轉換爲鍵值字典,這樣鍵將是數組的元素,值會是發生次數。對於您的測試示例:{0,5,5,3,4} {5,2,3,5,6} {1,3,5,5,6}這些字典看起來像那樣
{0->1, 3-<1, 4->1, 5->2}
{2->1, 3->1, 5->2, 6->1}
{1->1, 3->1, 5->2, 6->1}
然後,比較從最小字典開始的字典對,如果元素同時出現在兩個字段中 - 則採用較少的字典數。 這種優化將節省處理重複所需的時間。你可以將其轉換回數組。
這似乎非常接近最佳。 – Patashu 2013-03-28 05:38:00
數學上,集合不包含重複;多重包或包可以包含重複項。 – 2013-03-28 05:43:51
對於多內核,可能是並行插入排序(當然,您的數據必須足夠大才能使其值得)。 – kfmfe04 2013-03-28 06:05:01