高級數據結構之多維數組與廣義表與存儲管理

多維數組

基本概念

多維數組 (Multi-array) 是向量的擴充,向量的向量就組成了多維數組。 能夠表示爲ELEM A[c1..d1][c2..d2]...[cn..dn]程序員

  • ci 和 di 是各維下標的下界和上界。

數組的存儲

內存是一維的,因此數組的存儲也只能是一維的數組

  • 以行爲主序 (也稱爲「行優先」)
  • 以列爲主序 (也稱爲「列優先」) C++ 多維數組ELEM A[d1][ d2]...[dn];

用數組表示特殊矩陣

  • 三角矩陣:上三角、下三角
  • 對稱矩陣

  • 對角矩陣

  • 稀疏矩陣
    • 十字鏈表存儲

矩陣乘法時間代價

  • A爲 p×m 的矩陣,B 爲 m×n 的矩陣,乘得的結果 C 爲 p×n 的矩陣
    • 若矩陣 A 中行向量的非零元素個數最多爲 ta
    • 矩陣 B 中列向量的非零元素個數最多爲 tb
  • 總執行時間下降爲 O ( (ta+tb) ×p×n)
  • 經典矩陣乘法所須要的時間代價爲 O (p×m×n)

廣義表-更多內容

  • 回顧線性表
    • 由 n (n≥0) 個數據元素組成的有限有序序列
    • 線性表的每一個元素都具備相同的數據類型
  • 若是一個線性表中還包括一個或者多個子表,那 就稱之爲廣義表 (Generalized Lists,也稱Multi-list) 通常記做:
    • L=(x0,x1,...,xi,...,xn-1)

廣義表的各類類型

  • 純表 (pure list)
    • 從根結點到任何葉結點只有一條路徑
    • 即任何一個元素 (原子、子表) 在廣義表中只出現一次
  • 可重入表
    • 其元素(包括原子和子表)可能會在表中屢次出現
    • 若是沒有迴路圖示對應於一個 DAG
  • 循環表
    • 包含迴路
    • 循環表的深度爲無窮大 圖≥再入表≥純表 (樹)≥線性表,廣義表是線性與樹形結構的推廣。遞歸表是有迴路的再入表。

廣義表存儲

  • 增長頭指針,簡化刪除、插入操做

廣義表應用

  • 函數的調用關係
  • 內存空間的引用關係
  • LISP 語言

存儲管理

分配與回收

  • 內存管理最基本的問題
    • 分配存儲空間
    • 回收被「釋放」的存儲空間
  • 碎片問題
    • 存儲的壓縮
  • 無用單元收集
    • 無用單元:能夠回收而沒有回收的空間
    • 內存泄漏 (memory leak)
      • 程序員忘記 delete 已經再也不使用的指針

可利用空間表

  • 把存儲器當作一組變長塊數組
    • 一些塊是已分配的
    • 連接空閒塊,造成可利用空間表 (freelist)
  • 存儲分配和回收
    • new p 從可利用空間分配
    • delete p 把 p 指向的數據塊返回可利用空間表
  • 空間不夠,則求助於失敗策略

可利用空間表:單鏈表棧freelist

使用者老是從freelist中分配內存,若是存在沒有使用的內存塊就直接摘出來使用,若是沒有的話再從系統中分配。 使用完畢後並不去直接delete該內存塊,而是把他插入到可用內存塊的頭部,同時讓他指向以前可用內存塊的頭部。bash

存儲的動態分配和回收

變長可利用塊網絡

  • 分配
    • 找到其長度大於等於申請長度的結點
    • 從中截取合適的長度
  • 回收
    • 考慮剛剛被刪除的結點空間可否與鄰接合並
    • 以便能知足後來的較大長度結點的分配請求

碎片問題

  • 內部碎片:多於請求字節數的空間
  • 外部碎片:小空閒塊

空閒塊分配方法-順序適配 (sequential fit)

常見的順序適配方法:函數

  • 首先適配 (firstfit) ,實例
  • 最佳適配 (bestfit)
  • 最差適配 (worstfit) 組織成循環鏈表的可利用空間表的結點大小按遞增序排列時, 首次適配策略就轉變爲最佳適配策略。

回收:考慮合併相鄰塊

把待回收塊釋放回可利用空間表ui

適配策略選擇

  • 須要考慮如下因素用戶的要求
    • 分配或回收效率對系統的重要性
    • 所分配空間的長度變化範圍
    • 分配和回收的頻率
  • 在實際應用中,首先適配最經常使用
    • 分配和回收的速度比較快
    • 支持比較隨機的存儲請求
  • 很難籠統地講這哪一種適配策略最好

失敗處理策略和無用單元回收

若是遇到因內存不足而沒法知足一個存儲請求,存儲管理器能夠有兩種行爲:this

  • 一是什麼都不作,直接返回一個系統錯誤信息;
  • 二是使用失敗處理策略 (failure policy) 來知足請求。

存儲壓縮 (compact)

  • 把內存中的全部碎片集中起來,組成一個大的可利用塊
  • 內存碎片不少,即將產生溢出時使用
  • 句柄使得存儲地址相對化
    • 對存儲位置的二級間接指引
    • 移動存儲塊位置,只須要修改句柄值,不須要修改應用程序
  • 兩種存儲壓縮
    1. 一旦有用戶釋放存儲塊即進行回收壓縮
    2. 在可利用空間不夠分配或在進行無用單元的收 集時進行「存儲壓縮」

無用單元收集

無用單元收集:最完全的失敗處理策略spa

  • 普查內存,標記把那些不屬於任何鏈的結點,將它們收集到可利用空間表中
  • 回收過程一般還可與存儲壓縮一塊兒進行

可利用空間表實例

經常會有頻繁申請、釋放內存的需求,好比在發送網絡報文時,每次都要分配內存以存儲報文,等報文發送完成後又須要刪除報文。 爲了不頻繁的new/delete對系統帶來的開銷,須要實現一個通用的FreeList機制。使用者老是從free list中分配內存,若是存在沒有使用的內存塊就直接摘出來使用,若是沒有的話再從系統中分配。使用完畢後並不去直接delete該內存塊,而是交給FreeList保管。3d

template <typename Elem>
class FreeList
{
private:
    static FreeList<Elem> *freelist;
public:
    Elem element;
    FreeList *next;
    FreeList(const Elem& elem, FreeList* next=NULL);
    FreeList(FreeList* next=NULL);
    void* operator new(size_t);    // 重載new
    void operator delete(void*);   // 重載delete
};


template <typename Elem>
FreeList<Elem>* FreeList<Elem>::freelist = NULL;

template <typename Elem>
FreeList<Elem>::FreeList(const Elem& elem, FreeList* next)
{
    this->element = elem;
    this->next = next;
}

template <typename Elem>
FreeList<Elem>::FreeList(FreeList* next)
{
    this->next = next;
}

template <typename Elem>
void* FreeList<Elem>::operator new(size_t)
{
    /*freelist沒有可用空間,就從系統分配*/
    if(freelist == NULL)  
        return ::new FreeList;

    /*不然,從freelist表頭摘取結點*/
    FreeList<Elem>* temp = freelist;
    freelist = freelist->next;
    return temp;
}

template <typename Elem>
void FreeList<Elem>::operator delete(void* ptr)
{
    /*把要釋放的結點空間加入到freelist中*/
    ((FreeList<Elem>*)ptr)->next = freelist;
    freelist = (FreeList<Elem>*)ptr;
}
複製代碼
相關文章
相關標籤/搜索