字典和集合是進行過性能高度優化的 數據結構,特別是對於查找、添加和刪除操做。本節將結合實例介紹它們在具體場景下的性能表現,以及與列表等其餘數據結構的對比。
例如,有一個存儲產品信息(產品 ID、名稱和價格)的列表,如今的需求是,藉助某件產品的ID找出其價格。則實現代碼以下:html
def find_product_price(products, product_id): for id, price in products: if id == product_id: return price return None products = [ (111, 100), (222, 30), (333, 150) ] print('The price of product 222 is {}'.format(find_product_price(products, 222)))
運行結果爲:python
The price of product 222 is 30
在上面程序的基礎上,若是列表有 n 個元素,由於查找的過程須要遍歷列表,那麼最壞狀況下的時間複雜度就爲 O(n)
。即便先對列表進行排序,再使用二分查找算法,也須要 O(logn)
的時間複雜度,更況且列表的排序還須要 O(nlogn)
的時間。
但若是用字典來存儲這些數據,那麼查找就會很是便捷高效,只需 O(1)
的時間複雜度就能夠完成,由於能夠直接經過鍵的哈希值,找到其對應的值,而不須要對字典作遍歷操做,實現代碼以下:算法
products = { 111: 100, 222: 30, 333: 150 } print('The price of product 222 is {}'.format(products[222]))
運行結果爲:服務器
The price of product 222 is 30
有些讀者可能對時間複雜度並無直觀的認識,不要緊,再給你們列舉一個實例。下面的代碼中,初始化了含有 100,000 個元素的產品,並分別計算出了使用列表和集合來統計產品價格數量的運行時間:數據結構
# 統計時間須要用到 time 模塊中的函數,瞭解便可 import time def find_unique_price_using_list(products): unique_price_list = [] for _, price in products: # A if price not in unique_price_list: #B unique_price_list.append(price) return len(unique_price_list) id = [x for x in range(0, 100000)] price = [x for x in range(200000, 300000)] products = list(zip(id, price)) # 計算列表版本的時間 start_using_list = time.perf_counter() find_unique_price_using_list(products) end_using_list = time.perf_counter() print("time elapse using list: {}".format(end_using_list - start_using_list)) # 使用集合完成一樣的工做 def find_unique_price_using_set(products): unique_price_set = set() for _, price in products: unique_price_set.add(price) return len(unique_price_set) # 計算集合版本的時間 start_using_set = time.perf_counter() find_unique_price_using_set(products) end_using_set = time.perf_counter() print("time elapse using set: {}".format(end_using_set - start_using_set))
運行結果爲:app
time elapse using list: 68.78650900000001 time elapse using set: 0.010747099999989018能夠看到,僅僅十萬的數據量,二者的速度差別就如此之大。而每每企業的後臺數據都有上億乃至十億數量級,所以若是使用了不合適的數據結構,很容易形成服務器的崩潰,不但影響用戶體驗,而且會給公司帶來巨大的財產損失。
| 哈希值 (hash) 鍵 (key) 值 (value) . | ... 0 | hash0 key0 value0 . | ... 1 | hash1 key1 value1 . | ... 2 | hash2 key2 value2 . | ...這種結構的弊端是,隨着哈希表的擴張,它會變得愈來愈稀疏。好比,有這樣一個字典:
{'name': 'mike', 'dob': '1999-01-01', 'gender': 'male'}那麼它會存儲爲相似下面的形式:
entries = [ ['--', '--', '--'] [-230273521, 'dob', '1999-01-01'], ['--', '--', '--'], ['--', '--', '--'], [1231236123, 'name', 'mike'], ['--', '--', '--'], [9371539127, 'gender', 'male'] ]顯然,這樣很是浪費存儲空間。爲了提升存儲空間的利用率,如今的哈希表除了字典自己的結構,會把索引和哈希值、鍵、值單獨分開,也就是採用以下這種結構:
Indices ---------------------------------------------------- None | index | None | None | index | None | index ... ---------------------------------------------------- Entries -------------------- hash0 key0 value0 --------------------- hash1 key1 value1 --------------------- hash2 key2 value2 --------------------- ... ---------------------
indices = [None, 1, None, None, 0, None, 2] entries = [ [1231236123, 'name', 'mike'], [-230273521, 'dob', '1999-01-01'], [9371539127, 'gender', 'male'] ]經過對比能夠發現,空間利用率獲得很大的提升。
當向字典中插入數據時,Python 會首先根據鍵(key)計算出對應的哈希值(經過 hash(key) 函數),而向集合中插入數據時,Python會根據該元素自己計算對應的哈希值(經過 hash(valuse) 函數)。
例如:函數
dic = {"name":1} print(hash("name")) setDemo = {1} print(hash(1))
運行結果爲:性能
8230115042008314683 1獲得哈希值(例如爲 hash)以後,再結合字典或集合要存儲數據的個數(例如 n),就能夠獲得該元素應該插入到哈希表中的位置(好比,能夠用 hash%n 的方式)。
具體遇到哈希衝突時,各解決方法的具體含義可閱讀《哈希表詳解》一節作詳細瞭解。優化
這裏的找到空位,表示哈希表中沒有存儲目標元素。spa
O(1)
。