iOS開發(fā)之哈希表、時間復雜度、鏈表

哈希表(Hash table)

哈希表(Hash table,也叫散列表),是根據關鍵碼值(Key value)而直接進行訪問的數(shù)據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數(shù)叫做散列函數(shù),存放記錄的數(shù)組叫做散列表。
哈希表hashtable(key,value) 就是把Key通過一個固定的算法函數(shù)既所謂的哈希函數(shù)轉換成一個整型數(shù)字即哈希值,然后就將該數(shù)字對數(shù)組長度進行取余,取余結果就當作數(shù)組的下標,將value存儲在以該數(shù)字為下標的數(shù)組空間里。
而當使用哈希表進行查詢的時候,就是再次使用哈希函數(shù)將key轉換為對應的數(shù)組下標,并定位到該空間獲取value,如此一來,就可以充分利用到數(shù)組的定位性能進行數(shù)據定位。

哈希表的本質是一個數(shù)組,數(shù)組中每一個元素稱為一個箱子(bin),箱子中存放的是鍵值對。數(shù)組長度即箱子數(shù)。
哈希表還有一個重要的屬性: 負載因子(load factor),它用來衡量哈希表的 空/滿 程度,一定程度上也可以體現(xiàn)查詢的效率,計算公式為:
負載因子 = 總鍵值對數(shù) / 箱子個數(shù)
負載因子越大,意味著哈希表越滿,越容易導致沖突,性能也就越低。因此,一般來說,當負載因子大于某個常數(shù)(可能是 1,或者 0.75 等)時,哈希表將自動擴容。

哈希表在自動擴容時,一般會創(chuàng)建兩倍于原來個數(shù)的箱子,因此即使 key 的哈希值不變,對箱子個數(shù)取余的結果也會發(fā)生改變,因此所有鍵值對的存放位置都有可能發(fā)生改變,這個過程也稱為重哈希(rehash)。

哈希表的擴容并不總是能夠有效解決負載因子過大的問題。假設所有 key 的哈希值都一樣,那么即使擴容以后他們的位置也不會變化。雖然負載因子會降低,但實際存儲在每個箱子中的鏈表長度并不發(fā)生改變,因此也就不能提高哈希表的查詢性能。

基于以上總結,細心的讀者可能會發(fā)現(xiàn)哈希表的兩個問題:

如果哈希表中本來箱子就比較多,擴容時需要重新哈希并移動數(shù)據,性能影響較大。
如果哈希函數(shù)設計不合理,哈希表在極端情況下會變成線性表,性能極低。

時間復雜度

時間復雜度是一個偏理論的概念,我們要描述它,首先需要了解它的描述方法,即:「大 O 表示法」。
其實大 O 表示法的意思挺簡單的,就是表示:隨著輸入的值變化,程序運行所需要的時間與輸入值的變化關系。如果不理解也沒關系,我們看兩行代碼就很容易懂了。

我們先看第一個代碼,這是一個函數(shù),輸入一個數(shù)組,輸出這個數(shù)組里元數(shù)的和。

int count(int a[], int n) {
    int result = 0;
    for (int i = 0; i < n; ++i) {
        result += a[i];
    }
    return result;
}

對于這個程序來說,如果它處理 N 個元素求和所花的時間是 T,那么它處理 N2 個元素的和所花的時間就是 T2。所以隨著 N 變大,時間 T 的變大是與 N 呈「線性」關系的。

在時間復雜度中,我們用 O(N) 表示這種「線性」時間復雜度。

那是不是所有的函數(shù)都是「線性」關系的呢?我們再來看下面的程序。這是一個二分查找程序,從一個有序數(shù)組中尋找指定的值。

int binary_search(int A[], int key, int imin, int imax)
{
    if (imax < imin) {
        return KEY_NOT_FOUND;
    } else {
        int imid = midpoint(imin, imax);
        if (A[imid] > key)
            return binary_search(A, key, imin, imid - 1);
        else if (A[imid] < key)
            return binary_search(A, key, imid + 1, imax);
        else
            return imid;
    }
}

對于這個程序來說,如果它處理 N 個元素求和所花的時間是 T,那么它處理 N 2 個元素的和所花的時間是多少呢?是 T 2 嗎?

如果頭腦算不清楚,我們可以拿實際的數(shù)字來實驗,二分查找每次(幾乎)可以去掉一半的候選數(shù)字。所以假如 N = 1024,那么它最多要找多少次呢?答案是 10 次,因為 2^10 = 1024,每次去掉一半,10 次之后就只剩下唯一一個元素了。

好,這個時候,如果元素的個數(shù)翻一倍,變成 2048 個,那么它最多要找多少次呢?相信大家都能算出來吧?答案是 11 次,因為 2 ^ 11 = 2048。

所以在這個例子中,輸入的元素個數(shù)雖然翻倍,但是程序運行所花的時間卻只增加了 1,我們把這種時間復雜度要叫「對數(shù)」時間復雜度,用 O(logN) 來表示。

除了剛剛講的「線性」時間復雜度和「對數(shù)」時間復雜度。我們還有以下這次常見的時間復度數(shù)。

「常數(shù)」時間復雜度,例如返回一個有序數(shù)組中的最小數(shù),這個數(shù)因為始終在第一個位置,所以就不會受到數(shù)組大小的影響,無論數(shù)組多大,我們都可以在一個固定的時間返回結果。

「線性對數(shù)」時間復雜度,即 O(N*logN),這個復雜度比較常見,因為常見的高效的排序算法,都是這個時間復雜度,比如快速排序,堆排序,歸并排序等。

別的時間復雜度還有很多,每個具體的問題,我們都可以通過具體的分析,得到這個問題的時間復雜度。

總結一下學習時間復雜度的知識對于我們的工作有什么用:

  1. 對于不同的數(shù)據規(guī)模,能夠決策采用不同的解決方案。
  1. 了解什么情況下用暴力解法就能夠解決問題,避免寫復雜的代碼。
  2. 在寫代碼之前,就能夠預估程序的運行時間,從而可以知道是否能夠滿足產品需求。
    4.在程序出現(xiàn)性能瓶頸時,能夠有解決方案而不是抓瞎。

鏈表

鏈式存儲的線性表,簡稱鏈表。鏈表由多個鏈表元素組成,這些元素稱為節(jié)點。結點之間通過邏輯連接,形成鏈式存儲結構。存儲結點的內存單元,可以是連續(xù)的也可以是不連續(xù)的。邏輯連接與物理存儲次序沒有關系。
**鏈表分為兩個域: **
值域:用于存放結點的值
鏈域:用于存放下一個結點的地址或位置
從內存角度出發(fā): 鏈表可分為 靜態(tài)鏈表、動態(tài)鏈表。
從鏈表存儲方式的角度出發(fā):鏈表可分為 單鏈表、雙鏈表、以及循環(huán)鏈表。

靜態(tài)鏈表:
把線性表的元素存放在數(shù)組中,這些元素可能在物理上是連續(xù)存放的,也有可能不是連續(xù)的,它們之間通過邏輯關系來連接,數(shù)組單位存放鏈表結點,結點的鏈域指向下一個元素的位置,即下一個元素所在的數(shù)組單元的下標。顯然靜態(tài)鏈表需要數(shù)組來實現(xiàn)。
引出的問題:數(shù)組的長度定義的問題,無法預支。

**動態(tài)鏈表:(實際當中用的最多) **
改善了靜態(tài)鏈表的缺點。它動態(tài)的為節(jié)點分配存儲單元。當有節(jié)點插入時,系統(tǒng)動態(tài)的為結點分配空間。在結點刪除時,應該及時釋放相應的存儲單元,以防止內存泄露。

**單鏈表: **
單鏈表是一種順序存儲的結構。
有一個頭結點,沒有值域,只有連域,專門存放第一個結點的地址。
有一個尾結點,有值域,也有鏈域,鏈域值始終為NULL。
所以,在單鏈表中為找第i個結點或數(shù)據元素,必須先找到第i - 1 結點或數(shù)據元素,而且必須知道頭結點,否者整個鏈表無法訪問。

循環(huán)鏈表:
循環(huán)鏈表,類似于單鏈表,也是一種鏈式存儲結構,循環(huán)鏈表由單鏈表演化過來。單鏈表的最后一個結點的鏈域指向NULL,而循環(huán)鏈表的建立,不要專門的頭結點,讓最后一個結點的鏈域指向鏈表結點。

循環(huán)鏈表與單鏈表的區(qū)別
區(qū)別一、鏈表的建立。單鏈表需要創(chuàng)建一個頭結點,專門存放第一個結點的地址。單鏈表的鏈域指向NULL。而循環(huán)鏈表的建立,不要專門的頭結點,讓最后一個結點的鏈域指向鏈表的頭結點。
區(qū)別二、鏈表表尾的判斷。單鏈表判斷結點是否為表尾結點,只需判斷結點的鏈域值是否是NULL。如果是,則為尾結點;否則不是。而循環(huán)鏈表盤判斷是否為尾結點,則是判斷該節(jié)點的鏈域是不是指向鏈表的頭結點。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 大學的時候不好好學習,老師在講臺上講課,自己在以為老師看不到的座位看小說,現(xiàn)在用到了老師講的知識,只能自己看書查資...
    和玨貓閱讀 1,565評論 1 3
  • 本文內容取自于小甲魚的數(shù)據結構與算法。http://m.itdecent.cn/p/230e6fde9c75 ...
    阿阿阿阿毛閱讀 3,105評論 0 7
  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,917評論 0 11
  • 問卿何須匆匆 帶傷帶淚不由衷 情意綿綿 心將絕 訴離愁 暗垂淚珠流 人在何處 君難知否 獨處空樓 冷冷清清 白了少...
    滿地落葉閱讀 197評論 0 0
  • 是圍著火爐烤火的時候,媽媽問我:“現(xiàn)在我跟你爸的錢都給你用了,那等到我們老了的時候,不知道你哥會不會孝順我們,給我...
    大啊大的閱讀 353評論 0 0

友情鏈接更多精彩內容