1. 程式人生 > >2019資料結構考研(二)-----線性表

2019資料結構考研(二)-----線性表

知識框架

這裡寫圖片描述

線性表的基本概念

線性表的定義

線性表是具有相同資料型別的n(n>=0)個數據元素的有限序列,n為線性表的表長,當n=0時,則線性表為空表,線性表的第一個元素稱為表頭,最後一個元素稱為表尾

線性表的基本特點

  • 線性表的元素個數有上限
  • 線性表裡的元素具有順序性
  • 表裡的元素型別都相同
  • 線性表表示一種邏輯上一對一的關係,是邏輯結構,而順序表和連結串列則表示一種儲存結構,不要混淆
  • 表裡的元素具有抽象性,即僅僅討論表裡元素之間的邏輯關係,而不討論表裡每個元素的具體內容

線性表的基本操作

線性表的基本操作

鄰接表:儲存方法跟樹的孩子連結串列示法相類似,是一種順序分配和鏈式分配相結合的儲存結構。如這個表頭結點所對應的頂點存在相鄰頂點,則把相鄰頂點依次存放於表頭結點所指向的單向連結串列中。鄰接表

線性表的順序表示

線性表的順序儲存稱為順序表,順序表的特點:表中的元素的邏輯順序和物理順序是一致的 線性表中元素的位序從1開始,而陣列中元素的下標從0開始的

//c的初始動態分配
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize)
//c++初始動態分配
L.data=new ElemType[InitSize] 

特點:隨機訪問,儲存密度高,只儲存資料元素,邏輯上相鄰的元素在物理上也相鄰

順序表的插入操作

bool ListInsert(Sqlist &L,int i,ElemType e){
    if(i<1||i>L.length+1)//判斷i的範圍是否有效 
    return false;
    if(L.length>=MaxSize)//判斷表的儲存空間是否已滿
    return false;
    for(int j=L.length;j>=i;j--){
        //將i後的元素往後移動
        L.data[j]=L.data[j-1]; 
    } 
    L.data[i-1]=e;//將元素e插入i位置 
    L.length++;
    return true; 
}

判斷i是否有效時,i>length+1,i可以是插在表尾 最好的情況:在表尾插入,不移動元素,時間複雜度為O(1) 最壞的情況:在表頭插入.都要移動,時間複雜度為O(n) 平均情況:pi為概率pi=1/(1+n),有n+1個位置可以插入平均插入時間複雜度

順序表的刪除操作

bool ListDelete(Sqlist &L,int i,ElemType e){
    //本演算法實現刪除順序表中第i個元素 
    if(i<1||i>L.length+1)//判斷i的範圍是否有效 
    return false;
    e=L.data[i-1];//將被刪除的元素賦值給e 
    for(int j=i;j<length;j++){
        //將i後的元素往後移動
        L.data[j-1]=L.data[j]; 
    } 
    L.length--;
    return true; 
}

順序表的按值查詢

int LocateElem(Sqlist L,ElemType e){
    //本演算法實現查詢順序表裡值為e的元素,成功返回位值,否則返回0
    int i;
    for(i=0;i<L.length;i++){
        if(L.data[i]==e)//下標為i的的元素為e,返回其位序i+1 
        return i+1;
    } 
    return 0;
} 

單鏈表

typedef struct LNode{
    ElemType data;//資料域存放資料元素
    struct LNode *next; //指標域 ,存放下一個結點的地址
}LNode,*LinkList;

頭結點和頭指標的區別

  • 無論有沒有頭結點,頭指標都指向連結串列的第一個結點,通常用頭指標來表示一個連結串列L
  • 頭結點,結點內通常不儲存元素,有頭結點的話,頭指標都指向一個非空連結串列 ##單鏈表的基本操作實現 ###頭插法建立單鏈表
typedef struct LNode{
    ElemType data;//資料域 
    struct LNode *next; //指標域 
}LNode,*LinkList;

LinkList CreateList1(LinkList &L){
    LNode *s;
    int x;
    L->next=null;//初始為空連結串列 
    scanf("%d",&x); 
    while(x!=9999)//輸入9999表示結束
    {
        s=(LNode*)malloc(sizeof(LNode));//建立新結點 
        s-data=x;
        s->next=L->next;
        L->next=s;//新結點插入到連結串列中,L為頭指標 
        scanf("%d",&x);
    } 
}

LNode,*LinkListl,都是匿名結構體別名,Lnode是實體,而LiskList是這種ElemType型別的指標 malloc是動態開闢記憶體,函式返回為void型指標(指向開闢的記憶體空間) 前面那個括號是開闢記憶體的型別,如L=(linklist*)malloc(sizeof(lnode)),就是將原來malloc返回的void型指標強制定義為 linklist型(也就是你一開始定義的指標L的型別),這樣才可以賦值給L. sizeof(Inode)是指malloc開闢的記憶體空間的大小,這裡就是指,這個大小為Inode型所佔的容量.(例如sizeof(int),就是開闢一個整形的空間(4位元組).分配兩個int的空間就是2*sizeof(int))

尾插法建立單鏈表

LinkList CreateList2(LinkList &L){
    L=(LinkList)malloc(sizeof(LNode)); 
    LNode *s,*r=L;//r為表尾指標 
    int x;  
    scanf("%d",&x); 
    while(x!=9999)//輸入9999表示結束
    {
        s=(LNode*)malloc(sizeof(LNode));//建立新結點 
        s-data=x;
        r->next=s->next;
        r=s;//r指向新的表尾結點 
        scanf("%d",&x);
    } 
    r->next=null;//尾結點置空 
    return L; 
}

按序號查詢結點

LNode *GetElemt(LinkList L,int i){
    //該演算法取出連結串列中i位置的結點指標
    int j=1;
    LNode *p=L->next;
    if(i==0)
    return L;
    if(i<1)
    return null; 
    while(p&&j<i){
        p=p->next;
        j++;
    }
    return p;//如果i大於表長,p=null 
}

時間複雜度為O(n)

單鏈表的插入操作

前插法

p=GetElemt(L,i-1)//獲得i的前驅結點
s->next=p->next;
p->next=s; 

時間複雜度為O(1) 順序不能顛倒 也可以轉化成後插法,先進行前插,再將兩個的資料進行交換

刪除結點操作

p=GetElemt(L,i-1)//獲得i的前驅結點
q=p->next;//q指向被刪除結點
p->next=q->next;
free(q);//釋放q 結點 

時間複雜度為O(1)

雙鏈表

雙鏈表在單鏈表的基礎上增加了一個結點,有一個前驅結點prior,有一個後繼結點next

typedef struct DNode{
    ElemType data;//資料域 
    struct DNode *next,*prior;//前驅和後繼結點 
}DNode,*DLinkList; 

插入和刪除的時間複雜度為O(1)

雙鏈表的插入操作

在雙鏈表指標p後面插入結點s

s->next = p->next;//1
p->next->prior=s;//2
p->next=s;//3
s->prior=p;//4

1,2兩步必須在3之前

雙鏈表的刪除操作

//刪除q結點
p->next=q->next;
q->next->proir=p; 

迴圈連結串列

迴圈連結串列與單鏈表的區別在於最後一個結點不為空,而改成指向頭結點

順序表和連結串列的比較

  • 存取方式:順序表為隨機存取,而連結串列為順序存取
  • 邏輯結構和物理結構:順序儲存時,邏輯上相鄰的元素在物理結構上也相鄰,而連結串列儲存時,物理結構上不一定相鄰
  • 查詢,刪除和插入操作:對於按值查詢,在順序表無序時,兩者的時間複雜度都為O(n),當順序表有序時可以採用折半查詢,順序表的時間複雜度為O(log2n),對於按序號查詢,順序表的時間複雜度為O(1),而連結串列時間複雜度為O(n)
  • 空間分配:連結串列可以在需要時申請分配,只要記憶體有空間就可以進行分配,高效靈活