【问题标题】:Merge interval with merge distance in C#在 C# 中合并间隔与合并距离
【发布时间】:2021-01-19 07:34:25
【问题描述】:

给定一个区间列表 [start, end] 我必须根据指定的合并距离合并它们。间隔以特定顺序到达,并且当它们到达时,它应该在收到每个间隔时根据指定的合并距离合并它们。其中一些间隔将被删除(在到达流中它们将被标记为已删除)——在这种情况下,我必须将原始间隔视为从未存在过。示例:

合并距离为 7 - 在以下示例中,输入按顺序到达

我尝试了以下算法来合并它们,但我的输出与上面的示例不同。有人可以帮助我吗,我在这里缺少什么!

这是我的代码。


class Program
    {
        static void Main(string[] args)
        {
            var intervals = new List<Interval>
            {
                new Interval
                {
                    start = 1,
                    end = 20
                },
                new Interval
                {
                    start = 55,
                    end = 58
                },
                new Interval
                {
                    start = 60,
                    end = 89
                },
                new Interval
                {
                    start = 15,
                    end = 31
                },
                new Interval
                {
                    start = 10,
                    end = 15
                },
                new Interval
                {
                    start = 1,
                    end = 20
                }
            };

            var mergedIntervals = Merge(intervals, 7);

            foreach (var item in mergedIntervals)
            {
                Console.WriteLine($"[{item.start}, {item.end}]");
            }

            Console.ReadKey();
        }

        public static List<Interval> Merge(List<Interval> intervals, int mergeDistance)
        {
            var result = new List<Interval>();

            for (int i = 0; i < intervals.Count; i++)
            {
                var newInterval = new Interval(intervals[i].start, intervals[i].end);
                //while (i < intervals.Count - 1 && newInterval.end >= intervals[i + 1].start)
                while (i < intervals.Count - 1 && newInterval.end <= mergeDistance) //  intervals[i + 1].start)
                {
                    newInterval.end = Math.Max(newInterval.end, intervals[i + 1].end);
                    i++;
                }

                result.Add(newInterval);
            }

            return result;
        }
    }

 

public class Interval
    {
        public int start { get; set; }
        public int end { get; set; }

        public Interval()
        {

        }

        public Interval(int start, int end)
        {
            this.start = start;
            this.end = end;
        }
    }

【问题讨论】:

  • 对于区间[[1,4], [10, 15], [5, 9]],合并距离为5,最后的区间([5, 9])是否应该与[1,4][5, 9]或两者合并?
  • 如果您正在考虑合并距离 5,那么这里是一个示例:给定两个区间 [1,5] 和 [10,15] 以及合并距离 5,这两个区间在此范围内重叠合并距离允许它们被合并到 [1,15] 的新区间。类似地,给定两个区间 [1,5] 和 [11,15] 以及合并距离 5,您无法合并这两个区间,因为它们在合并距离上不重叠
  • 你没有回答我的问题。如果有三个区间:[1,4][10, 15]先出现,然后[5, 9],合并范围为5?输出是[[1, 9], [10, 15]][[1,4], [5, 15]] 还是[1, 15]
  • 我们可以按照到达时间排序,然后按照距离合并。
  • 你的意思是“按每个区间的开始排序”吗?这只是将整个“以特定顺序出现”的细节抛到了窗外。顺序不重要吗?

标签: c# algorithm merge intervals


【解决方案1】:

你能试试这个代码吗,工作演示代码here

 class Program
    {
        static void Main(string[] args)
        {
            var intervals = new List<Interval>
            {
                new Interval
                {
                    start = 1,
                    end = 20,
                    isAdded = true
                },
                new Interval
                {
                    start = 55,
                    end = 58,
                    isAdded = true
                },
                new Interval
                {
                    start = 60,
                    end = 89,
                    isAdded = true
                },
                new Interval
                {
                    start = 15,
                    end = 31,
                    isAdded = true
                },
                new Interval
                {
                    start = 10,
                    end = 15,
                    isAdded = true
                },
                new Interval
                {
                    start = 1,
                    end = 20,
                    isAdded = false
                }
            };

            var mergedIntervals = Merge(intervals, 7);

            foreach (var item in mergedIntervals)
            {
                Console.WriteLine($"[{item.start}, {item.end}]");
            }

            Console.ReadKey();
        }

        public static List<Interval> Merge(List<Interval> intervals, int mergeDistance)
        {
            var result = new List<IntervalGroup>();
            var group = new IntervalGroup();
            foreach (var item in intervals)
            {
                group = result.Where(c => c.Groups.Any(g =>
                Math.Abs(g.end - item.start) <= mergeDistance ||
                Math.Abs(g.end - item.end) <= mergeDistance)).FirstOrDefault();
                if (group != null && item.isAdded)
                {
                    group.Groups.Add(item);
                }
                else if(item.isAdded)
                {
                    group = new IntervalGroup();
                    group.Groups = new List<Interval>();
                    result.Add(group);
                    group.Groups.Add(item);
                }
                else if(item.isAdded == false)
                {
                    group.Groups.Remove(group.Groups.Where(c => c.start == item.start && c.end == item.end).First());
                }
            }

            var finalResult = result.Select(s => new Interval { start = s.Groups.Min(min => min.start), end = s.Groups.Max(min => min.end) });
            return finalResult.ToList();
        }
    }

    public class Interval
    {
        public int start { get; set; }
        public int end { get; set; }

        public bool isAdded { get; set; }

        public Interval()
        {

        }

        public Interval(int start, int end, bool isAdded)
        {
            this.start = start;
            this.end = end;
            this.isAdded = isAdded;
        }
    }

    public class IntervalGroup
    {
        public List<Interval> Groups { get; set; }
    }

【讨论】:

  • 感谢您的代码,但它给了我致命错误:演示中超出了执行时间限制。但是,我们需要在输出中显示下一个或传入间隔的操作列被添加或删除。
  • 不客气!在您的本地机器上尝试它可能会出现在线站点错误。能详细解释一下吗?
  • 程序的输入应该是一个文件和一个合并间隔。该文件应具有以下格式:到达时间、开始、结束、操作——您可以假设文件按到达时间排序。 Action 列是一个字符串值,可以是“ADDED”或“REMOVED”。 Removed 表示原始间隔已从输入流中删除。在上面的例子中,当在序列 6 中的动作上调用 Removed 时,它实际上是在移除序列中出现的区间
【解决方案2】:

当一个问题超出您的掌握范围时,将其分解为更小的部分并编码您确实理解的部分是非常有帮助的。这是我如何逐步分解的。

首先,我认为能够检查间隔的长度会很方便,所以让我们添加一个属性。

class Interval
{
    /* Prior code */

    public int Length => this.end - this.start;

现在让我们编写一个合并两个区间的方法:

class Interval
{
    /* Prior code */

    static public Interval Merge(Interval a, Interval b )
    {
        return new Interval(Math.Min(a.start, b.start), Math.Max(a.end, b.end));
    }

现在我们需要编写代码来确定两个区间是否能够合并。原型可能如下所示:

static public bool CanMerge(Interval a, Interval b, int mergeDistance)

我们内部需要什么逻辑?好吧,我们可以检查重叠并检查两端的合并距离,但我知道一个捷径。给定合并 A + B = C,当且仅当 C 的长度小于或等于 A + B + 合并距离的总和时才允许合并。所以我们可以这样写:

class Interval
{
    /* Prior code */

    static public bool CanMerge(Interval a, Interval b, int mergeDistance)
    {
        var merged = Merge(a,b);
        var canMerge = merged.Length <= a.Length + b.Length + mergeDistance;

        return canMerge;
    }

您可以从那里通过检查可合并项目来添加到列表中。请注意,递归是必需的,因为合并一个区间的行为可能会导致另一个区间变得可合并。

void AddToList(List<Interval> list, Interval newInterval, int mergeDistance)
{
    var target = list.FirstOrDefault( x => Interval.CanMerge(x, newInterval, mergeDistance) );
    if (target == null)
    {
        list.Add(newInterval);
        return;
    }
    list.Remove(target);
    AddToList(list, Interval.Merge(target, newInterval), mergeDistance);
}

【讨论】:

  • 谢谢@John Wu,但我如何才能返回列表以供输出?因为 AddToList 方法是递归的,也不返回任何列表。你能更新一下主函数吗?
  • 该方法会修改正在传入的列表。
猜你喜欢
  • 2012-01-15
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
  • 2021-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-22
相关资源
最近更新 更多