我想知道是否有人可以提出一种以更节省内存的方式实现数字数组的方法,该方法将自动将自身组织为范围。例;
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
但是,这有一些不足之处
Add(11)
)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] 删除。
我来说两句