2012-02-24 80 views
4

我要合併一些間隔是這樣的:自上而下範圍合併?

>>> ranges = [(30, 45), (40, 50), (10, 50), (60, 90), (90, 100)] 
>>> merge(ranges) 
[(10, 50), (60, 100)] 

我不是在CS野戰。我知道如何通過迭代來實現,但是想知道是否有更高效的「自上而下」方法來更有效地合併它們,也許使用某種特殊的數據結構?

謝謝。

回答

2

是的,有效的方法是使用interval tree

+0

嗨,賈森,我只是擡頭間隔樹,並得到了基本的想法。但是我很困惑如何使用它來合併間隔,你能給我一個關於這裏缺少的鏈接的簡要描述嗎?謝謝。 – Jingping 2012-02-24 21:00:40

+0

我能想到的唯一的事情就是構造區間樹,然後對區間進行排序,並使用最左邊的區間作爲第一個查詢,搜索樹並獲取所有相交區間,然後合併這些區域,並迭代執行,直到合併範圍不變;然後使用下一個右側間隔開始第二輪掃描。這是掃描整個樹的重疊區間的所有「子樹」的正確方法嗎? – Jingping 2012-02-24 22:19:02

3

區間樹確實有效,但它比您需要的更復雜。間隔樹是「在線」的解決方案,所以它可以讓你添加一些間隔,看工會,加入更多的時間間隔,再看看,等

如果你把所有的時間間隔前期,你可以做一些簡單的:

  1. 開始與輸入

    範圍= [(30,45),(40,50),(10,50)]

  2. 轉換範圍列表轉換的列表端點。如果你有範圍(A,B),你可以將它轉換爲兩個端點:(A,0)將是左端點,(B,1)將是正確的端點。

    端點= [(30,0),(45,1),(40,0),(50,1),(10,0),(50,1)]

  3. 排序端點

    端點= [(10,0),(30,0),(40,0),(45,1),(50,1),(50,1)]

  4. 掃描轉發到端點列表。當您看到左端點時遞增計數器,當您看到正確的端點時遞減計數器。每當計數器達到0時,就關閉當前的合併時間間隔。

該解決方案可以在幾行中實現。

+0

如果有多個重疊區間的集羣,這個工作是否會起作用? – Jingping 2012-02-24 22:16:29

+0

我真的很喜歡這個優雅的主意:) – Jingping 2012-02-24 22:24:13

+0

是的,它可以工作,無論有多少羣集。每當計數器達到零時,您就到達合併羣集的末尾。下一個端點必須是左端點(否則數據會混亂),並且它將啓動下一個合併的集羣。 – 2012-02-24 23:19:57

0

以下算法在C#做你想做的。它使用DateTime間隔範圍,但您可以根據自己的喜好進行調整。按照升序開始順序對收集進行排序後,如果下一個時間間隔的開始時間是前一個時間間隔的開始時間或之前的時間,它們會重疊,並且如果需要,可以延長結束時間。否則它們不會重疊,並且將前一個保存到結果中。

public static List<DateTimeRange> MergeTimeRanges(List<DateTimeRange> inputRanges) 
{ 
    List<DateTimeRange> mergedRanges = new List<DateTimeRange>(); 

    // Sort in ascending start order. 
    inputRanges.Sort(); 

    DateTime currentStart = inputRanges[0].Start; 
    DateTime currentEnd = inputRanges[0].End; 

    for (int i = 1; i < inputRanges.Count; i++) 
    { 
    if (inputRanges[i].Start <= currentEnd) 
    { 
     if (inputRanges[i].End > currentEnd) 
     { 
     currentEnd = inputRanges[i].End; // Extend range. 
     } 
    } 
    else 
    { 
     // Save current range to output. 
     mergedRanges.Add(new DateTimeRange(currentStart, currentEnd)); 

     currentStart = inputRanges[i].Start; 
     currentEnd = inputRanges[i].End; 
    } 
    } 

    mergedRanges.Add(new DateTimeRange(currentStart, currentEnd)); 

    return mergedRanges; 
}