HashMap, HashTable,HashSet,TreeMap 的時間複雜度 注意數組鏈表 增刪改查的時間複雜度都不相同(阿里)

hashmap的擴容因子是0.75 緣由 參考:HashMap默認加載因子爲何選擇0.75?(阿里)html

阿里的人問 數組的時間複雜度是多少,鏈表的是多少,hashmap的時間複雜度是多少。。。。。java

後來才知道,時間複雜度是要區分 增刪改查的。。。。主要看查詢的時間複雜度;git

一、數組 查詢的時間複雜度 O(n)面試

二、鏈表 查詢的時間複雜度 O(n)算法

三、hashmap  查詢的時間複雜度 O(1)編程

數組 查詢的時間複雜度 O(n)數組

建議看一下下面的博客:安全

 

hashSet,hashtable,hashMap 都是基於散列函數, 時間複雜度 O(1) 可是若是太差的話是O(n)數據結構

TreeSet==>O(log(n))==> 基於樹的搜索,只須要搜索一半便可編程語言

 

O⑴的緣由是離散後,下標對應關鍵字
hash就是散列,甚至再散列。可是我一直對hash表的時間複雜度有個疑問。一個須要存儲的字符串,經過hash函數散列到一個相對較短的索引,使得存取速度加快。但爲何存取的時間複雜度能達到常量級O(1)呢?? 查找時搜索索引不須要費時間嗎?爲何不是O(n)呢? n是hash表的長度,

若是對Hashtable的構造有很深的理解的話,就知道了,Hashtable 實際上是綜合了數組和鏈表的優勢,當Hashtable對數值進行搜索的時候,首先用該數值與Hashtable的長度作了取模的操做,獲得的數字直接做爲hashtable中entry數組的index,由於hashtable是由entry數組組成的,所以,能夠直接定位到指定的位置,不須要搜索,固然,這裏還有個問題,每一個entry實際上是鏈表,若是entry有不少值的話,仍是須要挨個遍歷的,所以能夠這樣講Hashtable的時間複雜度最好是O(1)可是最差是 O(n) 最差的時候也就是hashtable中全部的值的hash值都同樣,都分配在一個entry裏面,固然這個機率跟中1億彩票的機率相差不大。

若是還不理解能夠參考我寫的專門的博客:

關於HashMap的:HashMap的實現原理--鏈表散列

關於Hashtable的:Hashtable數據存儲結構-遍歷規則,Hash類型的複雜度爲啥都是O(1)-源碼分析 

在看起來就是對Entry鏈表的循環的時間複雜度影響最大,鏈表查找的時間複雜度爲O(n),與鏈表長度有關。咱們要保證那個鏈表長度爲1,才能夠說時間複雜度能知足O(1)。但這麼說來只有那個hash算法儘可能減小衝突,才能使鏈表長度儘量短,理想狀態爲1。所以能夠得出結論:HashMap的查找時間複雜度只有在最理想的狀況下才會爲O(1),最差是O(n),而要保證這個理想狀態不是咱們開發者控制的。

======================================================================開始=======================================================================================

經常使用數據結構的時間複雜度

經常使用數據結構的時間複雜度
Data Structure 新增 查詢/Find 刪除/Delete GetByIndex
   數組  Array (T[]) O(n) O(n) O(n) O(1)
   鏈表 Linked list (LinkedList<T>) O(1) O(n) O(n) O(n)
Resizable array list (List<T>) O(1) O(n) O(n) O(1)
Stack (Stack<T>) O(1) - O(1) -
Queue (Queue<T>) O(1) - O(1) -
Hash table (Dictionary<K,T>) O(1) O(1) O(1) -
Tree-based dictionary(SortedDictionary<K,T>) O(log n) O(log n) O(log n) -
Hash table based set (HashSet<T>) O(1) O(1) O(1) -
Tree based set (SortedSet<T>) O(log n) O(log n) O(log n) -

 

 

 

 

 

 

 

 

 

 

如何選擇數據結構

Array (T[])

  • 當元素的數量是固定的,而且須要使用下標時。

Linked list (LinkedList<T>)

  • 當元素須要可以在列表的兩端添加時。不然使用 List<T>。

Resizable array list (List<T>)

  • 當元素的數量不是固定的,而且須要使用下標時。

Stack (Stack<T>)

  • 當須要實現 LIFO(Last In First Out)時。

Queue (Queue<T>)

  • 當須要實現 FIFO(First In First Out)時。

Hash table (Dictionary<K,T>)

  • 當須要使用鍵值對(Key-Value)來快速添加和查找,而且元素沒有特定的順序時。

Tree-based dictionary (SortedDictionary<K,T>)

  • 當須要使用價值對(Key-Value)來快速添加和查找,而且元素根據 Key 來排序時。

Hash table based set (HashSet<T>)

  • 當須要保存一組惟一的值,而且元素沒有特定順序時。

Tree based set (SortedSet<T>)

  • 當須要保存一組惟一的值,而且元素須要排序時。

Array

在計算機程序設計中,數組(Array)是最簡單的並且應用最普遍的數據結構之一。在任何編程語言中,數組都有一些共性:

  • 數組中的內容是使用連續的內存(Contiguous Memory)來存儲的。
  • 數組中的全部元素必須是相同的類型,或者類型的衍生類型。所以數組又被認爲是同質數據結構(Homegeneous Data Structures)。
  • 數組的元素能夠直接被訪問。好比你須要訪問數組的第 i 個元素,則能夠直接使用 arrayName[i] 來訪問。

對於數組的常規操做包括:

  • 分配空間(Allocation)
  • 數據訪問(Accessing)

在 C# 中,能夠經過以下的方式聲明數組變量。

int allocationSize = 10;
bool[] booleanArray = new bool[allocationSize];
FileInfo[] fileInfoArray = new FileInfo[allocationSize];

 

上面的代碼將在 CLR 託管堆中分配一塊連續的內存空間,用以容納數量爲 allocationSize ,類型爲 arrayType 的數組元素。若是 arrayType 爲值類型,則將會有 allocationSize 個未封箱(unboxed)的 arrayType 值被建立。若是 arrayType 爲引用類型,則將會有 allocationSize 個 arrayType 類型的引用被建立。

 

若是咱們爲 FileInfo[] 數組中的一些位置賦上值,則引用關係爲下圖所示。

 

.NET 中的數組都支持對元素的直接讀寫操做。語法以下:

// 讀數組元素
bool b = booleanArray[7];
 
// 寫數組元素
booleanArray[0] = false;

 

訪問一個數組元素的時間複雜度爲 O(1),所以對數組的訪問時間是恆定的。也就是說,與數組中包含的元素數量沒有直接關係,訪問一個元素的時間是相同的。

ArrayList

因爲數組是固定長度的,而且數組中只能存儲同一種類型或類型的衍生類型。這在使用中會受到一些限制。.NET 提供了一種數據結構 ArrayList 來解決這些問題。

ArrayList countDown = new ArrayList();
countDown.Add(3);
countDown.Add(2);
countDown.Add(1);
countDown.Add("blast off!");
countDown.Add(new ArrayList());

 

 

ArrayList 是長度可變的數組,而且它能夠存儲不一樣類型的元素。

 

但這些靈活性是以犧牲性能爲代價的。在上面 Array 的描述中,咱們知道 Array 在存儲值類型時是採用未裝箱(unboxed)的方式。因爲 ArrayList 的 Add 方法接受 object 類型的參數,致使若是添加值類型的值會發生裝箱(boxing)操做。這在頻繁讀寫 ArrayList 時會產生額外的開銷,致使性能降低。

List<T>

當 .NET 中引入泛型功能後,上面 ArrayList 所帶來的性能代價可使用泛型來消除。.NET 提供了新的數組類型 List<T>。

泛型容許開發人員在建立數據結構時推遲數據類型的選擇,直到使用時才肯定選擇哪一種類型。泛型(Generics)的主要優勢包括:

  • 類型安全(Type Safety):使用泛型定義的類型,在使用時僅能使用指定的類型或類型的衍生類型。
  • 性能(Performance):泛型移除了運行時類型檢測,消除了裝箱和拆箱的開銷。
  • 可重用(Reusability):泛型打破了數據結構與存儲數據類型之間的緊耦合。這提升了數據結構的可重用性。

List<T> 等同於同質的一維數組(Homogeneous self-redimensioning array)。它像 Array 同樣能夠快速的讀取元素,還能夠保持長度可變的靈活性。

// 建立 int 類型列表
List<int> myFavoriteIntegers = new List<int>();
 
// 建立 string 類型列表
List<string> friendsNames = new List<string>();

 

List<T> 內部一樣使用 Array 來實現,但它隱藏了這些實現的複雜性。當建立 List<T> 時無需指定初始長度,當添加元素到 List<T> 中時,也無需關心數組大小的調整(resize)問題。

 
List<int> powersOf2 = new List<int>();
 
  powersOf2.Add(1);
  powersOf2.Add(2);
 
  powersOf2[1] = 10;
 
  int sum = powersOf2[1] + powersOf2[2];

 

List<T> 的漸進運行時(Asymptotic Running Time)複雜度與 Array 是相同的。

LinkedList<T>

在鏈表(Linked List)中,每個元素都指向下一個元素,以此來造成了一個鏈(chain)。

 

向鏈表中插入一個新的節點的漸進時間取決於鏈表是不是有序的。若是鏈表不須要保持順序,則插入操做就是常量時間O(1),能夠在鏈表的頭部或尾部添加新的節點。而若是須要保持鏈表的順序結構,則須要查找到新節點被插入的位置,這使得須要從鏈表的頭部 head 開始逐個遍歷,結果就是操做變成了O(n)。下圖展現了插入節點的示例。

 

鏈表與數組的不一樣之處在於,數組的中的內容在內存中時連續排列的,能夠經過下標來訪問,而鏈表中內容的順序則是由各對象的指針所決定,這就決定了其內容的排列不必定是連續的,因此不能經過下標來訪問。若是須要更快速的查找操做,使用數組多是更好的選擇。

使用鏈表的最主要的優點就是,向鏈表中插入或刪除節點無需調整結構的容量。而相反,對於數組來講容量始終是固定的,若是須要存放更多的數據,則須要調整數組的容量,這就會發生新建數組、數據拷貝等一系列複雜且影響效率的操做。即便是 List<T> 類,雖然其隱藏了容量調整的複雜性,但仍然難逃性能損耗的懲罰。

鏈表的另外一個優勢就是特別適合以排序的順序動態的添加新元素。若是要在數組的中間的某個位置添加新元素,不只要移動全部其他的元素,甚至還有可能須要從新調整容量。

因此總結來講,數組適合數據的數量是有上限的狀況,而鏈表適合元素數量不固定的狀況。

在 .NET 中已經內置了 LinkedList<T> 類,該類實現了雙向鏈表(doubly-linked list)功能,也就是節點同時持有其左右節點的引用。而對於刪除操做,若是使用 Remove(T),則運算複雜度爲 O(n),其中 n 爲鏈表的長度。而若是使用 Remove(LinkedListNode<T>), 則運算複雜度爲 O(1)。

Queue<T>

當咱們須要使用先進先出順序(FIFO)的數據結構時,.NET 爲咱們提供了 Queue<T>。Queue<T> 類提供了 Enqueue 和 Dequeue 方法來實現對 Queue<T> 的存取。

Queue<T> 內部創建了一個存放 T 對象的環形數組,並經過 head 和 tail 變量來指向該數組的頭和尾。

 

默認狀況下,Queue<T> 的初始化容量是 32,也能夠經過構造函數指定容量。

Enqueue 方法會判斷 Queue<T> 中是否有足夠容量存放新元素。若是有,則直接添加元素,並使索引 tail 遞增。在這裏的 tail 使用求模操做以保證 tail 不會超過數組長度。若是容量不夠,則 Queue<T> 根據特定的增加因子擴充數組容量。

默認狀況下,增加因子(growth factor)的值爲 2.0,因此內部數組的長度會增長一倍。也能夠經過構造函數中指定增加因子。Queue<T> 的容量也能夠經過 TrimExcess 方法來減小。

Dequeue 方法根據 head 索引返回當前元素,以後將 head 索引指向 null,再遞增 head 的值。

Stack<T>

當須要使用後進先出順序(LIFO)的數據結構時,.NET 爲咱們提供了 Stack<T>。Stack<T> 類提供了 Push 和 Pop 方法來實現對 Stack<T> 的存取。

Stack<T> 中存儲的元素能夠經過一個垂直的集合來形象的表示。當新的元素壓入棧中(Push)時,新元素被放到全部其餘元素的頂端。當須要彈出棧(Pop)時,元素則被從頂端移除。

 

Stack<T> 的默認容量是 10。和 Queue<T> 相似,Stack<T> 的初始容量也能夠在構造函數中指定。Stack<T> 的容量能夠根據實際的使用自動的擴展,而且能夠經過 TrimExcess 方法來減小容量。

若是 Stack<T> 中元素的數量 Count 小於其容量,則 Push 操做的複雜度爲 O(1)。若是容量須要被擴展,則 Push 操做的複雜度變爲 O(n)。Pop 操做的複雜度始終爲 O(1)。

Hashtable

如今咱們要使用員工的社保號做爲惟一標識進行存儲。社保號的格式爲 DDD-DD-DDDD(D 的範圍爲數字 0-9)。

若是使用 Array 存儲員工信息,要查詢社保號爲 111-22-3333 的員工,則將會嘗試遍歷數組的全部選擇,即執行復雜度爲 O(n) 的查詢操做。好一些的辦法是將社保號排序,以使查詢複雜度下降到 O(log(n))。但理想狀況下,咱們更但願查詢複雜度爲 O(1)。

一種方案是創建一個大數組,範圍從 000-00-0000 到 999-99-9999 。

 

這種方案的缺點是浪費空間。若是咱們僅須要存儲 1000 個員工的信息,那麼僅利用了 0.0001% 的空間。

第二種方案就是用哈希函數(Hash Function)壓縮序列。

咱們選擇使用社保號的後四位做爲索引,以減小區間的跨度。這樣範圍將從 0000 到 9999。

 

在數學上,將這種從 9 位數轉換爲 4 位數的方式稱爲哈希轉換(Hashing)。能夠將一個數組的索引空間(indexers space)壓縮至相應的哈希表(Hash Table)。

在上面的例子中,哈希函數的輸入爲 9 位數的社保號,輸出結果爲後 4 位。

H(x) = last four digits of x

 

上圖中也說明在哈希函數計算中常見的一種行爲:哈希衝突(Hash Collisions)。即有可能兩個社保號的後 4 位均爲 0000。

當要添加新元素到 Hashtable 中時,哈希衝突是致使操做被破壞的一個因素。若是沒有衝突發生,則元素被成功插入。若是發生了衝突,則須要判斷衝突的緣由。所以,哈希衝突提升了操做的代價,Hashtable 的設計目標就是要儘量減低衝突的發生。

避免哈希衝突的一個方法就是選擇合適的哈希函數。哈希函數中的衝突發生的概率與數據的分佈有關。例如,若是社保號的後 4 位是隨即分佈的,則使用後 4 位數字比較合適。但若是後 4 位是以員工的出生年份來分配的,則顯然出生年份不是均勻分佈的,則選擇後 4 位會形成大量的衝突。

咱們將選擇合適的哈希函數的方法稱爲衝突避免機制(Collision Avoidance)。

在處理衝突時,有不少策略能夠實施,這些策略稱爲衝突解決機制(Collision Resolution)。其中一種方法就是將要插入的元素放到另一個塊空間中,由於相同的哈希位置已經被佔用。

例如,最簡單的一種實現就是線性挖掘(Linear Probing),步驟以下:

  1. 當插入新的元素時,使用哈希函數在哈希表中定位元素位置;
  2. 檢查哈希表中該位置是否已經存在元素。若是該位置內容爲空,則插入並返回,不然轉向步驟 3。
  3. 若是該位置爲 i,則檢查 i+1 是否爲空,若是已被佔用,則檢查 i+2,依此類推,直到找到一個內容爲空的位置。

如今若是咱們要將五個員工的信息插入到哈希表中:

Alice (333-33-1234)
Bob (444-44-1234)
Cal (555-55-1237)
Danny (000-00-1235)
Edward (111-00-1235)

 

則插入後的哈希表可能以下:

 

元素的插入過程:

Alice 的社保號被哈希爲 1234,所以存放在位置 1234。
Bob 的社保號被哈希爲 1234,但因爲位置 1234 處已經存放 Alice 的信息,則檢查下一個位置 1235,1235 爲空,則 Bob 的信息就被放到 1235。
Cal 的社保號被哈希爲 1237,1237 位置爲空,因此 Cal 就放到 1237 處。
Danny 的社保號被哈希爲 1235,1235 已被佔用,則檢查 1236 位置是否爲空,1236 爲空,因此 Danny 就被放到 1236。
Edward 的社保號被哈希爲 1235,1235 已被佔用,檢查1236,也被佔用,再檢查1237,直到檢查到 1238時,該位置爲空,因而 Edward 被放到了1238 位置。

 

線性挖掘(Linear Probing)方式雖然簡單,但並非解決衝突的最好的策略,由於它會致使同類哈希的彙集。這致使搜索哈希表時,衝突依然存在。例如上面例子中的哈希表,若是咱們要訪問 Edward 的信息,由於 Edward 的社保號 111-00-1235 哈希爲 1235,然而咱們在 1235 位置找到的是 Bob,因此再搜索 1236,找到的倒是 Danny,以此類推直到找到 Edward。

一種改進的方式爲二次挖掘(Quadratic Probing),即每次檢查位置空間的步長爲平方倍數。也就是說,若是位置 s 被佔用,則首先檢查 s + 12 處,而後檢查s – 12,s + 22,s – 22,s + 32 依此類推,而不是象線性挖掘那樣以 s + 1,s + 2 … 方式增加。儘管如此,二次挖掘一樣也會致使同類哈希彙集問題。

.NET 中的 Hashtable 的實現,要求添加元素時不只要提供元素(Item),還要爲該元素提供一個鍵(Key)。例如,Key 爲員工社保號,Item 爲員工信息對象。能夠經過 Key 做爲索引來查找 Item。

Hashtable employees = new Hashtable();
 
      // Add some values to the Hashtable, indexed by a string key
      employees.Add("111-22-3333", "Scott");
      employees.Add("222-33-4444", "Sam");
      employees.Add("333-44-55555", "Jisun");
 
      // Access a particular key
      if (employees.ContainsKey("111-22-3333"))
      {
        string empName = (string)employees["111-22-3333"];
        Console.WriteLine("Employee 111-22-3333's name is: "  + empName);
      }
      else
        Console.WriteLine("Employee 111-22-3333 is not in the hash table...");

 

Hashtable 類中的哈希函數比前面介紹的社保號的實現要更爲複雜。哈希函數必須返回一個序數(Ordinal Value)。對於社保號的例子,經過截取後四位就能夠實現。但實際上 Hashtable 類能夠接受任意類型的值做爲 Key,這都要歸功於 GetHashCode 方法,一個定義在 System.Object 中的方法。GetHashCode 的默認實現將返回一個惟一的整數,而且保證在對象的生命週期內保持不變。

Hashtable 類中的哈希函數定義以下:

H(key) = [GetHash(key) + 1 + (((GetHash(key) >> 5) + 1) % (hashsize – 1))] % hashsize

 

 

這裏的 GetHash(key) 默認是調用 key 的 GetHashCode 方法以獲取返回的哈希值。hashsize 指的是哈希表的長度。由於要進行求模,因此最後的結果 H(key) 的範圍在 0 至 hashsize – 1 之間。

當在哈希表中添加或獲取一個元素時,會發生哈希衝突。前面咱們簡單地介紹了兩種衝突解決策略:

線性挖掘(Linear Probing)
二次挖掘(Quadratic Probing)

 

在 Hashtable 類中則使用的是一種徹底不一樣的技術,稱爲二度哈希(rehashing)(有些資料中也將其稱爲雙精度哈希(double hashing))。

二度哈希的工做原理以下:

有一個包含一組哈希函數 H1…Hn 的集合。當須要從哈希表中添加或獲取元素時,首先使用哈希函數 H1。若是致使衝突,則嘗試使用 H2,以此類推,直到 Hn。全部的哈希函數都與 H1 十分類似,不一樣的是它們選用的乘法因子(multiplicative factor)。

一般,哈希函數 Hk 的定義以下:

Hk(key) = [GetHash(key) + k * (1 + (((GetHash(key) >> 5) + 1) % (hashsize – 1)))] % hashsize

 

 

當使用二度哈希時,重要的是在執行了 hashsize 次挖掘後,哈希表中的每個位置都有且只有一次被訪問到。也就是說,對於給定的 key,對哈希表中的同一位置不會同時使用 Hi 和 Hj。在 Hashtable 類中使用二度哈希公式,其始終保持 (1 + (((GetHash(key) >> 5) + 1) % (hashsize – 1)) 與 hashsize 互爲素數(兩數互爲素數表示二者沒有共同的質因子)。

二度哈希較前面介紹的線性挖掘(Linear Probing)和二次挖掘(Quadratic Probing)提供了更好的避免衝突的策略。

Hashtable 類中包含一個私有成員變量 loadFactor,loadFactor 指定了哈希表中元素數量與位置(slot)數量之間的最大比例。例如:若是 loadFactor 等於 0.5,則說明哈希表中只有一半的空間存放了元素值,其他一半都爲空。

哈希表的構造函數容許用戶指定 loadFactor 值,定義範圍爲 0.1 到 1.0。然而,無論你提供的值是多少,範圍都不會超過 72%。即便你傳遞的值爲 1.0,Hashtable 類的 loadFactor 值仍是 0.72。微軟認爲loadFactor 的最佳值爲 0.72,這平衡了速度與空間。所以雖然默認的 loadFactor 爲 1.0,但系統內部卻自動地將其改變爲 0.72。因此,建議你使用缺省值1.0(但實際

// Add some employees
employeeData.Add(455110189) = new Employee("Scott Mitchell");
employeeData.Add(455110191) = new Employee("Jisun Lee");
 
// See if employee with SSN 123-45-6789 works here
if (employeeData.ContainsKey(123456789))

 

上是 0.72)。

向 Hashtable 中添加新元素時,須要檢查以保證元素與空間大小的比例不會超過最大比例。若是超過了,哈希表空間將被擴充。步驟以下:

  • 哈希表的位置空間幾乎被翻倍。準確地說,位置空間值從當前的素數值增長到下一個最大的素數值。
  • 由於二度哈希時,哈希表中的全部元素值將依賴於哈希表的位置空間值,因此表中全部值也須要從新二度哈希。

由此看出,對哈希表的擴充將是以性能損耗爲代價。所以,咱們應該預先估計哈希表中最有可能容納的元素數量,在初始化哈希表時給予合適的值進行構造,以免沒必要要的擴充。

Dictionary<K,T>

Hashtable 類是一個類型鬆耦合的數據結構,開發人員能夠指定任意的類型做爲 Key 或 Item。當 .NET 引入泛型支持後,類型安全的 Dictionary<K,T> 類出現。Dictionary<K,T> 使用強類型來限制 Key 和 Item,當建立 Dictionary<K,T> 實例時,必須指定 Key 和 Item 的類型。

 
Dictionary<keyType, valueType> variableName = new Dictionary<keyType, valueType>();

 



若是繼續使用上面描述的社保號和員工的示例,咱們能夠建立一個 Dictionary<K,T> 的實例:

Dictionary<int, Employee> employeeData = new Dictionary<int, Employee>();

 

這樣咱們就能夠添加和刪除員工信息了。

Dictionary<K,T> 與 Hashtable 的不一樣之處還不止一處。除了支持強類型外,Dictionary<K,T> 還採用了不一樣的衝突解決策略(Collision Resolution Strategy),這種新的技術稱爲鏈技術(chaining)。

前面使用的挖掘技術(probing),若是發生衝突,則將嘗試列表中的下一個位置。若是使用二度哈希(rehashing),則將致使全部的哈希被從新計算。而新的鏈技術(chaining)將採用額外的數據結構來處理衝突。Dictionary<K,T> 中的每一個位置(slot)都映射到了一個數組。當衝突發生時,衝突的元素將被添加到桶(bucket)列表中。

下面的示意圖中描述了 Dictionary<K,T> 中的每一個桶(bucket)都包含了一個鏈表以存儲相同哈希的元素。

上圖中,該 Dictionary 包含了 8 個桶,也就是自頂向下的黃色背景的位置。必定數量的 Employee 對象已經被添加至 Dictionary 中。若是一個新的 Employee 要被添加至 Dictionary 中,將會被添加至其 Key 的哈希所對應的桶中。若是在相同位置已經有一個 Employee 存在了,則將會將新元素添加到列表的前面。

向 Dictionary 中添加元素的操做涉及到哈希計算和鏈表操做,但其仍爲常量,複雜度爲 O(1)。

對 Dictionary 進行查詢和刪除操做時,其平均時間取決於 Dictionary 中元素的數量和桶(bucket)的數量。具體的說就是運行時間爲 O(n/m),這裏 n 爲元素的總數量,m 是桶的數量。但 Dictionary 幾乎老是被實現爲 n = m,也就是說,元素的總數毫不會超過桶的總數。因此 O(n/m) 也變成了常量 O(1)。 

參考:經常使用數據結構的時間複雜度

參考:關於hash表的時間複雜度

參考:面試中關於HashMap的時間複雜度O(1)的思考

相關文章
相關標籤/搜索