大話數據結構


第一章: 數據結構緒論

  • 術語
    • 數據:是描敘客觀事物的符號,是計算機中可以操作的對象,是能被計算機識別,並輸入給計算機處理的符號集合
    • 數據元素: 是組成數據的、有一定意義的基本單位,在計算機中通常作為整體處理。也被成為記錄。
    • 數據項:一個數據元素可以由若干個數據項組成。數據項是數據不可分割的最小單位。
    • 數據對象:是性質相同的數據元素的集合,是數據的子集。
    • 數據結構:是相互之間存在一種或多種特定關系的數據元素的集合
  • 邏輯結構與物理結構
    • 邏輯結構:是指數據對象中數據元素之間的相互關系
      • 集合結構:集合結構中的數據元素
      • 線性結構:線性結構中的數據元素之間是一對一的關系
      • 樹形結構:樹形結構中的數據元素之間存在一種一對多的層次關系
      • 圖形結構:圖形結構的數據元素是多對多的關系
    • 物理結構: 是指數據的邏輯結構在計算機中的存儲形式
      • 順序存儲結構: 是把數據元素存放在地址連續的存儲單元里,其數據間的邏輯關系和物理關系是一致的
      • 鏈式存儲結構:是把數據元素存放在任意的存儲單元里,這組存儲單元可以是連續的,也可以是不連續的。
    • 抽象數據類型
      • 數據類型: 是指一組性質相同的值的集合及定義在此集合上的一些操作的總稱。
        • 原子類型: 是不可以再分解的基本類型, 包括整型、實型、字符型
        • 結構類型: 由若干個類型組合而成,是可以再分解的。例如,整型數組是由若干整型數據組成的。
      • 抽象:是指抽取出事物具有的普遍性的本質, 意義在於數據類型的數學抽象特性。
      • 抽象數據類型(abstract data type, adt):是指一個數學模型及定義在該模型上的一組操作。
      • 抽象數據類型體現了程序設計中問題分解、抽象和信息隱藏的特性

第二章:算法

  • 算法: 算法是解決特定問題求解步驟的描敘,在計算機中表現為指令的有限序列,並且每條指令表示一個或多個操作。
  • 算法特性:
    • 輸入輸出:算法具有零個或多個輸入,算法至少有一個或多個輸出
    • 有窮性:指算法在執行有限的步驟之后,自動結束而不會出現無限循環,並且每一個步驟在可接受的時間內完成。
    • 確定性:算法的每一步驟都具有確定的含義,不會出現二義性。
    • 可行性:算法的每一步都必須是可行的,也就是說,每一步都能通過執行有限次數完成。
  • 算法設計的要求:
    • 正確性: 算法的正確性是指算法至少具有輸入、輸出和加工處理無歧義行、能正確反映問題的需求、能夠得到問題的正確答案。
    • 可讀性: 算法設計的另一目的是為了便於便於閱讀、理解和交流
    • 健壯性: 當輸入數據不合法時,算法也能做出相關處理,而不是產生異常或莫名其妙的結果。
    • 時間效率高和存儲量低
  • 算法效率的度量方法
    • 事后統計方法:這種方法主要是通過設計好的測試程序和數據,利用計算法計時器對不同算法編制的程序的運行時間進行比較,從而確定算法效率的高低
    • 事前分析估算方法: 在計算機程序編制前,依據統計方法對算法進行估算
      • 一個程序的運行時間,依賴於算法的好壞和問題的輸入規模。所謂問題的輸入規模是指輸入量的多少。
      • 最終,在分析程序的運行時間時,最重要的是把程序看成是獨立於程序設計語言的算法或一系列步驟
* 函數的漸近增長: 給定兩個函數f(n)和g(n),如果存在一個整數N,是的對於所有的n > N,f(n)總是比g(n)大,那么,我們說f(n)的增長漸進快於g(n).
* 判斷一個算法的效率時,函數中的常數和其他次要項常常可以忽略,而更應該關注主項(最高階項)的階數。
  • 算法時間復雜度
    • 算法時間復雜度定義:在進行算法分析時,語句總的執行次數T(n)是關於問題規模n的函數,進而分析T(n)隨n的變化情況並確定T(n)的數量級。算法的時間復雜度,也就是算法的時間量度,記作:T(n)=O(f(n))。它表示隨問題規模n的增大,算法執行時間的增長率和f(n)的增長率相同,稱作算法的漸近時間復雜度,簡稱為時間復雜度。其中f(n)是問題規模n的某個函數。
    • 推導大O階方法:
      1. 用常數1取代運行時間中的所有加法常數。
      2. 在修改后的運行次數函數中,只保留最高階項。
      3. 如果最高階項存在且不是1,則去除與這個項相乘的常數。得到的結果就是大O階。
    • 常數階
    • 線性階:分析算法的復雜度,關鍵就是要分析循環結構的運行情況
    • 對數階
    • 平方階
  • 常見的時間復雜度
    • 常用的時間復雜度所耗費的時間從小到大依次是:
      O(1) < O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn))
    • 最壞情況與平均情況
      • 最壞情況運行時間是一種保證,那就是運行時間將不會再壞了。在應用中,這是一種最重要的需求,通常,除非特別指定,我們提到的運行時間都是最壞情況的運行時間。
      • 平均運行時間是所有情況中最有意義的,因為它是期望的運行時間。
      • 一般在沒有特殊說明的情況下,都是指最壞時間復雜度。
    • 算法空間復雜度
      • 算法的空間復雜度通過計算算法所需的存儲空間實現,算法空間復雜度的計算公式記作:S(n)=O(f(n)), 其中,n為為題的規模,f(n)為語句關於n所占存儲空間的函數。
    • 總結回顧

第三章 線性表

  • 線性表的定義

    • 線性表:零個或多個數據元素的有限序列
      • 若將線性表記為(a1,----,ai-1,an),則表中ai-1領先於ai,ai領先於ai+1,稱ai-1是ai的直接前驅元素,ai+1是ai的直接后驅元素。當i=1,2,...,n-1時,ai有且僅有一個直接后繼,當i=2,3,...,n時,ai有且僅有一個直接前驅。
      • 線性表元素的個數n(n >= 0)定義為線性表的長度, 當n=0時, 稱為空表.
      • 在較復雜的線性表中,一個數據元素可以由若干個數據項組成。
  • 線性表的抽象數據類型

    • ADT 線性表(List)
      • 線性表的數據對象集合為{a1,a2,...,an},每個元素的類型均為DataType.其中,除第一個元素a1外,每一個元素有且只有一個直接前驅元素,除了最后一個元素an外,每一個元素有且只有一個直接后繼元素。數據元素之間的關系是一對一的關系。
      • operation:
        • InitList(*L):初始化操作,建立一個空線性表L。
        • ListEmpty(L): 若線性表為空,返回true,否則返回false.
        • ClearList(*L): 將線性表清空。
        • GetElem(L,i,*e): 將線性表L中的第i個位置元素值返回給e.
        • LocateElem(L,e): 在線性表L中查找與給定值e相等的元素,如果查找成功,返回該元素在表中序號表示成功;否則,返回0表示失敗。
        • ListInsert(*L,i,e):在線性表L中的第i個位置插入新元素e.
        • ListDelete(L,i,e): 刪除線性表L中第i個位置元素,並用e返回其值。
        • ListLength(L): 返回線性表L的元素個數。
  • 線性表的順序存儲結構

    • 順序存儲定義:線性表的順序存儲結構,指的是用一段地址連續的存儲單元依次存儲線性表的數據元素。
    • 地址計算方法:
      • 存儲器中的每個存儲單元都有自己的編號,這個編號稱為地址。
  • 順序存儲結構的插入與刪除

  • 線性表順序存儲結構的優缺點

    • 優點
      • 無需為表示表中元素之間的邏輯關系而增加額外的存儲空間
      • 可以快速地存取表中任一位置的元素
    • 缺點
      • 插入和刪除操作需要移動大量元素
      • 當線性表長度變化較大時,難以確定存儲空間的容量
      • 造成存儲空間的碎片
  • 線性表的鏈式存儲結構

    • 線性表鏈式存儲結構定義
    • 節點由存放數據元素的數據域和存放后繼節點地址的指針域組成
    • 對於插入和刪除數據越頻繁的操作,單鏈表的效率優勢就越是明顯。
  • 單鏈表結構與順序存儲結構優缺點

    • 存儲分配方式:
      • 順序存儲結構用一段連續的存儲單元依次存儲線性表的數據元素
      • 單鏈表采用鏈式存儲結構,用一組任意的存儲單元存放線性表的元素
    • 時間性能
      • 查找
        • 順序存儲結構O(1)
        • 單鏈表O(n)
      • 插入和刪除
        • 順序存儲結構需要平均移動表長一半的元素,時間為O(n)
        • 單鏈表在線出某位置的指針后,插入和刪除時間僅為O(1)
    * 空間性能
        * 順序存儲結構需要預分配存儲空間,分大了,浪費,分小了易發生上溢
        * 單鏈表不需要分配存儲空間,只要有就可以分配,元素個數也不受限制。
    
    • 靜態鏈表

      • 用數組描敘的鏈表叫做靜態鏈表
      • 靜態鏈表優缺點
        • 優點:在插入和刪除操作時,只需要修改游標,不需要移動元素,從而改進了在順序存儲結構中的插入和刪除操作需要移動大量元素的缺點。
        • 缺點:沒有解決連續存儲分配帶來的表長難以確定的問題
        • 失去了順序存儲結構隨機存取的特性
    • 循環鏈表: 將單鏈表中終端結點的指針端由空指針改為指向頭結點,就使整個單鏈表形成一個環,這種頭尾相接的單鏈表稱為單循環鏈表,簡稱循環鏈表。

    • 雙向鏈表:雙向鏈表(double linked list)是在單鏈表的每個結點中,再設置一個指向其前驅結點的指針域。

    • 總結

      • 線性表
        • 順序存儲結構
        • 鏈式存儲結構
          • 單鏈表
          • 靜態鏈表
          • 循環鏈表
          • 雙向鏈表

棧與隊列

  • 棧與隊列
    • 棧是限定僅在表尾進行插入和刪除操作的線性表。
    • 隊列是只允許在一端進行插入操作、而在另一端進行刪除操作的線性表。
    • 棧的插入操作,叫做進棧,也成壓棧、入棧。
    • 棧的刪除操作,叫做出站,也有的叫做彈棧。
    • 兩棧共享空間
  • 棧的鏈式存儲結構及實現
    • 棧的鏈式存儲結構,簡稱為鏈棧
    • 如果棧的使用過程中元素變化不可預料,有時很小,有時非常大,那么最好是用鏈表,反之,如果它的變化在可控范圍內,建議使用順序棧會更好一些。
  • 遞歸定義: 我們把一個直接調用自己或通過一系列的調用語句間接地調用自己的函數,稱作遞歸函數。
  • 每個遞歸定義必須至少有一個條件,滿足時遞歸不再進行,即不再引用自身而是返回值退出。
  • 棧的應用-四則運算表達式求值
    • 后綴(逆波蘭)表示法定義
    • 后綴表達式計算結果
    • 中綴表達式轉后綴表達式
  • 隊列的定義
    • 隊列是只允許在一端進行插入操作,而在另一端進行刪除操作的線性表。
    • 頭尾相接的隊列稱為循環隊列
  • 隊列的鏈式存儲: 隊列的鏈式存儲結構,其實就是就是線性表的單鏈表,只不過它只能尾進頭出而已,我們把它簡稱為鏈隊列。
  • 總結
隊列
順序棧 順序隊列
兩棧共享空間 循環隊列
鏈棧 鏈隊列

  • 串的定義
    • 串是有零個或多個字符組成的有限序列,又名叫字符串
  • KMP模式匹配算法
    • 可以大大避免重復遍歷的情況
    • next[j] =
      {
      0, 當j=1時
      Max{k|1<k<j, 且'p1...pk-1' = 'pj-k+1...pj-1'}當此集合不空時
      1,其他情況
      }

  • 定義: 樹(Tree)是n(n>=0)個結點的有限集。n=0時稱為空樹。在任意一顆非空樹中:(1)有且僅有一個特定的稱為根(Root)的結點:(2)當n>1時,其余結點可分為m(m>0)個互不相交的有限集T1、T2、....、Tm,其中每一個集合本身又是一棵樹,並且稱為根的子樹(SubTree)。
      1. n>0 時根結點是唯一的,不可能存在多個根結點,別和現實中的大樹混合在一起,現實中的樹有很多根須,那是真實的樹,數據結構中的樹是只能有一個根結點。
    • m>0時,子樹的個數沒有限制,但它們一定是互不相交的。

    • 結點分類

      • 樹的結點包含一個數據元素及若干指向其子樹的分支。結點擁有的子樹稱為結點的度(degree).度為0的結點稱為葉節點(Leaf)或終端結點;度不為0的結點稱為非終端結點或分支結點。除根結點之外,分支結點也稱為內部結點。樹的度是樹內各結點的度的最大值。
    • 結點間關系

      • 結點的子樹的根稱為該結點的孩子(Child),相應地,該結點稱為孩子的雙親。同一個雙親的孩子之間互稱兄弟。結點的祖先是從根到該結點所經分支上的所有結點。以某結點為根的子樹中的任一結點都稱為該結點的子孫。
    • 樹的其他相關概念

      • 結點的層次(Level)從根開始定義起,根為第一層,根的孩子為第二層。
      • 樹中結點的最大層次稱為樹的深度(Depth)或高度
      • 如果將樹中的結點的各子樹看成從左至右是有次序的,不能互換的,則稱該樹為有序樹,否則稱為無序樹
      • 森林(Forest)是m(m>=0)棵互不相交的樹的集合
      • 對比線性表與樹
線性結構 樹結構
第一個數據元素:無前驅 根結點:無雙親,唯一
最后一個數據元素:無后驅 葉結點:無孩子,可以多個
中間元素:一個前驅一個后驅 中間結點:一個雙親多個孩子
  • 樹的存儲結構

    • 雙親表示法
      • 存儲結構的設計是一個非常靈活的過程。一個存儲結構設計得是否合理,取決於基於該存儲結構的運算是否適合、是否方便,時間復雜度好不好等。
    • 孩子表示法
      • 每個結點有多個指針域,其中每個指針指向一顆子樹的根結點,我們把這種方法叫做多重鏈表表示法。
      • 把每個結點的孩子結點排列起來,以單鏈表作存儲結構,則n個結點有n個孩子鏈表, 如果是葉子結點則此單鏈表為空。然后n個頭指針又組成一個線性表,采用順序存儲結構,存放進一個一維數組中。
    • 孩子兄弟表示法
      • 任意一棵樹,它的結點的第一個孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,我們設置兩個指針,分別指向該結點的第一個孩子和此結點的右兄弟。
  • 二叉樹的定義

    • 二叉樹(Binary Tree)是n(n >= 0)個結點的有限集合,該集合或者為空集(稱為空二叉樹),或者由一個根結點和兩顆互不相交的、分別稱為根結點的左子樹和右子樹的二叉樹組成。
    • 二叉樹特點
    • 特殊二叉樹
      • 斜樹
        • 所有的結點都只有左子樹的二叉樹叫左斜樹。所有結點都是只有右子樹的二叉樹叫有斜樹。這兩者統稱為斜樹。
      • 滿二叉樹
        • 在一顆二叉樹中,如果所有分支結點都存在左子樹和右子樹,並且所有葉子都在同一層上,這樣的二叉樹稱為滿二叉樹。
      • 完全二叉樹
        • 對一顆具有n個結點的二叉樹按層序編號, 如果編號為i(1<=i<=n)的結點與同樣深度的滿二叉樹中編號為i的結點在二叉樹中位置完全相同,則這顆二叉樹稱為完全二叉樹。
    • 二叉樹的性質
      • 二叉樹性質1: 在二叉樹的第i層上至多有2**(i-1)個結點。
      • 二叉樹性質2:深度為k的二叉樹至多有2**k-1個結點。(k>=1)
      • 二叉樹性質3: 對任何一顆二叉樹T, 如果其終端結點數為n0,度為2的結點數為n2,則n0=n2+1
      • 二叉樹性質4: 具有n個結點的完全二叉樹的深度為[log2n]+1([x]表示不大於x的最大整數)
      • 二叉樹性質5:
        • 如果對一顆有n個結點的完全二叉樹(其深度為[log2n] + 1) 的結點按層序編號(從第1層到第[log2n] + 1層,每層從左到右),對任一結點i(1<=i<=n)有:
          • 如果i=1, 則結點i是二叉樹的根,無雙親;如果i>1,則其雙親是結點[i/2]。
          • 如果2i>n, 則結點i無左孩子(結點I為葉子結點); 否則其左孩子是結點2i。
          • 如果2i+1>n, 則結點i無右孩子;否則其右孩子是結點2i+1。
  • 二叉樹的存儲結構

    • 二叉樹順序存儲結構
      • 順序存儲結構一般只用於完全二叉樹,其它二叉樹很浪費空間
    • 二叉鏈表
    • 二叉樹遍歷原理
      • 二叉樹的遍歷(traversing binary tree)是指從根結點出發,按照某種次序依次訪問二叉樹中所有結點,使得每個結點被訪問依次且僅被訪問依次。
    • 二叉樹遍歷方式
      • 前序遍歷
        • 規則是若二叉樹為空,則空操作返回,否則先訪問根結點,然后前序遍歷左子樹,再前序遍歷右子樹。
      • 中序遍歷
        • 規則是若樹為空,則空操作返回,否則從根結點開始(注意並不是先訪問根結點), 中序遍歷根結點的左子樹,然后是訪問根結點,最后中序遍歷右子樹。
      • 后序遍歷
        • 規則是若樹為空,則空操作返回,否則從左到右先葉子后結點的方式遍歷訪問左子樹,最后訪問根結點。
      • 層序遍歷
        • 規則是若樹為空,則空操作返回,否則從樹的第一層,也就是根結點開始訪問,從上而下逐層遍歷,在同一層中,按從左到右的順序對結點逐個訪問。
  • 二叉樹的建立

  • 線索二叉樹

    • 我們把這種指向前驅和后繼的指針稱為線索,加上線索的二叉鏈表稱為線索鏈表,相應的二叉樹就稱為線索二叉樹。
    • 我們對二叉樹以某種次序遍歷使其變為線索二叉樹的過程稱作是線索化。
    • 線索化的過程就是在遍歷的過程中修改空指針的過程。
    • 如果所用的二叉樹需經常遍歷或者查找結點是需要某種遍歷序列中的前驅和后繼,那么采用線索二叉鏈表的存儲結構就是非常不錯的選擇。
    • 樹與森林的遍歷
      • 前序遍歷
      • 后序遍歷
      • 樹的前序遍歷和后序遍歷跟二叉樹的前序遍歷和中序遍歷一樣的。
  • 赫夫曼樹及其應用

    • 赫夫曼樹定義與原理
      • 從樹中一個結點到另一個結點之間的分支構成兩個結點之間的路徑,路徑上的分支數目稱作路徑長度。
      • 樹的路徑長度就是從樹根到每一個結點的路徑長度之和。
      • 帶權路徑長度WPL最小的二叉樹稱作赫夫曼樹。
    • 赫夫曼編碼
      • 若要設計長短不等的編碼,則必須是任一字符的編碼都不是另一個字符的編碼的前綴,這種編碼稱作前綴編碼。

  • 圖是由定點(graph)是由頂點的有窮非空集合和頂點之間邊的集合組成,通常表示為:G(V,E), 其中,G表示一個圖,V是圖G中頂點的集合,E是圖G中邊的集合。

  • 圖的定義

    • 在圖中數據元素,我們則稱之為頂點(Vertex)

    • 圖不允許沒有頂點

    • 圖中,任意兩個頂點之間都可能有關系,頂點之間的邏輯關系用邊來表示。

    • 各種圖定義

      • 若頂點vi到vj之間的邊沒有方向,則稱這條邊為無向邊(Edge),用無序偶對(vi,vj)來表示.
      • 如果圖中任意兩個頂點之間的邊都是無向邊,則稱該圖為無向圖。
      • 有向邊:若從頂點vi到vj的邊有方向,則稱這條邊為有向邊,也稱為弧(Arc)
      • 如果圖中任意兩個頂點之間的邊都是有向邊,則稱該圖為有向圖.
      • 連接頂點A到D的有向邊就是弧,A是弧尾,D是弧頭,<A,D>表示弧,注意不能寫成<D,A>。
      • 在圖中,若不存在頂點到其自身的邊,且同一條邊不重復的出現,則稱這樣的圖為簡單圖。
      • 在無向圖中,如果任意兩個頂點之間都存在邊,則稱該圖為無向完全圖。
      • 在有向圖中,如果任意兩個頂點之間都存在方向互為相反的兩條弧,則稱改圖為有向完全圖。
      • 對於具有n個頂點和e條邊數的圖,無向圖0<=e<=n(n-1)/2,有向圖0<=e<=n(n-1).
      • 有多少條邊或弧的圖稱為稀疏圖,反之稱為稠密圖。
      • 與圖的邊或弧相關的數叫做權(Weight)。
      • 這種帶權的圖通常稱為網(Network)。
    • 圖的頂點與邊間關系

      • 樹中根結點到任意結點的路徑是唯一的,但是圖中頂點與頂點之間的路徑卻是不唯一的。
      • 在無向圖G中,如果從頂點V到頂點V'有路徑,則稱V和V'是連通的。如果對於圖中任意兩個頂點都是連通的,則稱G是連通圖。
      • 無向圖中的極大連通子圖稱為連通分量。
      • 在有向圖G中,從Vi到Vj和從Vj到Vi都存在路徑,則稱G是強連通圖。在有向圖中的極大強連通子圖稱作有向圖的強連通分量。
      • 一個連通圖的生成樹是一個極小的連通子圖,它含有圖中全部的n個頂點,但只有足以構成一棵樹的n-1條邊。
      • 如果一個有向圖恰有一個頂點的入度為0,其余頂點的入度均為1。則是一顆有向樹。
      • 一個有向圖的生成森林由若干顆有向樹組成,含有圖中全部頂點,但只有足以構成若干顆不相交的有向樹的弧。
      • dfs:Deepness First Search
      • bfs:Breadth First Search
  • 圖的存儲結構

    • 鄰接矩陣
      • 圖的鄰接矩陣(adjacency matrix)存儲方式是用兩個數組來表示圖。一個一維數組存儲圖中頂點信息,一個二維數組(稱為鄰接矩陣)存儲圖中的邊或弧的信息。
    • 鄰接表
      • 數組與鏈表相結合的存儲方式稱為鄰接表(adjacency list)。
      • 有向圖的逆鄰接表,即對每個頂點vi都建立一個鏈接vi為弧頭的表。i
    • 十字鏈表
    • 鄰接多重表

    邊表結點結構如表

    ivex ilink jvex jlink
    其中ivex和jvex是與某條邊依附的兩個頂點在頂點表中下標。ilink指向依附頂點ivex的下一條邊,jlink指向依附頂點jvex的下一條邊。這就是鄰接多重表結構。
    • 邊集數組

    邊集數組是由兩個一維數組構成。一個是存儲頂點的信息;另一個是存儲邊的信息,這個邊數組每個數據元素由一條邊的起點下標(begin)、終點下標(ebd)和權(weight)組成。

    • 圖的遍歷
      • 我們希望從圖中某一頂點出發訪遍圖中其余頂點,且使每一個頂點僅被訪問依次,這一過程就叫做圖的遍歷(traversing Graph) 。
      • 深度優先遍歷
        • 深度優先遍歷(Depth_First_Search),DFS。
        • 它從圖中某個頂點v出發,訪問此頂點,然后從v的未被訪問的鄰接點出發深度優先遍歷圖,直至圖中所有和v有路徑相通的頂點都被訪問到。
        • 若圖中尚有頂點未被訪問,則另選圖中一個未曾被訪問的頂點作起始點,重復上述過程,直至圖中所有頂點都被訪問到為止。
  • 最小生成樹
    我們把構造連通網的最小代價生成樹稱為最小生成樹

    • 普里姆(prim)算法
    • 克魯斯卡爾(Kruskal)算法
  • 最短路徑
    對於網圖來說,最短路徑,是指兩頂點之間經過的邊上權值之和最少的路徑,並且我們稱路徑上的第一個頂點是源點,最后一個頂點是終點。

    • 迪傑斯特拉(Dijkstra)算法
    • 弗洛伊德(floyd)算法
  • 拓撲排序

    • 拓撲排序介紹
      在一個表示工程的有向圖中,用頂點表示活動,用弧表示活動之間的優先關系,這樣的有向圖為頂點表示活動的網,我們成為AOV網(Activity On Vertex Network)
      設G={V,E}是一個具有n個頂點的有向圖,V中的頂點序列v1,v2,.....,vn, 滿足若從頂點vi到vj有一條路徑,則在頂點序列中頂點vi必在頂點vj之前。則我們稱這樣的頂點序列為一個拓撲序列。
      所謂拓撲序列,其實就是對一個有向圖構造拓撲序列的過程。
      • 拓撲排序算法
  • 關鍵路徑
    在一個表示你工程的帶權有向圖中,用頂點表示事件,用有向邊表示活動,用邊上的權值表示活動的持續事件,這種有向圖的邊表示活動的網,我們稱為AOE網(Activity On Edge Network).
    我們把路徑上各個活動所持續的時間之和稱為路徑長度,從源點到匯點具有最大長度的路徑叫關鍵路徑,在關鍵路徑上的活動叫關鍵活動。

    • 關鍵路徑算法原理
    • 關鍵路徑算法
  • 查找
    查找(searching)就是根據給點的某個值,在查找表中確定一個其關鍵字等於給定值的數據元素(或記錄).

    • 查找概論
      查找表(search table)是由同一類型的數據元素(或記錄)構成的集合。
      關鍵字(Key)是數據元素中某個數據項的值。
      若此關鍵字可以唯一地標識一個記錄,則稱此關鍵字為主關鍵字(primary key)。主關鍵字所在的數據項稱為主關鍵碼。
      對於那些可以識別多個數據元素(或記錄)的關鍵字,我們稱為此關鍵字(secondary key)。
      靜態查找表(static search table):只作查找操作的查找表
      動態查找表(dynamic search table):在查找過程中同時插入查找表中不存在的數據元素,或者從查找表中刪除已經存在的某個數據元素。
    • 順序表查找
      • 順序查找表優化
    • 有序表查找
    • 插值查找
      mid = low + ((key - a[low])/(a[high)-a[low])*(high -low)
    • 斐波拉契查找
  • 線性索引查找
    索引就是把一個關鍵字與它對應的記錄相關聯的過程。
    索引按照結構可以分為線性索引、樹形索引、多級索引。所謂線性索引就是將索引項集合組織為線性結構,也稱為索引表。我們重點介紹三種線性索引:稠密索引、分塊索引和倒排索引。

    • 稠密索引
      稠密索引是指在線性索引中,將數據集中的每個記錄對應一個索引項。
      對於稠密索引這個索引來說,索引項一定是按照關鍵碼有序的排列。
    • 分塊索引
      分塊有序,是把數據集的記錄分成了若干塊,並且這些快需要滿足兩個條件:
      • 塊內無序
      • 塊間有序
        我們定義的分塊索引的索引項結構分三個數據項:
      • 最大關鍵碼,它存儲每一塊中的最大關鍵字,這樣的好處就是可以使得在它之后的下一塊的最小關鍵字也能比這一塊最大的關鍵字還要大
      • 存儲了塊中的記錄個數,以便於循環時使用
      • 用於指向塊首數據元素的指針,便於開始對這一塊中記錄進行遍歷
    • 倒排索引
      索引項的通用結構是:
      * 次關鍵碼
      * 記錄號表
      其中記錄號表存儲具有相同次關鍵字的所有記錄的記錄號(可以是指向記錄的指針或者是該記錄的主關鍵字).這樣的索引方法就是倒排索引。
  • 二叉排序樹
    二叉排序樹(Binary Sort Tree), 又稱為二叉查找樹。它或者是一顆空樹,或者是具有下列性質的二叉樹。

    • 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結構的值
    • 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值
    • 它的左、右子樹也分別為二叉排序樹。
    • 查找
    • 插入
    • 刪除
  • 平衡二叉樹
    平衡二叉樹,是一種二叉排序樹,其中每一個節點的左子樹和右子樹的高度差至多等於1.
    我們將二叉樹上結點的左子樹深度減去右子樹深度的值稱為平衡因子BF(balance factor)
    距離插入結點最近的,且平衡因子的絕對值大於1的結點為根的子樹,我們稱為最小不平衡子樹。

  • 多路查找樹(B樹)
    多路查找樹(muitl-way search tree), 其每一個結點的孩子樹可以多於兩個,且每一個結點處可以存儲多個元素。

    • 2-3樹
      2-3 樹是這樣的一顆多路查找樹;其中的每一個結點都具有兩個孩子(我們稱它為2結點)或三個孩子(我們稱它為3結點)。
      一個2結點包含一個元素和兩個孩子(或沒有孩子)
      左子樹包含的元素小於該元素,右子樹包含的元素大於該元素

      一個3結點包含一小一大兩個元素和三個孩子(或沒有孩
      子)
      如果某個3結點有孩子的話,左子樹包含小於較小元素的元素,右子樹包含大於較大元素的元素,中間子樹包含介於兩元素之間的元素。

    • 2-3-4樹
      它其實就是2-3樹的概念擴展,包括了4結點的使用。一個4結點包含小中大三個元素和四個孩子(或沒有孩子),一個4結點要么沒有孩子,要么具有四個孩子。如果某個4結點有孩子的話,左子樹包含小於最小元素的元素;第二子樹包含大於最小元素,小於第二元素的元素;第三子樹包含大於第二元素,小於最大元素的元素;右子樹包含大於最大元素的元素。

    • B樹
      B樹(B-tree)是一種平衡的多路查找樹,2-3樹和2-3-4樹都是B樹的特例。結點最大的孩子數目稱為B樹的階(order), 因此,2-3樹是3階B數,2-3-4樹是4階B樹。
      一個m階的B樹具有如下屬性:
      * 如果根結點不是葉結點,則其至少有兩顆子樹。
      * 每一個非根的分支結點都有K-1個元素和K個孩子,其中[m/2]<=k<=m.每一個葉子結點n都有k-1個元素,其中[m/2]<=k<=m.
      * 所有葉子結點都位於同一個層 次。
      * 所有分支結點包含下列信息數據(n,A0,k1,A1,k2,A2,...,Kn,An), 其中:Ki(i=1,2,...,n)為關鍵字,且ki<ki+1.....

    • B+樹

  • 散列表查找(哈希表)概述

    • 散列表查找定義
      散列技術是在記錄的存儲位置和它的關鍵字之間建立一個確定的對應關系f,使得每個關鍵字key對應一個存儲位置f(key).
      我們把這種對應關系f稱為散列函數,又稱為哈希(hash)函數。按這個思想,采用散列技術將記錄存儲在一塊連續的存儲空間中,這塊連續存儲空間稱為散列表或哈希表(Hash table)。

    • 散列表查找步驟
      散列技術既是一種存儲方法,也是一種查找方法。
      散列技術最合適的求解問題是查找與給定值相等的記錄。

  • 散列函數的構造方法
    什么才算是好的散列函數了?這里我們有兩個原則可以參考。

    1. 計算簡單
    2. 散列地址分布均勻
    • 直接地址法
      我們可以取關鍵字的某個線性函數值為散列地址,即
      f(key) = axkey+b(a,b為常數)
    • 數字分析法
    • 平方取中法
    • 折疊法
    • 除留余數法
      因此根據前輩們的經驗,若散列表表長為m,通常p為小於或等於表長(最好接近m)的最小質數或不包含小於20質因子的合數。
    • 隨機數法
  • 處理散列沖突的方法

    • 開放地址法
      所謂的開放地址法就是一旦發生了沖突,就去尋找下一個空的散列地址,只要散列表足夠大,空的散列地址總能夠找到,並將記錄存入。
      它的公司是:
      f(key) = (f(key) + di) mod m(di=1,2,3,.....,m-1)
      我們把這種解決沖突的開放地址法稱為線性探測法。
      增加平方運算的目的是為了不讓關鍵字都聚集在某一塊局域。我們稱這種方法為二次探測法。
      在沖突時,對於位移量di采用隨機函數計算得到,我們稱之為隨機探測法(使用同一個隨機種子)。

    • 再散列函數法

    • 鏈地址法
      鏈地址法對於可能會造成很多沖突的散列函數來說,提供了絕不會出現找不到地址的保障。當然,這也就帶來了查找時需要遍歷單鏈表的性能損耗。

    • 公共溢出區法
      如果相對於基本表而言,有沖突的數據很少的情況下,公共溢出區的結構對查找性能來說還是非常高的。

  • 散列表查找實現

    • 散列表查找算法實現
    • 散列表查找性能分析
      1. 散列函數是否均勻

      2. 處理沖突的方法
        相同的關鍵字、相同的散列函數,但處理沖突的方法不同,會使得平均查找長度不同。比如線性探測處理沖突可能會產生堆積,顯然就沒有二次探測法好,而鏈地址法處理沖突不會產生任何堆積,因而具有更佳的平均查找性能。

      3. 散列表的裝填因子

  • 排序

    • 排序的基本概念與分類
      對於組合排序的問題,當然可以先排序總分,若總分相等的情況下,再排序語數外總分,但這是比較土的辦法。我們還可以應用一個技巧來實現一次排序即完成組合排序問題,例如把總分與語數外都當層字符串首尾連接在一起。
      • 排序的穩定性
        假設ki=kj(1<=i<=n, 1<=j<=n,i!=j),且在排序錢的序列中ri領先於rj(即i<j)。如果排序后ri忍領先於rj,則稱所有的排序方法是穩定的;反之,若可能使得排序后的序列中rj領先ri,則稱所用的排序方法是不穩定的。
      • 內排序和外排序
        內排序是在排序整個過程中,待排序的所有記錄全部放置在內存中。外排序是由於排序的記錄個數太多,不能同時放置在內存,整個排序過程需要在內外存之間多次交換數據才能進行。
        對於內排序來說,排序算法的性能主要受3個方面影響:
      1. 時間性能
        高效率的內排序算法應該具有盡可能少的關鍵字比較次數和盡可能少的記錄移動次數。
      2. 輔助空間
        輔助存儲空間是除了存放待排序所占用的存儲空間之外,執行算法所需要的其他存儲空間.
      3. 算法復雜性
    • 冒泡排序
      時間復雜度為O(n2)
    • 簡單選擇排序
      總的時間復雜度依然為O(n2)
      簡單選擇排序的性能上還是要略優於冒泡排序
    • 直接插入排序
      直接插入排序的基本操作是將一個記錄插入到已經排好序的有序表中,從而得到一個新的、記錄數增1的有序表。
      o(n2)時間復雜度.
      直接插入排序法比冒泡和簡單選擇排序的性能要好一些。
    • 希爾排序
      所謂的基本有序,就是小的關鍵字基本在前面,大的基本在后面,不大不小的基本在中間。
      將相距某個"增量"的記錄組成一個子序列,這樣才能保證在子序列內分別進行直接插入排序后得到的結果是基本有序而不是局部有序。
      增量序列的最后一個增量值必須等於1才行。
    • 堆排序
      堆是具有下列性質的完全二叉樹: 每個結點的值都大於或等於其左右孩子結點的值,稱為大頂堆; 或者每個結點的值都小於或等於其左右孩子結點的值,稱為小頂堆。
      堆排序就是利用堆進行排序的方法。它的基本思想是,將待排序的序列構造成一個大頂堆。此時,整個序列的最大值就是堆頂的根結點。將它移走(其實就是將其與堆數組的末尾元素交換, 此時末尾元素就是最大值), 然后將剩余的n-1個序列重新構造成一個堆,這樣就會得到n個元素中的次小值。如此反復執行,便能得到一個有序序列了。
      不穩定排序
    • 歸並排序
      歸並排序(meging sort)就是利用歸並的思想實現的排序方法。它的原理是假設初始序列含有n個記錄,則可以看成是n個有序的子序列,每個子序列的長度為1,然后兩兩歸並,得到n/2個長度為2或1的有序子序列;再兩兩歸並,....,如此重復,直至得到一個長度為n的有序序列為止,這種排序的方法稱為2路歸並排序。
    • 快速排序
      快速排序的基本思想是:通過一趟排序將待排序記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序的目的。


免責聲明!

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



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