代码之家  ›  专栏  ›  技术社区  ›  dan-gph

有没有针对.Net的multiset的实现?

  •  22
  • dan-gph  · 技术社区  · 15 年前

    我在找一个多集的.Net实现。有人能推荐一个好的吗?

    (multiset或bag是一个可以有重复值的集合,您可以在其上执行集合操作:交集、差异等。例如,购物车可以被认为是一个multiset,因为您可以有同一产品的多个实例。)

    3 回复  |  直到 15 年前
        1
  •  4
  •   treaschf    15 年前

    我不知道,不过你可以用 Dictionary 为此,其中值是项目的数量。当第二次添加项时,您可以在字典中增加该项的值。

    另一种可能是简单地使用 List 在其中可以放置副本的项。对于购物车来说,这可能是一种更好的方法。

        2
  •  3
  •   jwezorek    9 年前

    任何自称为多集的C#实现的东西都不应该在内部基于字典。字典是散列表,无序的集合。C++的集合、多集、映射和多任务是有序的。在内部,每一个都表示为某种自平衡二叉搜索树。

    在C语言中,我们应该使用SortedDictionary作为实现的基础,就像微软自己的文档SortedDictionary一样。” is a binary search tree with O(log n) retrieval ". 基本多集可以实现如下:

    public class SortedMultiSet<T> : IEnumerable<T>
    {
        private SortedDictionary<T, int> _dict; 
    
        public SortedMultiSet()
        {
            _dict = new SortedDictionary<T, int>();
        }
    
        public SortedMultiSet(IEnumerable<T> items) : this()
        {
            Add(items);
        }
    
        public bool Contains(T item)
        {
            return _dict.ContainsKey(item);
        }
    
        public void Add(T item)
        {
            if (_dict.ContainsKey(item))
                _dict[item]++;
            else
                _dict[item] = 1;
        }
    
        public void Add(IEnumerable<T> items)
        {
            foreach (var item in items)
                Add(item);
        }
    
        public void Remove(T item)
        {
            if (!_dict.ContainsKey(item))
                throw new ArgumentException();
            if (--_dict[item] == 0)
                _dict.Remove(item);
        }
    
        // Return the last value in the multiset
        public T Peek()
        {
            if (!_dict.Any())
                throw new NullReferenceException();
            return _dict.Last().Key;
        }
    
        // Return the last value in the multiset and remove it.
        public T Pop()
        {
            T item = Peek();
            Remove(item);
            return item;
        }
    
        public IEnumerator<T> GetEnumerator()
        {
            foreach(var kvp in _dict)
                for(int i = 0; i < kvp.Value; i++)
                    yield return kvp.Key;
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }
    
        3
  •  1
  •   Paul Richards    9 年前
    public class Multiset<T>: ICollection<T>
    {
        private readonly Dictionary<T, int> data;
    
        public Multiset() 
        {
            data = new Dictionary<T, int>();
        }
    
        private Multiset(Dictionary<T, int> data)
        {
            this.data = data;
        }
    
        public void Add(T item)
        {
            int count = 0;
            data.TryGetValue(item, out count);
            count++;
            data[item] = count;
        }
    
        public void Clear()
        {
            data.Clear();
        }
    
        public Multiset<T> Except(Multiset<T> another)
        {
            Multiset<T> copy = new Multiset<T>(new Dictionary<T, int>(data));
            foreach (KeyValuePair<T, int> kvp in another.data)
            {
                int count;
                if (copy.data.TryGetValue(kvp.Key, out count))
                {
                    if (count > kvp.Value)
                    {
                        copy.data[kvp.Key] = count - kvp.Value;
                    }
                    else
                    {
                        copy.data.Remove(kvp.Key);
                    }
                }
            }
            return copy;
        }
    
        public Multiset<T> Intersection(Multiset<T> another)
        {
            Dictionary<T, int> newData = new Dictionary<T, int>();
            foreach (T t in data.Keys.Intersect(another.data.Keys))
            {
                newData[t] = Math.Min(data[t], another.data[t]);
            }
            return new Multiset<T>(newData);
        }
    
        public bool Contains(T item)
        {
            return data.ContainsKey(item);
        }
    
        public void CopyTo(T[] array, int arrayIndex)
        {
            foreach (KeyValuePair<T, int> kvp in data)
            {
                for (int i = 0; i < kvp.Value; i++)
                {
                    array[arrayIndex] = kvp.Key;
                    arrayIndex++;
                }
            }
        }
    
        public IEnumerable<T> Mode()
        {
            if (!data.Any())
            {
                return Enumerable.Empty<T>();
            }
            int modalFrequency = data.Values.Max();
            return data.Where(kvp => kvp.Value == modalFrequency).Select(kvp => kvp.Key);
        }
    
        public int Count
        {
            get 
            {
                return data.Values.Sum();
            }
        }
    
        public bool IsReadOnly
        {
            get 
            { 
                return false; 
            }
        }
    
        public bool Remove(T item)
        {
            int count;
            if (!data.TryGetValue(item, out count))
            {
                return false;
            }
            count--;
            if (count == 0)
            {
                data.Remove(item);
            }
            else
            {
                data[item] = count;
            }
            return true;
        }
    
        public IEnumerator<T> GetEnumerator()
        {
            return new MultisetEnumerator<T>(this);
        }
    
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new MultisetEnumerator<T>(this);
        }
    
        private class MultisetEnumerator<T> : IEnumerator<T>
        {
            public MultisetEnumerator(Multiset<T> multiset)
            {
                this.multiset = multiset;
                baseEnumerator = multiset.data.GetEnumerator();
                index = 0;
            }
    
            private readonly Multiset<T> multiset;
            private readonly IEnumerator<KeyValuePair<T, int>> baseEnumerator;
            private int index;
    
            public T Current
            {
                get 
                {
                    return baseEnumerator.Current.Key;
                }
            }
    
            public void Dispose()
            {
                baseEnumerator.Dispose();
            }
    
            object System.Collections.IEnumerator.Current
            {
                get 
                {
                    return baseEnumerator.Current.Key;
                }
            }
    
            public bool MoveNext()
            {
                KeyValuePair<T, int> kvp = baseEnumerator.Current;
                if (index < (kvp.Value - 1))
                {
                    index++;
                    return true;
                }
                else
                {
                    bool result = baseEnumerator.MoveNext();
                    index = 0;
                    return result;
                }
            }
    
            public void Reset()
            {
                baseEnumerator.Reset();
            }
        }
    }