散列算法是怎么实现的_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 散列算法是怎么实现的

散列算法是怎么实现的

 2013/12/5 17:26:00  不同冲突解决方式比较  博客园  我要评论(0)
  • 摘要:我们来比较下散列的3种冲突解决方式,建立3个类,分别代表3种不同的冲突解决方式:MyHash_MAD_多槽位MyHash_MAD_独立链MyHash_MAD_线性探测法然后在主程序中分别插入10000条记录,比较各自所需要的时间。先介绍下:MAD:multiply-add-dividemethod,乘法-加法-除法(取模),如下这个公式是散列核心公式(a*collisionIndex+b)%M,M要求是素数,a,b的取值要合理冲突解决方式:多槽位当计算出的Index位置处已经被占用后
  • 标签:实现 算法

我们来比较下散列的3种冲突解决方式,建立3个类,分别代表3种不同的冲突解决方式:

  1. MyHash_MAD_多槽位
  2. MyHash_MAD_独立链
  3. MyHash_MAD_线性探测法

然后在主程序中分别插入10000条记录,比较各自所需要的时间。

先介绍下:

  • MAD:
    • multiply-add-divide method,乘法 - 加法 - 除法(取模),如下这个公式是散列核心公式
    • (a*collisionIndex+b)%M, M要求是素数,a, b的取值要合理
  • 冲突解决方式:
    • 多槽位
      • 当计算出的Index位置处已经被占用后,还是会在这个index的地方增加一个元素
      • 主数组的每个元素是个列表(比如每个元素都能放5个子元素),因此每个位置都能放多个元素
    • 独立链
      • 基本同上
      • 区别:主数组的每个元素对应的是一个链表
    • 线性探测法
      • 主数组只有一层,也就是每个数组元素只有一个空位,没有子列表
      • 如果计算出的Index位置处已经被占用,就自动往后面查找,直到找到一个没有被占用的位置,把元素放这个空位中

code:

class Program
    {
        static void Main(string[] args)
        {
            Timing t = new Timing();
            Random rnd = new Random(DateTime.Now.Second);

            MyHash_MAD_多槽位 hash = new MyHash_MAD_多槽位();
            t.Start();
            for (var i = 0; i < 10000; i++)
            {
                string key = string.Format("{0}-{1}", rnd.Next(0, 9999), rnd.Next(0, 9999));
                hash[key] = i;
            }
            t.Stop();
            t.Display("MyHash_MAD_多槽位: ");
            hash.DisplayEmptyRatio();

            Console.WriteLine();

            rnd = new Random(DateTime.Now.Second);
            MyHash_MAD_独立链 hash2 = new MyHash_MAD_独立链();
            t.Start();
            for (var i = 0; i < 10000; i++)
            {
                string key = string.Format("{0}-{1}", rnd.Next(0, 9999), rnd.Next(0, 9999));
                hash2[key] = i;
            }
            t.Stop();
            t.Display("MyHash_MAD_独立链: ");
            hash2.DisplayEmptyRatio();

            Console.WriteLine();

            rnd = new Random(DateTime.Now.Second);
            MyHash_MAD_线性探测法 hash3 = new MyHash_MAD_线性探测法();
            t.Start();
            for (var i = 0; i < 10000; i++)
            {
                string key = string.Format("{0}-{1}", rnd.Next(0, 9999), rnd.Next(0, 9999));
                hash3[key] = i;
            }
            t.Stop();
            t.Display("MyHash_MAD_线性探测法: ");
            hash3.DisplayEmptyRatio();

            Console.WriteLine("done.");
            Console.ReadKey();
        }
    }

    class MyHash_MAD_多槽位
    {
        private const int defaultSize = 10001;
        private List<List<Tuple<string, object>>> lstArray = new List<List<Tuple<string, object>>>(defaultSize);

        public MyHash_MAD_多槽位()
        {
            int i = lstArray.Capacity;
            while(i>0)
            {
                lstArray.Add(new List<Tuple<string,object>>());
                i--;
            }
        }

        public object this[string key]
        {
            get
            {
                EnsureNotNull(key);

                List<Tuple<string, object>> lst;
                Tuple<string, object> obj = FindByKey(key, out lst);
                if (obj == null)
                    throw new Exception("Key不存在");

                return obj.Item2;
            }
            set
            {
                EnsureNotNull(key);

                List<Tuple<string, object>> lst;
                Tuple<string, object> obj = FindByKey(key, out lst);
                if (obj!=null)
                    lst.Remove(obj);

                lst.Add(new Tuple<string, object>(key, value));
            }
        }

        private Tuple<string, object> FindByKey(string key, out List<Tuple<string, object>> lst)
        {
            int hashIndex = MapString2Int(key);
            lst = lstArray[hashIndex];
            Tuple<string, object> obj = null;
            for (var i = 0; i < lst.Count; i++)
            {
                if (lst[i].Item1 == key)
                {
                    obj = lst[i];
                    break;
                }
            }

            return obj;
        }

        private static void EnsureNotNull(string key)
        {
            if (key == null || key.Trim().Length == 0)
                throw new Exception("Key不能为空");
        }

        private int MapString2Int(string key)
        {
            int hashIndex=0;
            char[] keyAry = key.ToCharArray();
            foreach (var c in keyAry)
                hashIndex += (int)c;

            hashIndex = (31 * hashIndex + 2) % lstArray.Capacity;

            return hashIndex;
        }

        public void DisplayFlags()
        {
            foreach (var item in lstArray)
                Console.Write(string.Format("{0}, ", item.Count));
        }
        public void DisplayEmptyRatio()
        {
            float emptyCount = 0;
            foreach (var item in lstArray)
                if (item.Count == 0)
                    emptyCount++;

            string msg = string.Format("空值个数:{1}/{2}\n有值个数:{3}\n空值比例:{0}%", emptyCount / lstArray.Capacity * 100, emptyCount, lstArray.Capacity, lstArray.Capacity-emptyCount);
            Console.WriteLine(msg);
        }
    }

    class MyHash_MAD_独立链
    {
        class HashNode
        {
            public string Key { get; set; }
            public object Value { get; set; }
        }

        private const int defaultSize = 10001;
        //private List<LinkedList<HashNode>> lstArray = new List<LinkedList<HashNode>>(defaultSize);
        private LinkedList<HashNode>[] lstArray = new LinkedList<HashNode>[defaultSize];

        public MyHash_MAD_独立链()
        {
            int i = defaultSize;
            while (i > 0)
            {
                lstArray[i - 1] = new LinkedList<HashNode>();
                i--;
            }
        }

        public object this[string key]
        {
            get
            {
                EnsureNotNull(key);

                LinkedList<HashNode> lst;
                HashNode obj = FindByKey(key, out lst);
                if (obj == null)
                    throw new Exception("Key不存在");

                return obj.Value;
            }
            set
            {
                EnsureNotNull(key);

                LinkedList<HashNode> lst;
                HashNode obj = FindByKey(key, out lst);
                if (obj != null)
                    lst.Remove(obj);

                lst.AddLast(new HashNode() {  Key=key, Value=value});
            }
        }

        private HashNode FindByKey(string key, out LinkedList<HashNode> lst)
        {
            int hashIndex = MapString2Int(key);
            lst = lstArray[hashIndex];
            HashNode obj = lst.FirstOrDefault(t => t.Key == key);
            if (obj != null && !string.IsNullOrEmpty(obj.Key))
                return obj;

            return null;
        }

        private static void EnsureNotNull(string key)
        {
            if (key == null || key.Trim().Length == 0)
                throw new Exception("Key不能为空");
        }

        private int MapString2Int(string key)
        {
            int hashIndex = 0;
            char[] keyAry = key.ToCharArray();
            foreach (var c in keyAry)
                hashIndex += (int)c;

            hashIndex = (31 * hashIndex + 2) % defaultSize;

            return hashIndex;
        }

        public void DisplayFlags()
        {
            foreach (var item in lstArray)
                Console.Write(string.Format("{0}, ", item.Count));
        }
        public void DisplayEmptyRatio()
        {
            float emptyCount = 0;
            foreach (var item in lstArray)
                if (item.Count == 0)
                    emptyCount++;

            string msg = string.Format("空值个数:{1}/{2}\n有值个数:{3}\n空值比例:{0}%", emptyCount / defaultSize * 100, emptyCount, defaultSize, defaultSize - emptyCount);
            Console.WriteLine(msg);
        }
    }

    class MyHash_MAD_线性探测法
    {
        private const int defaultSize = 10001;
        private List<List<Tuple<string, object>>> lstArray = new List<List<Tuple<string, object>>>(defaultSize);

        public MyHash_MAD_线性探测法()
        {
            int i = lstArray.Capacity;
            while (i > 0)
            {
                lstArray.Add(new List<Tuple<string, object>>());
                i--;
            }
        }

        public object this[string key]
        {
            get
            {
                EnsureNotNull(key);

                Tuple<string, object> obj = FindElement(key);
                if (obj == null)
                    throw new Exception("Key不存在");

                return obj.Item2;
            }
            set
            {
                EnsureNotNull(key);

                List<Tuple<string, object>> lst;
                Tuple<string, object> obj = FindNextEmptyPlace(key, out lst);
                if (obj != null)
                    lst.Remove(obj);

                lst.Add(new Tuple<string, object>(key, value));
            }
        }

        private Tuple<string, object> FindElement(string key)
        {
            int hashIndex = MapString2Int(key);

            while (true)
            {
                hashIndex = hashIndex % lstArray.Capacity;
                if (lstArray[hashIndex].Count > 0)
                {
                    List<Tuple<string, object>> lst = lstArray[hashIndex];
                    if (lst[0].Item1 == key)
                    {
                        return lst[0];
                        break;
                    }
                }
                hashIndex++;
            }
        }

        private Tuple<string, object> FindNextEmptyPlace(string key, out List<Tuple<string, object>> lst)
        {
            int hashIndex = MapString2Int(key);

            while (true)
            {
                hashIndex = hashIndex % lstArray.Capacity;
                if (lstArray[hashIndex].Count == 0)
                    break;
                hashIndex++;
            }

            lst = lstArray[hashIndex];
            Tuple<string, object> obj = null;
            for (var i = 0; i < lst.Count; i++)
            {
                if (lst[i].Item1 == key)
                {
                    obj = lst[i];
                    break;
                }
            }

            return obj;
        }

        private static void EnsureNotNull(string key)
        {
            if (key == null || key.Trim().Length == 0)
                throw new Exception("Key不能为空");
        }

        private int MapString2Int(string key)
        {
            int hashIndex = 0;
            char[] keyAry = key.ToCharArray();
            foreach (var c in keyAry)
                hashIndex += (int)c;

            hashIndex = (31 * hashIndex + 2) % lstArray.Capacity;
            return hashIndex;
        }

        public void DisplayFlags()
        {
            foreach (var item in lstArray)
                Console.Write(string.Format("{0}, ", item.Count));
        }

        public void DisplayEmptyRatio()
        {
            float emptyCount = 0;
            foreach (var item in lstArray)
                if (item.Count == 0)
                    emptyCount++;

            string msg = string.Format("空值个数:{1}/{2}\n有值个数:{3}\n空值比例:{0}%", emptyCount / lstArray.Capacity * 100, emptyCount, lstArray.Capacity, lstArray.Capacity - emptyCount);
            Console.WriteLine(msg);
        }

    }

 

 

线性探测法的空间占用率最高,几乎没有空位,但是耗费的时间最多,主要是查找I/O效率低。

多槽位性能最好。

 

 

 

发表评论
用户名: 匿名