散列算法是怎么實現的 - 不同沖突解決方式比較


我們來比較下散列的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效率低。

多槽位性能最好。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM