实施自定义Int + Range List解决方案

Windowsgm

我想知道是否有人可以提出一种以更节省内存的方式实现数字数组的方法,该方法将自动将自身组织为范围。例;

List testList = new List{1,2,3,4,5,6,7...};

List<Range> testList = new List<Range>{1-3000,3002,4000-5000...};

以前,我问过一个问题只是为了确认这是否实际上是一种内存效率更高的选择。但是,此问题与实际应用有关,如何实现此范围列表解决方案。

索引阵列存储内存

我想这可能需要成为一个自定义列表解决方案,将整数和范围混合在一起。我在想能够将.Add([int])添加到列表中,这时它将确定该值是否会导致添加范围或将int值简单地添加到列表中。

RangeList rangeList = new RangeList{1, 4, 7-9};
rangeList.Add(2);
//rangeList -> 1-2, 4, 7-9
rangeList.Add(3);
//rangeList -> 1-3, 4, 7-9

具体实施细节

在我的特殊情况下,我正在逐行分析一个非常大的文档。需要确定满足特定条件的行,然后需要向用户显示行索引的总体列表。

明显显示“已识别的第33-32019行”比“第33、34、35 ...等行”更可取。在这种情况下,数字将始终为正。

杰米茨

我要做的第一件事是创建一个代表您范围的类。您可以提供一些便利,例如格式化为字符串以及从int隐式转换(这有助于以后实现范围列表)

public class Range
{
    public int Start{get; private set;}
    public int End{get; private set;}

    public Range(int startEnd) : this(startEnd,startEnd)
    {           
    }

     public Range(int start, int end)
     {
        this.Start = start;
        this.End = end;
     }

    public static implicit operator Range(int i)
    {
        return new Range(i);
    }

    public override string ToString()
    {
        if(Start == End)
            return Start.ToString();
        return String.Format("{0}-{1}",Start,End);
    }
}

然后,您可以开始的简单实现RangeList通过提供一种Add方法,您可以使用类似于以下内容的列表初始化器List<T>

public class RangeList : IEnumerable<Range>
{
    private List<Range> ranges = new List<Range>();

    public void Add(Range range)
    {
        this.ranges.Add(range);
    }

    public IEnumerator<Range> GetEnumerator()
    {
        return this.ranges.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator(){
        return this.GetEnumerator();
    }
}

此时,您可以编写一些测试代码:

var rangeList = new RangeList(){
    new Range(1,10),
    15
};

foreach(var range in rangeList)
    Console.WriteLine(range);

// Outputs:
//  1-10
//  15

此时的实时示例:http : //rextester.com/NCZSA71850

接下来要做的是提供一个重载,Add重载采用int并找到合适的范围或添加一个新的范围。天真的蕴含可能如下所示(假设Update在范围上添加了一种方法)

public void Add(int i)
{
    // is it within or contiguous to an existing range
    foreach(var range in ranges)
    {
        if(i>=range.Start && i<=range.End)
            return; // already in a range
        if(i == range.Start-1)
        {
            range.Update(i,range.End);
            return;
        }
        if(i == range.End + 1)
        {
            range.Update(range.Start,i);
            return;
        }
    }
    // not in any ranges
    ranges.Add(i);
}

此时的实时示例:http : //rextester.com/CHX64125

但是,这有一些不足之处

  1. 不合并范围(例如您已经拥有1-10和12-20,并且您Add(11)
  2. 不需要重新排序,所以如果您有1-5和20-25,并且Add(7)将在最后而不是中间。

您可以通过在每次添加之后应用排序来解决这两个问题,并可以通过一些逻辑来确定是否应合并范围

private void SortAndMerge()
{
    ranges.Sort((a,b) => a.Start - b.Start);
    var i = ranges.Count-1;
    do
    {
        var start = ranges[i].Start;
        var end = ranges[i-1].End;
        if(end == start-1)
        {
            // merge and remove
            ranges[i-1].Update(ranges[i-1].Start,ranges[i].End);
            ranges.RemoveAt(i);
        }
    } while(i-- >1);
}

每次更改列表后都需要调用此方法。

public void Add(Range range)
{
    this.ranges.Add(range);
    SortAndMerge();
}

public void Add(int value)
{
    // is it within or contiguous to an existing range
    foreach(var range in ranges)
    {
        if(value>=range.Start && value<=range.End)
            return; // already in a range
        if(value == range.Start-1)
        {
            range.Update(value,range.End);
            SortAndMerge();
            return;
        }
        if(value == range.End + 1)
        {
            range.Update(range.Start,value);
            SortAndMerge();
            return;
        }
    }
    // not in any ranges
    ranges.Add(value);
    SortAndMerge();
}

此处的实时示例:http : //rextester.com/SYLARF47057

仍然有一些可能的极端情况,我敦促您逐步解决。

更新

下面将使此工作按预期进行。这将按照您的期望合并所有添加的范围/整数,并返回正确排序的它们。我只更改了Add(Range)方法,我认为这是一种相当干净的方法。

public void Add(Range rangeToAdd)
{
    var mergableRange = new List<Range>();
    foreach (var range in ranges)
    {
        if (rangeToAdd.Start == range.Start && rangeToAdd.End == range.End)
            return; // already exists

        if (mergableRange.Any())
        {
            if (rangeToAdd.End >= range.Start - 1)
            {
                mergableRange.Add(range);
                continue;
            }
        }
        else
        {
            if (rangeToAdd.Start >= range.Start - 1
                && rangeToAdd.Start <= range.End + 1)
            {
                mergableRange.Add(range);
                continue;
            }

            if (range.Start >= rangeToAdd.Start
                && range.End <= rangeToAdd.End)
            {
                mergableRange.Add(range);
                continue;
            }
        }
    }

    if (!mergableRange.Any()) //Standalone range
    {
        ranges.Add(rangeToAdd);
    }
    else //merge overlapping ranges
    {
        mergableRange.Add(rangeToAdd);
        var min = mergableRange.Min(x => x.Start);
        var max = mergableRange.Max(x => x.End);
        foreach (var range in mergableRange) ranges.Remove(range);
        ranges.Add(new Range(min, max));
    }

    SortAndMerge();
}

最后,我们需要if (ranges.Count > 1)在该SortAndMerge()方法中防止在添加第一个范围时出现索引错误。

因此,我认为这完全可以满足我的问题。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

自定义解决方案的Visual Studio配色方案

Git合并冲突自定义自动解决方案

如何创建自定义动态DNS解决方案?

自定义 Visual Studio 解决方案

如何扩展自定义解决方案Xamarin的按钮

使用自定义深度测试的深度解决方案

MVC 6自定义Taghelper验证-解决方案

C ++中多重定义的解决方案

如何为自定义日志记录解决方案正确创建性能测试?

解决方案预设的项目文件上的自定义属性

自定义解决方案不存在于整个重新引导中

将自定义项模板添加到 Visual Studio 解决方案

同一解决方案的其他项目中的自定义追加程序

无法导入Dynamics CRM 2013自定义代码验证工具解决方案

巴基斯坦的Chrome扩展程序自定义付款解决方案?

如何为托管解决方案添加自定义站点地图区域/组/子区域?

HTML选择元素-自定义默认值?(跨浏览器解决方案)

将此自定义回合转换为更数学友好的解决方案

自定义实体未显示在默认解决方案屏幕中 - Dynamics CRM 2013

Laravel:使用Guzzle使用自定义Passport登录的解决方案

CRM 托管解决方案 - 卸载会删除每个自定义字段吗?

针对C#解决方案的MSBuild(类似版本)上的自定义

Xaml实时编辑不适用于自定义解决方案配置

用于整个解决方案的自定义C#重构的Visual Studio 2017扩展

可以在自定义解决方案文件上调用MSBuild吗?

在解决方案上使用msbuild在某些项目上调用自定义目标

Imageview和ListViews-解决方案是自定义适配器吗?

这是使用Couchbase解决方案的.NET自定义角色提供程序的合理方法吗?

自定义数据库收集所需的 xslt 解决方案