1. 程式人生 > >(待補充)常用資料結構學習

(待補充)常用資料結構學習

1. 資料結構:相互之間存在一種或多種特定關係的資料元素的集合。形式定義:資料結構是一個二元組(D,S),D是資料元素的有限集,S是D上關係的有限集。

2. 結構:資料元素之間的關係稱為結構,通常由四種基本結構:集合、線性結構、樹形結構、圖狀(網狀)結構;

3. 資料結構在數學層面的描述是邏輯結構,在計算機中的表示(映像)是物理(儲存)結構。

4. 資料元素在計算機中表示為元素或結點,當一個數據元素由若干個資料項組成的時候,結點中對應於各個資料項的子位串稱為資料域。

5. 資料元素之間的關係在計算機中分為:順序映像和非順序映像;

由此得到的兩種儲存結構:順序儲存結構和鏈式儲存結構。

6. 資料型別:是一個值的集合和定義在這個值集上的一組操作的總稱。如整型變數的值集是某個區間上的整數,定義在其上的操作是加減乘除和取模等算術運算。

7. 抽象資料型別:指一個數學模型以及定義在該模型上的一組操作。按值的不同特性可分為三種類型:原子型別、固定聚合型別、可變聚合型別。形式定義:抽象資料型別是一個三元組(D,S,P),D是資料物件,S是D上的關係集,P是對D的基本操作集。

 

 

  • 線性表

1. 定義:一個線性表是n個數據元素的有限序列。資料元素依具體應用情況而定,可以是基本型別也可以是由若干個資料項組成的記錄,含有大量記錄的線性表又稱檔案。

2. 特點:線性表中元素個數n(n>=0)定義為長度,n=0時稱為空表。在非空表中每個資料元素都有一個確定的位置。

3. 操作:查詢、排序、新增、刪除

4. 結構:順序結構(陣列)、鏈式結構(結點)

 

1. 順序表:用一組地址連續的儲存單元依次儲存線性表的資料元素。

2. 鏈式表:用一組任意的儲存單元儲存線性表的資料元素(可以連續也可以不連續)。

3. 靜態連結串列:用陣列描述的鏈式表。

4. 迴圈連結串列:特點是表中最後一個結點的指標域指向頭結點,整個連結串列形成一個環。所以從表中任何一個結點出發都能找到其它結點。

5. 雙向連結串列:結點中有兩個指標域,一個指向前驅,一個指向後繼。

 

 

  • 棧和佇列:

1. 棧和佇列是特殊的線性表,但是不同的抽象資料型別。

2. 棧:是限定近在表尾(即棧頂)進行插入或刪除操作(後進先出)的線性表。

3. 佇列:先進先出的線性表,只允許在表一端(隊尾)插入,在另一端(隊頭)刪除元素

 

 

 

  • 串:

1. 串(或字串)是由零個或多個字元組成的有限序列,一般記為:s=’a1a2a3…an’ (n>=0) s是串名,單引號裡面的字元序列是串值,ai可以是字母、數字或其它字元,串中字元的數目稱為串的長度。長度為0的串是空串。

2. 串中任意連續個字元組成的子序列是該串的子串。

3. 兩個串相等=串長相等&&串中對應位置的字元相同

4. 串的定長順序儲存表示:類似於順序表,用一組地址連續的儲存單元儲存串值,儲存空間大小在最開始指定為固定值,如此在操作過程中如果出現串長超限的情況就統一用‘截尾法’處理。

5. 串的堆分配儲存表示:仍用一組地址連續的儲存單元存放串值,但它們的儲存空間是在程式執行過程中動態分配的。

6. 串的塊鏈儲存表示:用連結串列方式儲存,每個結點可以儲存一個或多個字元,當儲存多個字元時,存在最後一個結點不滿的情況,可用“#”來表示空字元

 

 

 

  • 陣列和廣義表

1. 陣列和廣義表可以看成是線性表的擴充套件,即表中的資料元素本身也是一個數據結構,如n維陣列可以看作資料元素是n-1維陣列型別的一維陣列型別。

2. 陣列的順序儲存表示:用一維陣列按約定次序(一般為行序)來表示多維陣列。

3. 廣義表:或稱列表lists,表中每個元素可以是單個元素(原子)或一個廣義表(子表),第一個元素稱表頭,剩下所有元素稱為表尾。

廣義表的儲存結構:通常為鏈式,每個資料元素可用一個結點表示。

 

樹和二叉樹

  1. 樹:是n(n>=0)個結點的有限集。樹的結點包含一個數據元素以及若干指向其子樹的分支。結點擁有的子樹個數稱為該結點的度。度為0(沒有子樹)的結點稱為葉子(或終端結點),度不為0的結點稱為分支結點。樹的度是各結點度的最大值。結點的層次從根開始定義,根為第一層。結點的最大層次稱為樹的深度(高度)。
  2. 有序樹、無序樹:結點的子樹從左到右有無次序。
  3. 森林:是m(m>=0)棵互不相交的樹的集合。非空樹根節點的各個子樹的集合就是森林。故可將樹看作二元組Tree=(root,F),root是根節點,F是子樹組成的森林。
  4. 二叉樹:特點是每個結點至多隻有兩棵子樹,並且子樹有左右之分,次序不能顛倒(有序樹)。
  5. 二叉樹的性質:(1)在二叉樹的第i層上至多有2^(i-1)個結點(i>=1);(2)深度為k的二叉樹至多有(2^k)-1個結點(k>=1);(3)任意二叉樹,如果其終端結點的個數為a,度為2的結點個數為b,則a=b+1;
  6. 滿二叉樹:深度為k且有(2^k)-1個結點的二叉樹
  7. 完全二叉樹:結點是從上到下、從左到右順序排列的二叉樹;
  8. 二叉樹的順序儲存結構:用一組地址連續的儲存單元(一維陣列)依次自上而下,從左到右儲存完全二叉樹的結點元素,僅適用於完全二叉樹。
  9. 二叉樹的鏈式儲存結構:二叉連結串列(資料域+左、右結點)、三叉連結串列(資料域+左、右結點+父結點)、線索連結串列

 

 

查詢

(1)靜態查詢表:表中元素個數不再改變,只對表進行查詢或排序操作,分類:

1.順序查詢:從表尾元素開始向前查詢,平均查詢長度(n+1)/2,可以通過將查詢頻率高的元素後移來提高查詢效率;

2.二分查詢(有序表):每次查詢將範圍縮小一半,直到最後一個元素或找到為止,平均查詢長度log2(n+1)-1

Int Search(SSTable ST, KeyType key) {
  //在有序表ST中折半查詢其關鍵字等於key的資料元素。若找到,則函式值為
  //該元素在表中的為止,否則為-1
  Low=1; high=ST.length; //區間初值
  While(low<=high) {
    Mid=(low+high)/2;
If(key==ST.elem[mid].key) return mid;
Else if (key<ST.elem[mid].key) high=mid-1;
Else low=mid+1;
}
Return -1;

3.分塊查詢:建立索引順序表,表中記錄該分塊的最大元素值和起始地址(及長度),索引表有序,塊內無序,所以對索引表可以用順序查詢或二分查詢,而對塊則需用順序查詢。平均查詢長度為((n/s + s)+1)/2(順序查詢)、log2 (n/s + 1) + s/2(二分查詢)。

 

  1. 動態查詢表:表中元素個數變化,可以對錶進行插入刪除查詢排序的操作。

二叉排序樹:左子樹結點值均小於根節點值,右子樹值均大於根節點值。

Int Search(BiTree T,KeyType key,BiTree f,BiTree &p) {
  //在根指標T所指二叉排序樹中遞迴查詢其關鍵字==key的元素,若查詢成功則
  //指標p指向該結點並返回TRUE,否則指標p指向查詢路徑上訪問的最後一個
  //結點並返回FALSE,指標f指向T的雙親,其初始值為NULL;
  If(!T) {p=f; return FALSE;}	//查詢不成功
  Else if (key==T->data.key) {p=T; return TRUE;}
  Else if (key<T->data.key) return Search(T->lchild,key,T,p);
  Else return Search(T->rchild,key,T,p);
} //查詢操作,成功則返回對應結點,否則返回葉子結點

Int Insert(BiTree &T,ElemType e) {
  //當二叉排序樹T中不存在關鍵字=e.key的元素時,插入e並返回TRUE
  If(!Search(T,e.key,NULL,p) {
    S=new BiTree();
S->data=e; s->lchild=s->rchild=NULL;
        If(!p) T=s;		//原本是空樹,則s為根結點
Else if(e.key<p->data.key) p->lchild=s;
Else p->rchild=s;
Return TRUE;
}
Else retrun FALSE;
} //插入操作

Int Delete(BiTree &T, KeyType key) {
      //若二叉排序樹T中存在關鍵字==key的元素,則刪除該元素結點並返回TRUE
  If(!T) return FALSE;  //不存在關鍵字==key的元素
  Else {
    If(key==T->data.key) {
  //從二叉排序樹中刪除結點T並重接它的左右子樹
  If(!p->rchild) { //右子樹空則只需重接它的左子樹
    q=T;T=T->lchild;free(q);
}
Else if(!p->lchild) { //左子樹空則只需重接它的右子樹
 	q=T;T=T->rchild;free(q);
}
Else { //左右子樹都不空
  q=T; s=T->lchild;
  While(s->rchild) {q=s; s=s->rchild;}
T->data=s->data;
If(q!=T)q->rchild=s->lchild;
Else q->lchild=s->lchild;
delete s;
}
}
Else if(key<T->data.key) return Delete(T->lchild,key);
Else return Delete(T->rchild,key);
  }
} //刪除操作

平衡二叉樹(AVL樹):左右子樹深度差的絕對值不超過1

 

雜湊表:根據設定的雜湊函式H(key)和處理衝突的方法將一組關鍵字映像到一個有限的連續的地址集上,並以關鍵字在地址集中的‘像’作為記錄在表中的儲存位置,這種表便被稱為雜湊表,這一映像過程稱為雜湊造表或雜湊,所儲存位置稱為雜湊地址或雜湊地址。

 

排序

  1. 簡單排序演算法:時間複雜度O(n^2),直接插入排序、折半插入排序,氣泡排序,簡單選擇排序
  2. 先進排序演算法:時間複雜度O(n*logn),快速排序、堆排序、歸併排序
  3. 基數排序演算法:時間複雜度O(d*n)

 

  1. 直接插入排序:將一個記錄插入到已排好序的有序表中,從而得到一個新的、記錄數增1的有序表,時間複雜度O(n^2),空間複雜度O(1):
void InsertSort(vector<int> &vt)
{
    for(int i=2,j;i<vt.size();++i)
    {
        if(vt[i]<vt[i-1]){  //判斷當前資料是否需要前移
            vt[0]=vt[i];    //設定vt[0]為哨兵
            vt[i]=vt[i-1];
            for(j=i-2;vt[j]>vt[0];--j)
                vt[j+1]=vt[j];  //記錄後移
            vt[j+1]=vt[0];      //插入到正確位置
        }
    }
}
  1. 折半插入排序:將直接插入排序中的‘查詢’操作採用‘折半查詢’來實現。時間複雜度O(n^2),空間複雜度O(1):
void InsertSort(vector<int> &vt)
{
    for(int i=2,j;i<vt.size();++i)
    {
        if(vt[i]<vt[i-1]){  //判斷當前資料是否需要前移
            vt[0]=vt[i];    //設定vt[0]為哨兵
            int low=1,high=i-1;
            while(low<=high){
                int mid=low+(high-low)/2;
                if(vt[mid]>vt[0])high=mid-1;
                else low=mid+1;
            }
            for(j=i-1;j>=high+1;--j)
                vt[j+1]=vt[j];  //記錄後移
            vt[high+1]=vt[0];      //插入到正確位置
        }
    }
}
  1. 希爾排序:先將整個待排記錄序列分割成為若干個子序列分別進行直接插入排序,待整個序列中的記錄‘基本有序’時,再對全體記錄進行一次直接插入排序。
void ShellSort(vector<int> &vt,vector<int> dlta)
{
    //dlta為增量序列,最後一個增量必須是1
    for(int k=0;k<dlta.size();++k)
    {
        for(int i=dlta[k]+1,j;i<vt.size();++i)
        {
            if(vt[i]<vt[i-dlta[k]])
            {
                vt[0]=vt[i];    //只做暫存資料,不是哨兵
                for(j=i-dlta[k];j>0&&vt[0]<vt[j];j-=dlta[k])
                    vt[j+dlta[k]]=vt[j];
                vt[j+dlta[k]]=vt[0];
            }
        }
    }
}
  1. 氣泡排序:將第一個記錄的關鍵字與第二個記錄的關鍵字進行比較,若為逆序則交換,然後比較第二個記錄和第三個記錄的關鍵字,以此類推。每趟氣泡排序都會將未排序列中最大的元素沉到最後。時間複雜度O(n^2),空間複雜度O(1):
void Sort(vector<int> &vt)
{
    for(int i=vt.size()-1;i>0;--i)
    {
        for(int j=0;j<i;++j)
        {
            if(vt[j]>vt[j+1])
            {
                int tp=vt[j];
                vt[j]=vt[j+1];
                vt[j+1]=tp;
            }
        }
    }
}
  1. 快速排序:通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可對這兩部分記錄繼續排序,依次達到整個序列有序。時間複雜度O(n*logn)(如果待排序列有序或基本有序,退化為氣泡排序,時間複雜度O(n^2),空間複雜度O(logn):
int Partition(vector<int> &vt,int low,int high)
{
    //對順序表vt中子表r[low…high]進行排序
    int tp=vt[low];
    while(low<high)
    {
        while(low<high&&vt[high]>=tp)--high;
        vt[low]=vt[high];//從後往前找到比樞軸小的元素賦值給low位置
        while(low<high&&vt[low]<=tp)++low;
        vt[high]=vt[low];//從前往後找到比樞軸大的元素賦值給high位置
    }
    vt[low]=tp;//將樞軸賦值到位
    return low;//返回樞軸
}

void QSort(vector<int> &vt,int low,int high)
{
    if(low<high)
    {
        int pivot=Partition(vt,low,high);
        QSort(vt,low,pivot-1);
        QSort(vt,pivot+1,high);
    }
}
  1. 簡單選擇排序:通過n-i次關鍵字間的比較,從n-i+1個記錄中選出關鍵字最小的記錄,並和第i個記錄交換,每趟選出最小(或最大)的元素放到最前面。時間複雜度O(n^2),空間複雜度O(1):
void SelectSort(vector<int> &vt)
{
    for(int i=0;i<vt.size();i++)
    {
        for(int j=i+1;j<vt.size();j++)
        {
            if(vt[i]>vt[j]){
                int tp=vt[i];
                vt[i]=vt[j];
                vt[j]=tp;
            }
        }
    }
}
  1. 堆排序:n個元素的序列{k1,k2,…,kn}當且僅當滿足{ki<=k2i ; ki<=k2i+1}或{ki>=k2i ; ki>=k2i+1}時,稱之為堆。堆頂元素必定是序列中的最大值(最小值)。若在輸出堆頂的最值之後,調整堆似得剩餘n-1個元素的序列重又構成堆,則得到次最值,以此類推便可以得到一個有序序列,即堆排序。時間複雜度O(nlogn),空間複雜度O(1):
void HeapAdjust(vector<int> &vt,int s,int m)
{
    if(s==m)return;//s==m==0的時候直接退出
    int tp=vt[s];
    for(int j=2*s+1;j<=m;j*=2)//依次往下調整堆
    {
        if(j<m&&vt[j]<vt[j+1]) ++j;
        if(tp>vt[j])break;
        vt[s]=vt[j];
        s=j;
    }
    vt[s]=tp;
}

void HeapSort(vector<int> &vt)
{
    for(int i=vt.size()/2-1;i>=0;--i) //從最後一個非葉子結點開始調整
        HeapAdjust(vt,i,vt.size()-1); //建立大頂堆
    for(int i=vt.size()-1;i>0;--i)
    {
        swap(vt[0],vt[i]);//將堆頂記錄和最後一個元素交換,然後重新調整大頂堆
        HeapAdjust(vt,0,i-1);    
}
}
  1. 歸併排序:利用有序表歸併操作實現的排序,核心操作是將一位陣列中前後相鄰的兩個有序序列歸併為一個有序序列。時間複雜度O(nlogn),空間複雜度O(n):
void Merge(vector<int> ovt,vector<int> &vt,int f,int m,int b)
{
    int i,j;
    for(i=f,j=m+1;f<=m&&j<=b;++i)
    {
        if(ovt[f]<ovt[j])vt[i]=ovt[f++];
        else vt[i]=ovt[j++];
    }
    while(f<=m)vt[i++]=ovt[f++];
    while(j<=b)vt[i++]=ovt[j++];
}

void MSort(vector<int> &vt,int f,int b)
{
    if(f==b)vt[f]=vt[f];
    else{
        int m=f+(b-f)/2;
        MSort(vt,f,m);
        MSort(vt,m+1,b);
        Merge(vt,vt,f,m,b); //歸併操作
    }
}

9.基數排序:藉助“分配”和“收集”兩種操作對單邏輯關鍵字進行排序的一種內部排序方法。時間複雜度O(d(n+rd)),空間複雜度O(rd)。