數據結構-線性表


一、線性表的定義

  線性表(List):零個或多個數據元素的有限序列

二、線性表的抽象數據類型

ADT 線性表 (List)
Data
Operation
    lnitList(*L);//初始化操作,建立一個空的線性表
    ListEmpty(L);//若線性表為空,返回true,否則返回false
    ClearList(*L);//清空線性表
    GetElem(L,i,*e);//將線性表L中的第i個位置元素值返回給e
    LocateElem(L,e);//在線性表L中查找與e相等的元素,若有,返回元素的序號,否則追回0表示失敗
    Listlnsert(*L,i,e);//在線性表L中的第i個位置插入新元素e
    ListDelete(*L,i,*e);//刪除線性表L中第i個位置元素,並用e返回其值
    ListLength(L);//返回線性表L的元素個數
endADT

 

三、順序存儲定義

  線性表的順序存儲定義:用一段地址連續的存儲單元依次存儲線性表的數據元素

1、順序存儲方式 

  線性表的每個數據元素的類型都相同,所以可以使用一維數據來實現順序存儲結構,即把第一個數據元素存放在數組下標為0的位置中,然后接着把線性表相鄰的元素存放在數組相鄰的位置

 

1、順序存儲的結構代碼
#define MAXSIZE 20 /*存儲空間初始化分配量*/
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status
typedef int ElemType;
typedef Struct
        {
            ElemType data[MAXSIZE]; /*數組存儲數據元素*/
            int  length; /*線性表當前長度*/
        }SqList;
        // 描述順序存儲結構的三個屬性
        // 存儲空間的起始位置:數組data,它的位置就是存儲空間的位置
        // 線性表的最大存儲容量:數組長度MAXSIZE
        // 線性表的當前長度:length

 

2、順序存儲結構的操作

 初始化順序表

void lnitList(SqList L)
    {
     if (L == NULL) { return; } L->length
=0; }
//算法的時間復雜度為O(1)

 

 獲取元素

Staus GetElem(SqList L,int i,ElemType *e)
{
if(L.length==0 || i<1 || i>L.length)
    return ERROR ;
*e=L.data[i-1] ;
return OK ;
}
//算法的時間復雜度為O(1)

 插入操作

// 算法思路:插入位置不合理,拋出異常
        // 如果線性表長度大於等於數組長度,則拋出異常或動態增加容量
        // 從最后一個元素向前遍歷到第i個位置,分別將他們向后移動一個位置
        // 將要插入的元素填入i處
     // 表長加1
Status Listlnsert(SqList L,int i,ElemType *e) { int k; if (L->length==MAXSIZE)return ERROR;/*線性表已滿*/ if (i<1 || i>L->length+1)return ERROR;/*當i不在范圍*/ if (1<=L->length)/*當i不在表尾*/ { for (k=L->length-1; k>=i-1; k--) { L->data[K+1]=L->data[k]; } } L->data[i-1]=e;/*插入新元素*/ L->length++;/*表長加一*/ return OK; }
//算法的時間復雜度為O(n)

 刪除操作

// 算法思路:刪除位置不合理,拋出異常
        // 取出刪除的元素
         // 從刪除元素位置開始遍歷到最后一個元素的位置
         // 分別將他們向前移動一個位置
         // 表長減一
        Status ListDelete(SqList L,int i,ElemType *e)
        {
            int k;
            if (L->length==0)return ERROR;/*線性表為空*/
            if (i<1 || i>L->length)return ERROR;/*當i不在范圍*/
            *e=L->data[i-1];
            if (1<L->length)/*當i不在表尾*/
            {
                for (k=i; k<L->length; k++)
                {
                    L->data[K-1]=L->data[k];
                }
            }
            L->length--;/*表長減一*/
            return OK;
        }
//算法的時間復雜度為O(n)

線性表的順序存儲結構在存、讀數據的時候,不管是哪個位置,時間復雜度都為0(1);而插入或刪除時,時間復雜度都是0(n)

四、線性表的順序存儲結構的優缺點和特點

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

五、線性表的鏈式存儲結構定義

  為了表示每個數據元素與它的后繼數據元素的邏輯關系,對於數據元素a來說,除了存儲其本身的信息之外(數據域),還需要存儲一個指示其直接后繼的存儲位置(指針域)。這兩部分信息組成數據元素a的存儲映像,稱為結點(Node)。

  n個結點鏈成一個鏈表,即為線性表(a1,a2,a3,...,an)的鏈式存儲結構

1、單鏈表

  在每個結點中除包含有數據域外,只設置一個指針域,用以指向其后繼結點

  頭指針:鏈表中的第一個結點的存儲位置叫做頭指針

        頭結點:在單鏈表的第一個結點前附設一個結點,叫做頭結點

/*線性表的單鏈表存儲結構*/
typedef struct Node { ElemType data; struct Node *next; }Node; typedef struct Node *LinkList;/*定義LinkList*/

1.1單鏈表的創建

   (1)頭插法建立單鏈表

    從一個空表開始,讀取數組a的元素,生成新結點,將讀取的數據放在新結點的數據域中,然后將新結點插入到當前鏈表的表頭,直到結束為止

    void CreateListF(LinkList *&L,ElemType a[],int n)
    {
        LinkList *s;int i;
        L=(LinkList*)malloc(sizeof(LinkList));/*創建頭結點*/
        L->next=NULL;
        for (i = 0; i < n; i++)
        {
            s=(LinkList*)malloc(sizeof(LinkList));/*創建新結點*/
            s->data=a[i];
            s->next=L->next;
            L->next=s;
        }
    }

  (2)尾插法建立單鏈表

    從一個空表開始,讀取數組a的元素,生成新結點,將讀取的數據放在新結點的數據域中,然后將新結點插入到當前鏈表的尾結點,但是要增加一個尾結點指針r。使他始終指向當前鏈表的表尾上,直到結束為止

void CreateListR(LinkList *&L,ElemType a[],int n)
    {
        LinkList *s,*r;int i;
        L=(LinkList*)malloc(sizeof(LinkList));/*創建頭結點*/
        r=L;/*r始終指向尾結點,開始時指向頭結點*/
        for (i = 0; i < n; i++)
        {
            s=(LinkList*)malloc(sizeof(LinkList));/*創建新結點*/
            s->data=a[i];
            r->next=s;/*將*s插入到*r之后*/
            r=s;
        }
        r->next=NULL;/*尾結點的next域設置為NULL*/
    }

1.2單鏈表的讀取

  獲取第i個數據的算法思路

    1. 聲明一個指針p指向鏈表第一個結點,初始化j從1開始
    2. 當j<i時,就遍歷鏈表,讓p指針向后移動,不斷指向先一個結點,j累加1
    3. 若到鏈表末尾p為空,說明第i個結點不存在
    4. 否則查找成功,返回結點p的數據
Status GetElem(LinkList L,int i,ElemType *e)
{
    int j;
    LinkList p;/*聲明一個指針p*/
    p=L->next;/*讓p指向鏈表L的第一個結點*/
    j=1;
    while(p && j<1)
    {
        p=p->next;
        ++j;
    }
    if (!p || j>i)return ERROR;
    *e=p->data;return OK;
}    

1.3單鏈表的插入與刪除

  單鏈表第i個數據插入結點的算法思路

  1. 聲明一個指針p指向鏈表的頭結點,初始化j從1開始
  2. 當j<i時,就遍歷鏈表,讓p指針向后移動,不斷指向先一個結點,j累加1
  3. 若到鏈表末尾p為空,說明第i個結點不存在
  4. 否則查找成功,在系統中生成一個空結點s
  5. 將數據元素e賦值給s->data
  6. 單鏈表的插入標准語句s->next=p->next;p->next=s;
  7. 返回成功

Status Listlnsert(LinkList *L,int i,ElemType e)
{
    int j;
    LinkList p,s;
    p= *L;
    j= 1;
    while(p && j<i)
    {
        p=p->next;
        ++j;
    }
    if (!p || j>i)return ERROR;
    s=(LinkList)malloc(sizeof(LinkList));
    s->data=e;
    s-next=p->next;/*將p的后繼結點賦值給s的后繼*/
    p->next=s;/*將s賦值給p的后繼*/
    return OK;
}

 

  單鏈表第i個數據刪除結點的算法思路

  1. 聲明一個指針p指向鏈表的頭結點,初始化j從1開始
  2. 當j<i時,就遍歷鏈表,讓p指針向后移動,不斷指向先一個結點,j累加1
  3. 若到鏈表末尾p為空,說明第i個結點不存在
  4. 否則查找成功,將欲刪除的結點p->next賦值給q
  5. 單鏈表的刪除標准語句p->next=q->next
  6. 將q結點中的數據賦值給e,作為返回
  7. 釋放q結點
  8. 返回成功

Status ListDelete(LinkList *L,int i,ElemType *e)
{
    int j;
    LinkList p,q;
    p= *L;
    j= 1;
    while(p->next && j<i)
    {
        p=p->next;
        ++j;
    }
    if (!(p->next) || j>i)return ERROR;
    q=p->next;
    p-next=q->next;
    *e=q->data;
    free(q);
    return OK;
}

1.4單鏈表的整表刪除

  單鏈表的整表刪除算法思路

  1. 聲明一個結點p和q
  2. 將第一個結點賦值給p
  3. 循環
    • 將下一個結點賦值給q; 
    • 釋放p
    • 將q賦值給p

 

Status ClearList(LinkList *L)
{
    LinkList p,q;
    p=(*L)->next;
    while(p)
    {
        q=p->next;
        free(p);
        p=q;
    }
    (*L)->next=NULL;
    return OK;
}

 

2、雙鏈表

  在每個結點中除包含數值域外,設置有兩個指針域,分別用以指向其前驅結點和后繼結點

//雙鏈表結點類型DLinkList的聲明如下
typedef struct DLinkList
{
    ElemType data;
    struct DLinkList *prior; //指向前驅結點
    struct DLinkList *next;  //指向后繼結點
}DLinkList;

2.1雙鏈表的創建

   (1)頭插法建立雙鏈表

    從一個空表開始,讀取數組a的元素,生成新結點,將讀取的數據放在新結點的數據域中,然后將新結點插入到當前鏈表的表頭,直到結束為止

void CreateDListF(LinkList *&L,ElemType a[],int n)
    {
        DLinkList *s;int i;
        L=(DLinkList*)malloc(sizeof(DLinkList));/*創建頭結點*/
        L->next=L->prior=NULL;
        for (i = 0; i < n; i++)
        {
            s=(DLinkList*)malloc(sizeof(DLinkList));/*創建新結點*/
            s->data=a[i];
            s->next=L->next;
            if (L->next!=NULL)
            {
                L->next->prior=s;
            }
            L->next=s;
            s->prior=L;
        }
    }

 (2)尾插法建立雙鏈表

    從一個空表開始,讀取數組a的元素,生成新結點,將讀取的數據放在新結點的數據域中,然后將新結點插入到當前鏈表的尾結點,但是要增加一個尾結點指針r。使他始終指向當前鏈表的表尾上,直到結束為止

void CreateDListR(LinkList *&L,ElemType a[],int n)
    {
        DLinkList *s,*r;int i;
        L=(DLinkList*)malloc(sizeof(DLinkList));/*創建頭結點*/
        r=L;
        for (i = 0; i < n; i++)
        {
            s=(DLinkList*)malloc(sizeof(DLinkList));/*創建新結點*/
            s->data=a[i];
            r->next=s;
            s->prior=r;
            r=s;
        }
        r->next=NULL;
    }

 2.2雙鏈表的基本運算法則

 (1)查找指定元素的結點

    在雙鏈表中查找第一個data域值為X的結點、從第一個結點開始。變遍歷邊比較。若找到這樣的結點。則返回序列。否則返回0

 

int Finfnode(DLinkList *L,ElemType X)
{
    LinkList *P=l->next;
    int i=1;
    while(p!=NULL&&p->data!=x)
    {
        i++;
        p=p->next;
    }
    if (p==NULL)return 0;
    else return i;
}

 

  (2)插入結點操作

s->next=p->next;   //將*s插入到*p之后
p->next-prior=s;
s->prior=p;
p->next=s;

//在雙鏈表L中第i個位置插入值域為e的結點
int Listlnsert(DLinkList *&L,int i,ElemType e)
{
    int j=0;
    DLinkList *p=L,*s;
    while(j<i-1 && p!=NULL) //查找第i-1個元素
    {
        j++;
        p=p->next;
    }
    if (p==NULL)return 0; //未找到第i-1個節點
    else                  //找到第i-1個節點
    {
        s=(DLinkList *)malloc(sizeof(DLinkList));    //創建新結點*s
        s->data=e;
        s->next=p->next;        //將*s插入到*p之后
        if (p->next!=NULL)        //若存在*p的后繼結點。將其前驅指向*s
        {
            p->next->prior=s;
        }
        s->prior=p;
        p->next=s;
        return 1;
    }
}
    

  (3)刪除結點操作

p->next=q->next;      //刪除*p結點后的結點
q->next-prior=p;

//在雙鏈表L中刪除第i個結點
int ListDelete(DLinkList *&L,int i,ElemType e)
{
    DLinkList *p=L,*q;
    int j=0;
    while(j<i-1 && p!=NULL) //查找第i-1個元素
    {
        j++;
        p=p->next;
    }
    if (p==NULL)return 0; //不存在第i-1個節點
    else                  //找到第i-1個節點*p
    {
        q=p->next;                    //q指向要刪除的結點
        if (q==NULL)return 0;        //不存在第i個結點
        p->next=q->next;            //從鏈表中刪除*q結點
        if (p->next!=NULL)    
        p->next->prior=p;
        free(q);                    //釋放*q結點
        return 1;
    }
}

 


免責聲明!

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



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