數據結構之線性表

1 線性表

必會概念:

鏈式存儲 前驅 後繼 表長

鏈表的本質:

LinkList L; L = create_LinkList(10);
此時L看起來是一個線性表,其實它本質只是一個節點的引用,指向線性表表頭節點.通過這個引用我們可以遍歷整張線性表

1.1數據結構如何應用到運算

邏輯結構定義算法,存儲結構運行算法

線性表常用邏輯算法:

  1. 初始化:Init_List(L) 構造一個空表
  2. 求表長:Length_List(L) 返回表含有的元素的個數
  3. 取表元:Get_List(L,i) L非空,i正確時,返回地i個表元的值或地址
  4. 查找(定位):已知值,查下標. Locate_List(L,x) 返回x在L中首次出現的序號或地址,否則返回-1
  5. 插入:Insert_List(L,i,x) L存在,i正確.是L[i]=x; L.len()++; L[i]之後的表元下標依次加一
  6. 刪除: Delete_List(L,i) L存在,i正確時 L[i] = L[i+1] ;L.len()-- ;L[i]之後的表元下標依次減一

1.2 順序表

列表在內存中按連續物理地址存儲,知道了地址就一定能訪問到對應的元素

優點與好處

  1. 已知表頭地址Loc($a_0$),表元類型(所佔字節) d比特,則瞬間可求第(i+1)個元素的地址.Loc($a_i$)=Loc($a_0$)+i*d , 0≤i≤n
  2. 所以順序存儲用來查找,訪問,修改元素非常方便

1.3 順序表的實現

距離是元素間相距長度,個數是下標相減再加1.

Capacity(L) = L.MAXSIZE ;下標n必須時刻≤MAXSIZE ;設插入下標爲i,最末下標爲n; i必須時刻≤n+1;

1.3.1 動態初始化

  1. 構造一個空表.
  2. 動態分配存儲空間,先確定表容量,但具體地址對應的表元在後續代碼裏確定
  3. 定義表指針last=-1,隨L.len()增加而增加

1.3.2 插入運算

  1. 用for循環實現:j=n+1 從$L(n)$起,元素依次向後挪動一位.$L(j)$ = $L(j-1)$; j–;
  2. 插入x: L(i) = x;
  3. 修改表尾指針:L.last = L.len()++;
  4. 算法複雜度計算: 主要耗時都發生在元素移動時.每次移動(n-i+1)個元素,即移動(n-i+1)次,假設插入到底i個元素是等概率事件,則對應的概率$P_i$=1/(n+1).
  5. 總移動次數 = $\sum_{i=1}^{n+1}=P_i(n-i+1)=\frac{n}{2}$,即時間複雜度是O(n)

1.3.3 刪除操作

  1. 0≤i≤n,否則報錯,已經包含了i=-1,L爲null時的情景.
  2. for循環實現:j=;j≤n;從L[i]起,L[j] = L[j+1];j++;
  3. 算法複雜度:O(n)

1.3.4 查找操作

找列表中值爲x的第一個元素下標

  1. for循環,j=0; L[j]==x ; 否則j++;
  2. 若j==n,返回"無"
  3. 算法複雜度:i=0nPii=n+12=O(n)\sum_{i=0}^nP_ii=\frac{n+1}{2}=O(n)`

1.4 線性表的鏈式存儲與運算實現

先修知識

malloc與new的區別

new會自動根據類型分配內存地址,並返回同類型的指針給引用

int *p1,*p2;
p1 = new int;
p2 = new int[100];

malloc只能動態分配內存,意思就是不會自動分配,必須人爲設定內存大小與返回的指針類型

int *p;
p = (int*)malloc(sizeof(int)*100);

->的用法

->是間接引用符,是二目訪問符,類似於成員符 .

typedef struct Node{
    int a;
    double b;
}node, *linkList;
*linkList = &node;
//struct Node *p = &node;
//此時
linkList->a等價於(*linkList).a;

struct的用法

自定義類型struct

typedef struct Node{
    Elemtype a;
    Node *next;
}node,linkList; //結構變量node等有點像靜態類,這裏借用java編程思想描述,Node是父類,node,linkList是子類.
或
struct Node node,linkList;//按模板類型Node定義具體類型node,linkList

寫一個struct Node,編譯器只是讀取它,並不會爲它賦予內存空間.邏輯合理,它只是一個模板.

1 線性表

必會概念:

鏈式存儲 前驅 後繼 表長

鏈表的本質:

LinkList L; L = create_LinkList(10);
此時L看起來是一個線性表,其實它本質只是一個節點的引用,指向線性表表頭節點.通過這個引用我們可以遍歷整張線性表

1.1數據結構如何應用到運算

邏輯結構定義算法,存儲結構運行算法

線性表常用邏輯算法:

  1. 初始化:Init_List(L) 構造一個空表
  2. 求表長:Length_List(L) 返回表含有的元素的個數
  3. 取表元:Get_List(L,i) L非空,i正確時,返回地i個表元的值或地址
  4. 查找(定位):已知值,查下標. Locate_List(L,x) 返回x在L中首次出現的序號或地址,否則返回-1
  5. 插入:Insert_List(L,i,x) L存在,i正確.是L[i]=x; L.len()++; L[i]之後的表元下標依次加一
  6. 刪除: Delete_List(L,i) L存在,i正確時 L[i] = L[i+1] ;L.len()-- ;L[i]之後的表元下標依次減一

1.2 順序表

列表在內存中按連續物理地址存儲,知道了地址就一定能訪問到對應的元素

優點與好處

  1. 已知表頭地址Loc($a_0$),表元類型(所佔字節) d比特,則瞬間可求第(i+1)個元素的地址.Loc($a_i$)=Loc($a_0$)+i*d , 0≤i≤n
  2. 所以順序存儲用來查找,訪問,修改元素非常方便

1.3 順序表的實現

距離是元素間相距長度,個數是下標相減再加1.

Capacity(L) = L.MAXSIZE ;下標n必須時刻≤MAXSIZE ;設插入下標爲i,最末下標爲n; i必須時刻≤n+1;

1.3.1 動態初始化

  1. 構造一個空表.
  2. 動態分配存儲空間,先確定表容量,但具體地址對應的表元在後續代碼裏確定
  3. 定義表指針last=-1,隨L.len()增加而增加

1.3.2 插入運算

  1. 用for循環實現:j=n+1 從$L(n)$起,元素依次向後挪動一位.$L(j)$ = $L(j-1)$; j–;
  2. 插入x: L(i) = x;
  3. 修改表尾指針:L.last = L.len()++;
  4. 算法複雜度計算: 主要耗時都發生在元素移動時.每次移動(n-i+1)個元素,即移動(n-i+1)次,假設插入到底i個元素是等概率事件,則對應的概率$P_i$=1/(n+1).
  5. 總移動次數 = $\sum_{i=1}^{n+1}=P_i(n-i+1)=\frac{n}{2}$,即時間複雜度是O(n)

1.3.3 刪除操作

  1. 0≤i≤n,否則報錯,已經包含了i=-1,L爲null時的情景.
  2. for循環實現:j=;j≤n;從L[i]起,L[j] = L[j+1];j++;
  3. 算法複雜度:O(n)

1.3.4 查找操作

找列表中值爲x的第一個元素下標

  1. for循環,j=0; L[j]==x ; 否則j++;
  2. 若j==n,返回"無"
  3. 算法複雜度:i=0nPii=n+12=O(n)\sum_{i=0}^nP_ii=\frac{n+1}{2}=O(n)`

1.4 線性表的鏈式存儲與運算實現

先修知識

malloc與new的區別

new會自動根據類型分配內存地址,並返回同類型的指針給引用

int *p1,*p2;
p1 = new int;
p2 = new int[100];

malloc只能動態分配內存,意思就是不會自動分配,必須人爲設定內存大小與返回的指針類型

int *p;
p = (int*)malloc(sizeof(int)*100);

->的用法

->是間接引用符,是二目訪問符,類似於成員符 .

typedef struct Node{
    int a;
    double b;
}node, *linkList;
*linkList = &node;
//struct Node *p = &node;
//此時
linkList->a等價於(*linkList).a;

struct的用法

自定義類型struct

typedef struct Node{
    Elemtype a;
    Node *next;
}node,linkList; //結構變量node等有點像靜態類,這裏借用java編程思想描述,Node是父類,node,linkList是子類.
或
struct Node node,linkList;//按模板類型Node定義具體類型node,linkList

寫一個struct Node,編譯器只是讀取它,並不會爲它賦予內存空間.邏輯合理,它只是一個模板.

後進先出,前插法

抽象:表L; 新節點s(malloc,s->a=x); s->next=L(指向表頭);L=s(表頭指向s,s再指向next裏保存的舊錶頭地址)

    linkList create_stack(int flag){
        linkList L = null; //linkList是指針子類,故L也是指針,又L=null靜態聲明,省去了malloc分配.
        // 同時! L==null;第一個節點s->next=L,巧妙地使最後一個節點的next爲null.不會出現野指針.
        node *s;
        int x;
        scanf("%d",&x);
        int cout=1;
        while(cout<=flag){
            s=(Node*)malloc(sizeof(node));//每次循環都會手動new一個s.因爲新的s節點的next後繼是上一節點,所以新的s節點是上一節點的前驅.即新的s節點是前插的.滿足棧後進先出的形式.
            s->data=x;
            s->next=L;//s上鍊指向L的表頭,此時s還不算在L內
            L = s;//讓s做L的表頭,即巧妙地讓L永遠指向新的表頭元素.
            scanf("%d",&x);
            cout++;
        }
        return L;
    }

隊列

後進後出,尾插法

表L永遠指向表頭,我們可以用副表*r先指向第一個節點(s作表頭時),它與L都是指向第一個節點的引用.

linkList create_quene(int flag){
    linkList L=null;
    node *s,*r=null;
    int x;
    scanf("%d",&x);
    int cout=1;
    while(cout<=flag){
        s = malloc(sizeof(node)); //new一個s,new完對s的屬性賦值
        s->a = x; 
        if(L=null) L=s;
        else r->next = s;上鍊
        r = s; //此處r,L都是s的軟引用.L佔了r的光,之後一條龍的鏈表全靠r接上.L最多指向表頭,其餘上鍊操作有r來完成
        scanf("%d",&x);
        cout++;
    }
    //定義表尾節點的後繼節點爲空.
}

1.4.1 節點

單鏈表由一個個節點構成

typedef struct node{
    int data;
    struct node *next;
}LNode

node LNode = {};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章