1. 程式人生 > >面試必談的雜湊,.Net 程式設計師溫故而知新

面試必談的雜湊,.Net 程式設計師溫故而知新

 引言:

作為資深老鳥,有事沒事,出去面試;找準差距、定位價值。

面試必談雜湊,

Q1:什麼是雜湊?

Q2:雜湊為什麼快?

Q3:你是怎麼理解雜湊演算法利用空間換取時間的?

Q4:你是怎麼解決雜湊衝突的?

Q5:你有實際用寫過雜湊演算法嗎?

1. 知識儲備

  雜湊(也叫雜湊)是一種查詢演算法(可用於插入),雜湊演算法希望能做到不經過任何比較(發生衝突,還是需要少許比較),通過一次存取就能得到查詢的資料。

因此雜湊的關鍵在key和資料元素的儲存位置之間建立一個確定的對應關係,每個key在雜湊表中都有唯一的地址相對應(形成有限、連續的地址空間),查詢時根據對應關係經過一步計算得到key在散列表的位置。

在數學上, 原Key叫做原像,由對映函式h(key)對映的儲存位置叫做像;在IT領域,以上儲存位置叫雜湊地址(雜湊地址),這個對映過程叫做雜湊/雜湊。

故我們可以預見:

  ① 不同的key值,由雜湊函式h(x) 作用後可能對映到同一個雜湊地址, 這就是雜湊衝突,衝突發生的概率取決於 定義的雜湊函式

  ② 由雜湊表作用後的雜湊地址需要空間儲存,這一系列連續相鄰的地址空間叫雜湊表、 散列表。 

  

  處理雜湊衝突可分為兩大類:

  (1)開雜湊法發生衝突的元素儲存於陣列空間之外。可以把“開”字理解為需要另外“開闢”空間儲存發生衝突的元素, 又稱【鏈地址法】

  (2)閉雜湊法發生衝突的元素儲存於陣列空間之內。可以把“閉”字理解為所有元素,不管是否有衝突,都“關閉”於陣列之中,閉雜湊法又稱【開放定址法】,意指陣列空間對所有元素,不管是否衝突都是開放的

   雜湊表是用陣列實現的一片連續的地址空間,兩種衝突解決方案的區別在於發生衝突的元素是儲存在這片陣列的空間之外還是空間之內

2. 看圖說話

----------------------------以下是開雜湊法(鏈地址法)解決衝突的示意圖---------------------------  

  從圖上看實現【雜湊】過程分兩部分:

① 雜湊函式

  收斂函式,不可避免會衝突,需要思考設定一個均衡的雜湊函式,使雜湊地址儘可能均勻地分佈在雜湊地址空間

②  構造雜湊表 + 衝突連結串列

  裝填因子loadfactor :所謂裝填因子是指雜湊表中已存入的記錄數n與雜湊地址空間大小m的比值,即 α=n / m ,α越小,衝突發生的可能性就越小;α越大(最大可取1),衝突發生的可能性就越大。

  另一方面,α越小,儲存窨的利用率就越低;反之,儲存窨的利用率就越高。為了既兼顧減少衝突的發生,又兼顧提高儲存空間的利用率,通常把α控制在0.6~0.9的範圍之內

 

雜湊在.Net中的應用

  Object基類中有GetHashCode方法,HashCode是一個數字值,用於在【基於雜湊特性的集合】中插入和查詢某物件;GetHashCode方法為需要快速檢查物件相等性的演算法提供此雜湊程式碼;

  Do not test for equality of hash codes to determine whether two objects are equal. (Unequal objects can have identical hash codes.) To test for equality, call the ReferenceEquals or Equals method.  (重要的話要讀3遍)

  • 值型別hashcode 的預設實現

  • 引用型別hashcode的預設實現

單純判斷【邏輯相等】時,本無所謂重寫 GetHashCode方法:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Http;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

namespace Test
{
    public class Persion
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public override bool Equals(object other1)  // 邏輯相等
        {
            return Name == (other1 as Persion)?.Name;
        }

        public override int GetHashCode()
        {
            return Name.GetHashCode();
        }
    }
    public class Program
    {
        static void Main(string[] args)
        {
            var p1 = new Persion {  Name="HJ" , Age=22};

            var p2 = new Persion { Name = "HJ", Age = 21 };

            var referenceEqual = (p1 == p2);    
            var logicEqual = (p1.Equals(p2));

            Console.WriteLine($"“==”操作符:引用相等(兩變數指向一個例項),始終為:{referenceEqual}");
            Console.WriteLine($"“Equal”方法:邏輯相等, {logicEqual}");
            Console.Read();
        }
    }
}
----------------------------------------------------------------
output:
“==”操作符:引用相等(兩變數指向一個例項),始終為:False
“Equal”方法: 邏輯相等, True

但是若需要利用HashCode快速 查詢 /插入某元素, 則一定要重寫GetHashCode方法。