根据范围数据查找值

时间:2012-10-12 08:41:05

标签: c# .net

哪种数据结构或数据类型适合保存数据范围,并根据该范围内的数据返回值?

例如,假设我有以下范围

1-10 -> 1  
11-35 -> 2.5  
36-49-> 3.8  
50-60 -> 1.2  
61-80 -> 0.9 

在这种情况下给定数字41我希望返回数字3.8(因为41在36到49之间)。

是否有一种巧妙的方法在数据结构中表示此类数据以执行此查找?

3 个答案:

答案 0 :(得分:2)

一个相对方便且性能非常高的实现方法是使用SortedList<int, Tuple<int, double>>。使用每个段的下限作为键和值的上限+映射值的元组:

var list = new SortedList<int, Tuple<int, double>>
{
    { 1, Tuple.Create(10, 1.0) },
    { 11, Tuple.Create(35, 2.5) },
};

(当然,您可以决定使用更好看的数据结构来声明您的参数,以便在开始业务之前增强代码可维护性并在内部转换为此。)

由于保证对list.Keys进行排序,因此在查找值时,您可以使用binary search来查找等于或大于输入值的索引:

var index = list.Keys.BinarySearch(value);
if (index < 0 && ~index == 0) {
    // no match, stop processing
}
else if (index < 0) {
    // key not found as is, look at the previous interval
    index = ~index - 1;
}

此时index指向可能包含value的唯一范围,所以剩下的就是测试它:

if(x >= list.Keys[index] && x <= list.Values[index].Item1) {
    var result = list.Values[index].Item2;
}
else {
    // no match
}

你不会称之为“干净”,但它非常短而且非常快。

答案 1 :(得分:1)

您可以使用此代码

关键:

public class Interval<T> where T : IComparable
{
    public Nullable<T> Start { get; set; }
    public Nullable<T> End { get; set; }

    public Interval(T start, T end)
    {
        Start = start;
        End = end;
    }

    public bool InRange(T value)
    {
        return ((!Start.HasValue || value.CompareTo(Start.Value) > 0) &&
                (!End.HasValue || End.Value.CompareTo(value) > 0));
    }
}

值:十进制

您可以使用此类型:Dictionary<Interval, decimal>

Nota:您可以定义访问方法

答案 2 :(得分:1)

我花了一段时间,但我有一个QuickAndDirty方法,假设所有给定值都有效且范围相邻,而不使用任何数据结构。 还有一个非常具体的数据结构,如果给定的索引完全在指定的范围内,并且可以在运行时扩展,那么它只会返回一些内容。

public abstract class TreeNode
{
    public static double QuickAndDirty(int index)
    {
        double result = 1.0;

        if (index > 10)
            result = 2.5;

        if (index > 35)
            result = 3.8;

        if (index > 49)
            result = 1.2;

        if (index > 60)
            result = 0.9;

        return result;
    }

    public abstract double GetValue(int index);

    public abstract TreeNode AddRange(int begin, int end, double value);

    public static TreeNode MakeTreePart(Tuple<int, int, double>[] ranges)
    {
        return TreeNode.MakeTreePart(ranges, 0, ranges.Length >> 1, ranges.Length - 1);
    }

    private static TreeNode MakeTreePart(Tuple<int, int, double>[] ranges, int min, int index, int max)
    {
        if (index == min || index == max)
            return new Leaf(ranges[index].Item1, ranges[index].Item2, ranges[index].Item3);

        return new SegmentTree(
            ranges[index].Item2 + .5,
            TreeNode.MakeTreePart(ranges, min, index >> 1, index - 1),
            TreeNode.MakeTreePart(ranges, index + 1, index << 1, max));
    }
}

public class SegmentTree : TreeNode
{
    private double pivot;
    private TreeNode left, right;

    public SegmentTree(double pivot, TreeNode left, TreeNode right)
    {
        this.pivot = pivot;
        this.left = left;
        this.right = right;
    }

    public override double GetValue(int index)
    {
        if (index < pivot)
            return left.GetValue(index);
        return right.GetValue(index);
    }

    public override TreeNode AddRange(int begin, int end, double value)
    {
        if (end < pivot)
            this.left = this.left.AddRange(begin, end, value);
        else
            this.right = this.right.AddRange(begin, end, value);
        // Do this to confirm to the interface.
        return this;
    }
}

public class Leaf : TreeNode
{
    private int begin, end;
    private double value;

    public Leaf(int begin, int end, double value)
    {
        this.begin = begin;
        this.end = end;
        this.value = value;
    }

    public override double GetValue(int index)
    {
        if (index >= begin && index <= end)
            return value;
        throw new Exception("index out of range");
    }

    public override TreeNode AddRange(int begin, int end, double value)
    {
        if (this.end < begin)
            return new SegmentTree(((double)this.end + begin) * .5, this, new Leaf(begin, end, value));
        else if (end < this.begin)
            return new SegmentTree(((double)end + this.begin) * .5, new Leaf(begin, end, value), this);
        else throw new Exception("Indexes overlap.");
    }
}
    static void Main()
    {
        TreeNode start = new Leaf(36, 49, 3.8);
        start = start.AddRange(11, 35, 2.5);
        start = start.AddRange(1, 10, 1.0);
        start = start.AddRange(50, 60, 1.2);
        start = start.AddRange(61, 80, 0.9);
        double[] values = new double[70];
        for (int i = 1; i < values.Length; i++)
            values[i] = start.GetValue(i);

        TreeNode array = TreeNode.MakeTreePart(
            new Tuple<int, int, double>[]
            {
                Tuple.Create(1, 10, 1.0),
                Tuple.Create(11, 35, 2.5),
                Tuple.Create(36, 49, 3.8),
                Tuple.Create(50, 60, 1.2),
                Tuple.Create(61, 80, 0.9)
            });

        for (int i = 1; i < values.Length; i++)
            values[i] = start.GetValue(i);
    }