c#递归问题

本文关键字:问题 递归 | 更新日期: 2023-09-27 18:03:19

我有一个Range类,我想从中减去一组Range。

我可以做得很好。

Range range1 = new Range(0,100);
Range range2 = new Range(40,60);
List<Range> differences = range1.Difference(range2);
differences[0]; // 0 to 40
differences[1]; // 60 to 100

我卡住的问题是当找到一个范围和一组范围之间的差异:

List<Range> rangeSet = new List<Range>();
rangeSet.Add(new Range(10, 30);
rangeSet.Add(new Range(25, 70);
rangeSet.Add(new Range(90, 120);
List<Range> results = range1.Difference(rangeSet);

结果应该是:

results[0]; // 0 to 10
results[1]; // 70 to 90

算法应该从range和rangeSet[0]之间的差中获取结果,然后将该结果用作下一次迭代的输入,等等,最后返回作为结果的范围集。

我猜这将需要一个递归的方法,但我只是不能得到我的头绕????

"澄清"。这个问题比我给出的值域的例子更复杂。这是我想通过的考试。

    [Test]
    public void BandCanReturnDifferenceWithASetOfOtherBands()
    {
        var bandA = Band.Create<ChargeBand>("Band A");
        bandA.AddMonth(EMonth.January);
        bandA.AddMonth(EMonth.February);
        bandA.AddDayOfWeek(DayOfWeek.Monday);
        bandA.AddDayOfWeek(DayOfWeek.Tuesday);
        bandA.AddTimeRange(5, 0, 11, 0);
        var bandA2 = Band.Create<ChargeBand>("Band A2");
        bandA2.AddMonth(EMonth.January);
        bandA2.AddMonth(EMonth.December);
        bandA2.AddDayOfWeek(DayOfWeek.Wednesday);
        bandA2.AddDayOfWeek(DayOfWeek.Friday);
        bandA2.AddTimeRange(1, 0, 10, 0);
        bandA2.AddTimeRange(12, 0, 24, 0);
        IList<IBand> bands = new List<IBand>();
        bands.Add(bandA);
        bands.Add(bandA2);
        var bandB = Band.CreateAllTimesBand<ChargeBand>("Band B");
        // this should result in
        var bandR1 = Band.Create<ChargeBand>("R1");
        var bandR2 = Band.Create<ChargeBand>("R2");
        var bandR3 = Band.Create<ChargeBand>("R3");
        bandR1.AddMonth(EMonth.January);
        bandR1.AddMonth(EMonth.February);
        bandR1.AddDayOfWeek(DayOfWeek.Monday);
        bandR1.AddDayOfWeek(DayOfWeek.Tuesday);
        bandR1.AddTimeRange(0, 0, 5, 0);
        bandR1.AddTimeRange(11, 0, 24, 0);
        bandR2.AddMonth(EMonth.January);
        bandR2.AddMonth(EMonth.December);
        bandR2.AddDayOfWeek(DayOfWeek.Wednesday);
        bandR2.AddDayOfWeek(DayOfWeek.Thursday);
        bandR2.AddTimeRange(0, 0, 1, 0);
        bandR2.AddTimeRange(10, 0, 12, 0);
        bandR3.AddMonth(EMonth.March);
        bandR3.AddMonth(EMonth.April);
        bandR3.AddMonth(EMonth.May);
        bandR3.AddMonth(EMonth.June);
        bandR3.AddMonth(EMonth.July);
        bandR3.AddMonth(EMonth.August);
        bandR3.AddMonth(EMonth.September);
        bandR3.AddMonth(EMonth.October);
        bandR3.AddMonth(EMonth.November);
        bandR3.SetAllDays();
        bandR3.AddTimeRange(0, 0, 24, 0);

        //                              J,F,M,A,M,J,J,A,S,O,N,D - M,T,W,T,F,S,S - (00:00,24:00)
        //                              J,F                     - M,T           - (05:00,11:00)              
        //                              J,                    D -     W   F     - (01:00,10:00),(12:00,24:00)

        IList<IBand> expectedResults = new List<IBand>();
        expectedResults.Add(bandR1); // J,F - M,T             - (00:00,05:00),(11:00,24:00)
        expectedResults.Add(bandR2); // J,D - W,F             - (00:00,01:00),(10:00,12:00)
        expectedResults.Add(bandR3); // M,A,M,J,J,A,S,O,N     - (00:00,24:00)
        var actualResults = bandB.Difference(bands);
        Assert.AreEqual(expectedResults.Count, actualResults.Count);
        foreach (var result in actualResults)
        {
            Assert.IsTrue(expectedResults.Contains(result));
        }
    }

对不起,如果我没有意义,但我很难解释。

c#递归问题

同意递归算法是不必要的。你只需要一些基本的集合操作,这很容易做到。其中最难的是减法操作,即从单个范围中删除一个或多个范围。一旦你有了这个,你需要一个可以再次标准化范围的联合。

下面是一个工作示例:

    [System.Diagnostics.DebuggerDisplay("Range({Start} - {End})")]
    public class Range : IEnumerable<int>
    {
        public int Start, End;
        public Range(int start, int end)
        {
            Start = start;
            End = end;
        }
        public IEnumerator<int> GetEnumerator()
        {
            for (int i = Start; i <= End; i++)
                yield return i;
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        { return GetEnumerator(); }
        public IEnumerable<Range> Subtract(IEnumerable<int> removed)
        {
            IEnumerator<int> add = this.GetEnumerator();
            IEnumerator<int> rem = removed.GetEnumerator();
            bool hasmore = rem.MoveNext();
            while (add.MoveNext())
            {
                int start = add.Current;
                int end = start;
                while (hasmore && rem.Current < start)
                    hasmore = rem.MoveNext();
                if(!hasmore)
                {
                    while (add.MoveNext())
                        end = add.Current;
                    yield return new Range(start, end);
                    yield break;
                }
                if(rem.Current == start)
                {
                    hasmore = rem.MoveNext();
                    continue;
                }
                while (add.MoveNext())
                {
                    if (add.Current == rem.Current)
                    {
                        hasmore = rem.MoveNext();
                        break;
                    }
                    end = add.Current;
                }
                if (end >= start)
                    yield return new Range(start, end);
            }
        }
    }
    public static IEnumerable<int> UnionRanges(this IEnumerable<Range> ranges)
    {
        int pos = int.MinValue;
        foreach(Range r in ranges.OrderBy(x => x.Start))
        {
            pos = Math.Max(pos, r.Start);
            for (; pos <= r.End; pos++)
                yield return pos;
        }
    }
    public static IEnumerable<Range> CreateRanges(this IEnumerable<int> values)
    {
        Range r = null;
        foreach (int val in values)
        {
            if (r == null)
                r = new Range(val, val);
            else if (val == r.End + 1)
                r.End++;
            else
            {
                yield return r;
                r = new Range(val, val);
            }
        }
        if (r != null)
            yield return r;
    }
    public static void Main()
    {
        Range range1 = new Range(0, 100);
        Range range2 = new Range(40, 60);
        IEnumerable<Range> diff1 = range1.Subtract(range2);
        Console.WriteLine("Subtract 40-60 from 0-100:");
        foreach (Range r in diff1)
            Console.WriteLine("{0} ~ {1}", r.Start, r.End);
        List<Range> rangeSet = new List<Range>();
        rangeSet.Add(new Range(10, 30));
        rangeSet.Add(new Range(25, 70));
        rangeSet.Add(new Range(90, 120));
        Console.WriteLine("Normalized ranges to remove:");
        foreach (Range r in rangeSet.UnionRanges().CreateRanges())
            Console.WriteLine("{0} ~ {1}", r.Start, r.End);
        IEnumerable<Range> diff2 = range1.Subtract(rangeSet.UnionRanges());
        Console.WriteLine("Results:");
        foreach (Range r in diff2)
            Console.WriteLine("{0} ~ {1}", r.Start, r.End);
    }

上面的程序产生以下输出:

Subtract 40-60 from 0-100:
0 ~ 39
61 ~ 100
Normalized ranges to remove:
10 ~ 70
90 ~ 120
Results:
0 ~ 9
71 ~ 89
Press any key to continue . . .

剩下的主要问题是示例中的栅栏柱错误。您需要确定该范围是否包含结束符,然后进行相应的调整。

我要注意,前面的程序并不打算用于"生产"…这只是一个如何解决相关问题的例子。更好的实现可以联合和减去范围,而不迭代范围中的项。概念还是一样的,构建set操作并从那里开始。

顺便说一句,如果你只有几百个项目,我会使用HashSet或Dictionary,然后继续生活;)

你可以试试:

        List<Range> rangeSet = new List<Range>();
        rangeSet.Add(new Range(10, 30));
        rangeSet.Add(new Range(25, 70));
        rangeSet.Add(new Range(90, 120));
        List<Range> results = new List<Range>();
        Range currentRange = rangeSet.First();
        foreach (var x in rangeSet.Skip(1))
        {
            results.Add(x.Difference(currentRange));
            currentRange = x;
        }