C# C中的整数范围列表#
我有几个类有一个整数序列,这些序列在另一个类中注册,该类检查序列中的数字是否已经被使用 序列是最连续的,从一个数字到另一个数字 现在我一直在使用一个简单的列表,这意味着如果一个序列代表5000到15000,那么列表中将有10000个元素。 我想用更合适的东西来代替它,它可以在一个简单的元素中表示范围 在我的特殊情况下,我还希望这些范围代表一个对象(序列起源的类),这样当我查找一个数字时,我就可以访问它的来源,而不是查看每个类是否包含我要查找的数字 以下是我的伪代码,其中包含我预期的结果:C# C中的整数范围列表#,c#,list,integer,partitioning,sequencing,C#,List,Integer,Partitioning,Sequencing,我有几个类有一个整数序列,这些序列在另一个类中注册,该类检查序列中的数字是否已经被使用 序列是最连续的,从一个数字到另一个数字 现在我一直在使用一个简单的列表,这意味着如果一个序列代表5000到15000,那么列表中将有10000个元素。 我想用更合适的东西来代替它,它可以在一个简单的元素中表示范围 在我的特殊情况下,我还希望这些范围代表一个对象(序列起源的类),这样当我查找一个数字时,我就可以访问它的来源,而不是查看每个类是否包含我要查找的数字 以下是我的伪代码,其中包含我预期的结果: /*
/* int is the integer type, while string is the "tag" object */
var animals = new IntRangeArray<int, string>();
animals.Add(1, "dog");
// [0] begin: 1, end: 1, object: "dog"
animals.Add(2, "dog");
// [0] begin: 1, end: 2, object: "dog"
/* AddRange with C#7.0 ValueTuple */
animals.AddRange((4,14), "dog");
// [0] begin: 1, end: 2, object: "dog"
// [1] begin: 4, end: 14, object: "dog"
animals.Add(3, "dog");
// [0] begin: 1, end: 14, object: "dog"
/* All sequences have been merged because they are contiguous and have the same tag */
animals.AddRange( new int[]{ 15, 17, 18, 19 }, "dog");
// [0] begin: 1, end: 15, object: "dog"
// [1] begin: 17, end: 19, object: "dog"
animals.Add(16, "cat");
// [0] begin: 1, end: 15, object: "dog"
// [1] begin: 16, end: 16, object: "cat"
// [2] begin: 17, end: 19, object: "dog"
animals.Remove(8);
// [0] begin: 1, end: 7, object: "dog"
// [1] begin: 9, end: 15, object: "dog"
// [2] begin: 16, end: 16, object: "cat"
// [3] begin: 17, end: 18, object: "dog"
animals.At(11);
// struct { Begin = 9, End = 15, Tag = "dog" }
animals.RemoveWithTag("dog");
// [0] begin: 16, end: 16, object: "cat"
animals.TagOf(16);
// "cat"
/*int是整数类型,而string是“tag”对象*/
var animals=新的Gearray();
动物。添加(1,“狗”);
//[0]开始:1,结束:1,对象:“狗”
动物。添加(2,“狗”);
//[0]开始:1,结束:2,对象:“狗”
/*使用C#7.0 ValueTuple添加范围*/
动物。AddRange((4,14),“狗”);
//[0]开始:1,结束:2,对象:“狗”
//[1]开始:4,结束:14,对象:“狗”
动物。添加(3,“狗”);
//[0]开始:1,结束:14,对象:“狗”
/*所有序列都已合并,因为它们是连续的并且具有相同的标记*/
动物.AddRange(新int[]{15,17,18,19},“狗”);
//[0]开始:1,结束:15,对象:“狗”
//[1]开始:17,结束:19,对象:“狗”
添加(16,“猫”);
//[0]开始:1,结束:15,对象:“狗”
//[1]开始:16,结束:16,对象:“猫”
//[2]开始:17,结束:19,对象:“狗”
动物。移除(8);
//[0]开始:1,结束:7,对象:“狗”
//[1]开始:9,结束:15,对象:“狗”
//[2]开始:16,结束:16,对象:“猫”
//[3]开始:17,结束:18,对象:“狗”
动物。At(11);
//结构{Begin=9,End=15,Tag=“dog”}
动物。移除标签(“狗”);
//[0]开始:16,结束:16,对象:“cat”
动物。TagOf(16);
//“猫”
我在.NET Framework中找不到任何实现此行为的类,因此我想知道如何实现此行为,或者是否已有任何实现。尝试类似的方法
List<KeyValuePair<int, string>> animals = new List<KeyValuePair<int,string>>();
List<KeyValuePair<int, string>> newValues = Enumerable.Repeat(1,11).Select((x,i) => new KeyValuePair<int,string>(i + 4, "dog")).ToList();
animals.AddRange(newValues);
列出动物=新建列表();
列出新值=可枚举。重复(1,11)。选择((x,i)=>newkeyvaluepair(i+4,“dog”)).ToList();
动物。添加范围(新值);
字典测试=新建字典();
int rangebegin=1;
int rangeend=4;
测试。添加(“狗”,新列表
{新元组(rangebegin,rangend)};
rangebegin=16;
rangend=22;
测试[dog]。添加(新元组(rangebegin,rangeend));
//存储在同一密钥下的多个范围
//你可以很容易地计算出你的号码在哪个范围内,然后得到
//那个范围
对于这类事情,我通常会编写自己的类。下面是我要做的:
首先是范围
类,它有开始
,结束
,和标记
。它还有一些辅助方法来简化对重叠和相邻范围的查询,或者进行不区分大小写的标记比较,以及输出字符串值:
class Range
{
public int Begin { get; set; }
public int End { get; set; }
public string Tag { get; set; }
public bool CombineWith(Range other)
{
Range combinedRange;
if (TryCombine(this, other, out combinedRange))
{
this.Begin = combinedRange.Begin;
this.End = combinedRange.End;
return true;
}
return false;
}
public bool IsAdjacentTo(Range other)
{
return AreAdjacent(this, other);
}
public bool OverlapsWith(Range other)
{
return AreOverlapping(this, other);
}
public bool ContainsIndex(int index)
{
return this.Begin <= index && this.End >= index;
}
public bool TagEquals(string tag)
{
if (this.Tag == null) return tag == null;
return this.Tag.Equals(tag, StringComparison.OrdinalIgnoreCase);
}
public static bool TryCombine(Range first, Range second, out Range combined)
{
combined = new Range();
if (first == null || second == null) return false;
if (!TagsEqual(first, second)) return false;
if (!AreAdjacent(first, second) && !AreOverlapping(first, second)) return false;
combined.Begin = Math.Min(first.Begin, second.Begin);
combined.End = Math.Max(first.End, second.End);
combined.Tag = first.Tag;
return true;
}
public static bool AreAdjacent(Range first, Range second)
{
if (first == null || second == null) return false;
if (!Range.TagsEqual(first, second)) return false;
return (first.Begin == second.End + 1) ||
(first.End == second.Begin - 1);
}
public static bool AreOverlapping(Range first, Range second)
{
if (first == null || second == null) return false;
return (first.Begin >= second.Begin && first.Begin <= second.End) ||
(first.End >= second.Begin && first.End <= second.End);
}
public static bool TagsEqual(Range first, Range second)
{
if (first == null || second == null) return false;
return first.TagEquals(second.Tag);
}
public override string ToString()
{
return $"begin: {Begin}, end: {End}, tag: {Tag}";
}
}
为了测试它,我刚刚复制并粘贴了上面的代码示例:
private static void Main()
{
/* int is the integer type, while string is the "tag" object */
var animals = new IntRangeArray();
animals.Add(1, "dog");
Console.WriteLine(animals);
animals.Add(2, "dog");
Console.WriteLine(animals);
/* AddRange with C#7.0 ValueTuple */
animals.AddRange(Tuple.Create(4, 14), "dog");
Console.WriteLine(animals);
animals.Add(3, "dog");
Console.WriteLine(animals);
animals.AddRange(new int[] { 15, 17, 18, 19 }, "dog");
Console.WriteLine(animals);
animals.Add(16, "cat");
Console.WriteLine(animals);
animals.Remove(8);
Console.WriteLine(animals);
Console.WriteLine(animals.At(11));
animals.RemoveWithTag("dog");
Console.WriteLine(animals);
Console.WriteLine(animals.TagOf(16));
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
并且输出与您预期的一样(除了有一项不同,但这是您的一个bug):
这是我的实现。我使用
SortedList
创建了一个帮助类来管理整数范围,然后创建了一个主类来管理标记的整数范围,使用字典来合并标记
class IntegerRangeCollection {
SortedList<int, int> ranges = new SortedList<int, int>();
public class Range {
public int begin, end;
}
public IntegerRangeCollection() {
}
public IntegerRangeCollection(int b, int e) {
this.Add(b, e);
}
public void Add(int b, int e) {
if (ranges.Any()) {
if (ranges.ContainsKey(b)) {
if (e > ranges[b])
ranges[b] = e;
}
else
ranges.Add(b, e);
FixUp();
}
else
ranges.Add(b, e); // new ranges list
}
public void Add(int p) => this.Add(p, p);
public void Remove(int p) {
var r = ranges.Where(pr => pr.Key <= p && p <= pr.Value).First();
if (r.Key == p) { // Remove Range begin
ranges.Remove(r.Key);
if (p+1 <= r.Value)
ranges.Add(p+1, r.Value);
}
else if (p == r.Value) // Remove Range end
ranges[r.Key] = p - 1;
else { // Split Range
ranges[r.Key] = p-1;
ranges.Add(p+1, r.Value);
}
}
public Range At(int n) {
var kvr = ranges.Where(kv => kv.Key <= n && n <= kv.Value);
if (kvr.Any()) {
var kvrf = kvr.First();
return new Range { begin = kvrf.Key, end = kvrf.Value };
}
else
return null;
}
public bool Contains(int n) => ranges.Where(kv => kv.Key <= n && n <= kv.Value).Any();
public bool IsEmpty() => !ranges.Any();
private bool DoFixUp() { // remove any overlapping ranges
foreach (var r in ranges) {
foreach (var pr in ranges.Where(pr => r.Key != pr.Key && r.Value == pr.Key - 1)) { // consolidate adjacent ranges
ranges.Remove(pr.Key);
ranges[r.Key] = pr.Value;
return true;
}
foreach (var pr in ranges.Where(pr => r.Key != pr.Key && pr.Key <= r.Value && r.Value <= pr.Value)) { // overlap end
if (pr.Key > r.Key) { // partial overlap, extend beginning
ranges.Remove(pr.Key);
ranges[r.Key] = pr.Value;
return true;
}
else { // complete overlap, remove
ranges.Remove(r.Key);
return true;
}
}
}
return false;
}
private void FixUp() {
while (DoFixUp())
;
}
}
class ObjectRangeCollection<objType> where objType : class {
Dictionary<objType, IntegerRangeCollection> d = new Dictionary<objType, IntegerRangeCollection>();
public void Add(int begin, int end, objType obj) {
if (d.TryGetValue(obj, out var ranges))
ranges.Add(begin, end);
else
d.Add(obj, new IntegerRangeCollection(begin, end));
}
public void Add(int p, objType obj) => Add(p, p, obj);
public void AddRange(ValueTuple<int, int> r, objType obj) => Add(r.Item1, r.Item2, obj);
public void AddRange(int[] rs, objType obj) {
foreach (var r in rs)
this.Add(r, r, obj);
}
public class AtAnswer {
public int begin, end;
public object tag;
}
public AtAnswer At(int p) {
var possibles = d.Where(kv => kv.Value.Contains(p));
if (possibles.Any()) {
var kv = possibles.First();
var r = kv.Value.At(p);
return new AtAnswer { tag = kv.Key, begin = r.begin, end = r.end };
}
else
return null;
}
public objType TagOf(int p) {
var possibles = d.Where(kv => kv.Value.Contains(p));
if (possibles.Any())
return possibles.First().Key;
else
return null;
}
public void Remove(int p) {
var possibles = d.Where(kv => kv.Value.Contains(p));
if (possibles.Any()) {
foreach (var kv in possibles) {
kv.Value.Remove(p);
if (kv.Value.IsEmpty())
d.Remove(kv.Key);
}
}
}
public void RemoveWithTag(objType aTag) {
d.Remove(aTag);
}
}
class IntegerRangeCollection{
分拣列表范围=新分拣列表();
公共类范围{
公共int开始,结束;
}
公共IntegrationCollection(){
}
公共整数集合(整数b、整数e){
本条增补(b,e);
}
公共无效添加(内部b、内部e){
if(ranges.Any()){
if(范围:集装箱(b)){
如果(e>范围[b])
范围[b]=e;
}
其他的
增加(b,e);
FixUp();
}
其他的
范围。添加(b,e);//新范围列表
}
public void Add(int p)=>this.Add(p,p);
公共无效删除(INTP){
var r=范围,其中(pr=>pr.键添加(r.Item1,r.Item2,obj);
public void AddRange(int[]rs,对象类型obj){
foreach(rs中的var r)
添加(r,r,obj);
}
公共类回答{
公共int开始,结束;
公共对象标签;
}
公共回答(int p){
var可能值=d,其中(kv=>kv值包含(p));
if(possibles.Any()){
var kv=可能性。第一个();
var r=在(p)处的千伏值;
返回新的AtAnswer{tag=kv.Key,begin=r.begin,end=r.end};
}
其他的
返回null;
}
公共对象类型TagOf(int p){
var可能值=d,其中(kv=>kv值包含(p));
if(possibles.Any())
返回possibles.First().Key;
其他的
返回null;
}
公共无效删除(INTP){
var可能值=d,其中(kv=>kv值包含(p));
if(possibles.Any()){
foreach(可能的var kv){
kv.值。移除(p);
if(kv.Value.IsEmpty())
d、 移除(千伏键);
}
}
}
public void RemoveWithTag(对象类型aTag){
d、 移除(aTag);
}
}
您想要一个字典吗?其中键是一个类似字符串(“dog”)的键,值是该键的整数集合。字典已经在C#中。否,我的“dog”可能是多个整数范围的一部分(如我的示例所示)因为标记或范围都不能作为键。我的库只需要获得一个数字,需要知道它来自哪个范围,并使用与此范围相关的标记。为什么要进行向下投票?我认为这是一个非常有趣的问题。
private static void Main()
{
/* int is the integer type, while string is the "tag" object */
var animals = new IntRangeArray();
animals.Add(1, "dog");
Console.WriteLine(animals);
animals.Add(2, "dog");
Console.WriteLine(animals);
/* AddRange with C#7.0 ValueTuple */
animals.AddRange(Tuple.Create(4, 14), "dog");
Console.WriteLine(animals);
animals.Add(3, "dog");
Console.WriteLine(animals);
animals.AddRange(new int[] { 15, 17, 18, 19 }, "dog");
Console.WriteLine(animals);
animals.Add(16, "cat");
Console.WriteLine(animals);
animals.Remove(8);
Console.WriteLine(animals);
Console.WriteLine(animals.At(11));
animals.RemoveWithTag("dog");
Console.WriteLine(animals);
Console.WriteLine(animals.TagOf(16));
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
class IntegerRangeCollection {
SortedList<int, int> ranges = new SortedList<int, int>();
public class Range {
public int begin, end;
}
public IntegerRangeCollection() {
}
public IntegerRangeCollection(int b, int e) {
this.Add(b, e);
}
public void Add(int b, int e) {
if (ranges.Any()) {
if (ranges.ContainsKey(b)) {
if (e > ranges[b])
ranges[b] = e;
}
else
ranges.Add(b, e);
FixUp();
}
else
ranges.Add(b, e); // new ranges list
}
public void Add(int p) => this.Add(p, p);
public void Remove(int p) {
var r = ranges.Where(pr => pr.Key <= p && p <= pr.Value).First();
if (r.Key == p) { // Remove Range begin
ranges.Remove(r.Key);
if (p+1 <= r.Value)
ranges.Add(p+1, r.Value);
}
else if (p == r.Value) // Remove Range end
ranges[r.Key] = p - 1;
else { // Split Range
ranges[r.Key] = p-1;
ranges.Add(p+1, r.Value);
}
}
public Range At(int n) {
var kvr = ranges.Where(kv => kv.Key <= n && n <= kv.Value);
if (kvr.Any()) {
var kvrf = kvr.First();
return new Range { begin = kvrf.Key, end = kvrf.Value };
}
else
return null;
}
public bool Contains(int n) => ranges.Where(kv => kv.Key <= n && n <= kv.Value).Any();
public bool IsEmpty() => !ranges.Any();
private bool DoFixUp() { // remove any overlapping ranges
foreach (var r in ranges) {
foreach (var pr in ranges.Where(pr => r.Key != pr.Key && r.Value == pr.Key - 1)) { // consolidate adjacent ranges
ranges.Remove(pr.Key);
ranges[r.Key] = pr.Value;
return true;
}
foreach (var pr in ranges.Where(pr => r.Key != pr.Key && pr.Key <= r.Value && r.Value <= pr.Value)) { // overlap end
if (pr.Key > r.Key) { // partial overlap, extend beginning
ranges.Remove(pr.Key);
ranges[r.Key] = pr.Value;
return true;
}
else { // complete overlap, remove
ranges.Remove(r.Key);
return true;
}
}
}
return false;
}
private void FixUp() {
while (DoFixUp())
;
}
}
class ObjectRangeCollection<objType> where objType : class {
Dictionary<objType, IntegerRangeCollection> d = new Dictionary<objType, IntegerRangeCollection>();
public void Add(int begin, int end, objType obj) {
if (d.TryGetValue(obj, out var ranges))
ranges.Add(begin, end);
else
d.Add(obj, new IntegerRangeCollection(begin, end));
}
public void Add(int p, objType obj) => Add(p, p, obj);
public void AddRange(ValueTuple<int, int> r, objType obj) => Add(r.Item1, r.Item2, obj);
public void AddRange(int[] rs, objType obj) {
foreach (var r in rs)
this.Add(r, r, obj);
}
public class AtAnswer {
public int begin, end;
public object tag;
}
public AtAnswer At(int p) {
var possibles = d.Where(kv => kv.Value.Contains(p));
if (possibles.Any()) {
var kv = possibles.First();
var r = kv.Value.At(p);
return new AtAnswer { tag = kv.Key, begin = r.begin, end = r.end };
}
else
return null;
}
public objType TagOf(int p) {
var possibles = d.Where(kv => kv.Value.Contains(p));
if (possibles.Any())
return possibles.First().Key;
else
return null;
}
public void Remove(int p) {
var possibles = d.Where(kv => kv.Value.Contains(p));
if (possibles.Any()) {
foreach (var kv in possibles) {
kv.Value.Remove(p);
if (kv.Value.IsEmpty())
d.Remove(kv.Key);
}
}
}
public void RemoveWithTag(objType aTag) {
d.Remove(aTag);
}
}