第2章_線性表

線性結構的特點是除第一個和最後一個數據元素外每個數據元素只有一個前驅數據元素和一個後繼數據元素。線性表是一種最簡單的線性結構。線性表的操作特點主要是可以在任意 位置插入和刪除一個數據元素。線性表可以用順序存儲結構和鏈式存儲結構存儲。用順序存儲結構實現的線性表稱作順序表,用鏈式存儲結構實現的線性表稱作鏈表。鏈表主要有單鏈表、循環單鏈表和雙向循環鏈表三種。順序表和單鏈表各有優缺點,並且優缺點剛好相反。

線性表的順序表示和實現

線性表的抽象數據類型瑤池示了線性表的數據元素、數據元素間的邏輯關係以及線性表的操作集合。任何需要計算機進行管理和處理的數據元素都必須首先按某種方式存儲在計算機中。線性表的抽象數據類型有兩種存儲結構:一種是順序存儲結構;另一種是鏈式存儲結構。一旦確定了線性表的存儲結構,線性表操作集合中的所有操作就可以具體實現。

typedef struct {
    DataType list[MaxSize];
    int size;
}SeqList;

//順序表操作實現
//1.初始化
void ListInitiate(SeqList*L)
{
    L->size = 0;//定義初始數據元素個數
}

//2.求當前數據元素個數
int ListLength(SeqList L)
{
    return L.size;//返回順序表L的當前數據元素個數
}

//3.插入數據元素
int ListInsert(SeqList*L,int i,DataType x)
{
    /*
    在順序表L的第i(0<=i<=size)個位置前插入數據元素值x
    插入成功返回1,插入失敗返回0
    */
    int j;
    if(L->size >= MaxSize)
    {
        printf("順序表已滿無法插入!\n");
        return 0;
    }
    else if(i < 0 || i > L->size)
    {
        printf("參數i不合法!\n");
        return 0;
    }
    else
    {
        /*爲插入做準備*/
        for(j=L->size;j > i; j--)
        {
            L->list[j] = L->list[j-1];
        }
        L->list[i] = x; /*插入元素x*/
        L->size++;      /*元素個數加1*/
        return 1;
    }
}

//4.刪除數據元素
int ListDelete(SeqList*L,int i,DataType*x)
{
    /*
    刪除順序表L中位置爲i(0<=i<=size-1)的數據元素並存放到x中
    刪除成功返回1,刪除失敗返回0
  */
    int j;
    if(L->size <= 0)
    {
        printf("順序表已空無數據元素可刪!\n");
        return 0;
    }
    else if(i < 0 || i > L->size - 1)
    {
        printf("參數i不合法\n");
        return 0;
    }
    else
    {
        *x = L->list[i];    /* 保存刪除的元素到x中*/
        for(j = i;j < L->size-1; j++)
        {
            L->list[j] = L->list[j+1];
        }
        L->size--;          /*數據元素個數減1*/
        return 1;
    }
}

//5.取數據元素
int ListGet(SeqList L,int i,DataType* x)
{
    /* 取順序表L中第i個數據元素存於x中,成功返回1,失敗返回0*/
    if(i < 0 || i > L.size - 1)
    {
        printf("參數i不合法!\n");
        return 0;
    }
    else
    {
        *x=L.list[i];
        return 1;
    }
}
/* 
例:編程實現如下任務:建立一個線性表,首先依次輸入數據元素1,2,3,...,10,然後刪除數據元素5,最後依次顯示當前線性表中的數據元素。假設該線性表的數據元素個數在最壞情況下不會超過100個。
設計:用順序表實現建立線性表的任務。因該線性表數據元素最壞情況下不會超過100個,所以定義順序表的MaxSize爲100;因該設計任務中數據元素的數據類型是int類型,所以定義順序表的DataType爲int;該任務要求的插入、刪除、取元素等操作可通過調用順序表的插入、刪除、取元素等函數來實現。
*/

#include<stdio.h>           /* 該文件包含printf函數*/
#define MaxSize 100         /* 定義MaxSize爲100*/
typedef int DataType;       /* 定義DataType爲int*/
#include "SeqList.h"        /* 包含順序表文件*/


int main(int argc, char* argv[])
{
    SeqList myList;
    int i,x;
    ListInitiate(&myList);  /* 初始化函數調用*/
    for(i = 0;i < 10; i++)  /* 插入10個數據元素*/
    {
        if(ListInsert(&myList,i,i+1) == 0)  /*插入函數調用*/
        {
            printf("錯誤!\n");
            return ;
        }
    }

    /* 顯示順序表當前的數據元素*/
    for(i = 0;i < ListLength(myList); i++)
    {
        if(ListGet(myList,i,&x) == 0)   /* 取元素函數調用*/
        {
            printf("錯誤!\n");
            return ;
        }
        else
        {
            printf("%d ",x);
        }
    }
    printf("\n");

    if(ListDelete(&myList,4,&x) == 0)   /* 刪除函數調用*/
    {
        printf("錯誤!\n");
        return ;
    }

    /* 顯示順序表當前的數據元素*/
    for(i = 0;i < ListLength(myList); i++)
    {
        if(ListGet(myList,i,&x) == 0)   /* 取元素函數調用*/
        {
            printf("錯誤!\n");
            return ;
        }
        else
        {
            printf("%d ",x);
        }
    }
    printf("\n");
    return 0;
}
/*
1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10
Press any key to continue
*/

這裏寫圖片描述

/*
2.編程實現如下任務:建立一個如下所示的學生情況表,要求先依次輸入數據元素然後依次顯示當前表中的數據元素。假設該表元素個數在最壞情況下不會超過100個。要求使用順序表。
  學號        姓名      性別      年齡
 2000001    張三      男       20
 2000002    李四      男       21
 2000003    王五      女       22
 設計:
 因該表數據元素個數在最壞情況下不會超過100個,所以我們定義順序表的MaxSize爲100;該設計任務的表元素爲學生,從表2-1可知,每個學生數據元素包括學號、姓名、性別和年齡四個數據項。
*/
#include<stdio.h>           /* 該文件包含printf函數*/
#define MaxSize 100         /* 定義MaxSize爲100*/
typedef struct Student{     /* 定義Student類型*/
    long number;            /* 學號數據項*/
    char name[10];          /* 姓名數據項*/
    char sex[3];            /* 性別數據項*/
    int age;                /* 年齡數據項*/
}StudentType;               /* 結構體StudentType*/

typedef StudentType DataType; /* 定義DataType爲StudentType數據類型*/
#include "SeqList.h"        /* 包含順序表文件*/

int main(int argc, char* argv[])
{
    SeqList myList;
    int i;
    StudentType x[3] = {
        {
            2000001,"張三","男",20
        },
        {
            2000002,"李四","男",21
        },
        {
            2000003,"王五","女",22
        }
    };
    StudentType s;

    ListInitiate(&myList);      /* 初始化函數調用*/
    for(i = 0;i < 3; i++)
    {
        if(ListInsert(&myList,i,x[i]) == 0) /*插入函數調用*/
        {
            printf("錯誤!\n");
            return ;
        }
    }

    /* 顯示順序表當前的數據元素*/
    for(i = 0;i < ListLength(myList); i++)
    {
        if(ListGet(myList,i,&s) == 0)   /* 取元素函數調用*/
        {
            printf("錯誤!\n");
            return ;
        }
        else
        {
            printf("%d\t%s\t%s\t%d\n",s.number,s.name,s.sex,s.age);
        }
    }
    printf("\n");
    return 0;
}
/*
2000001 張三    男      20
2000002 李四    男      21
2000003 王五    女      22

Press any key to continue
*/

這裏寫圖片描述

線性表的鏈式表示和實現

單向鏈表
鏈式存儲結構存儲線性表數據元素的方法,是把存放數據元素的結點用指針域構造成鏈。指針是指向物理存儲單元地址的變量,由數據元素域和一個或若干個指針域組成的一個結構體稱爲一個結點。其中,數據域用來存放數據元素,指針域用來構造數據元素之間的關聯關係。鏈式存儲結構的特點是數據元素間的邏輯關係表現在結點的鏈接關係上。鏈式存儲結構的線性表稱爲鏈表。根據指針域的不同和結點構造鏈的方法不同,鏈表主要有單鏈表、單循環鏈表和雙向循環鏈表三種。

/************************************************************************/
/* 單鏈表應用舉例:
建立一個表,首先依次輸入數據元素1,2,3,...,10,然後刪除 數據元素5,
最後依次顯示當前表中的數據元素。要求使用鏈表
設計:
因要求數據元素是int類型,所以定義單鏈表的DataType爲int.對錶元素的插入,刪除,
取元素等操作均可通過調用單鏈表的插入,刪除,取元素等操作來實現。                                                                     
*/
/************************************************************************/
#include <stdio.h>      //包含printf()函數
#include <stdlib.h>     //包含exit()函數
#include <malloc.h>     //包含malloc()等函數

typedef int DataType;   //定義DataType爲int
#include "LinList.h"    //包含單鏈表文件

int main(int argc, char* argv[])
{
    SLNode *head;   //定義頭指針變量
    int i,x;

    ListInitiate(&head);    //初始化

    for(i = 0;i < 10; i++)      //插入10個數據元素
    {
        if(ListInsert(head,i,i+1) == 0)
        {
            printf("錯誤!\n");
            return ;
        }
    }

    for(i = 0;i < ListLength(head); i++)    //顯示當前的數據元素中的值
    {
        if(ListGet(head,i,&x) == 0) //取元素值到x變量中
        {
            printf("錯誤!\n");
            return ;
        }
        else
        {
            printf("%d ",x);    //顯示
        }
    }
    printf("\n");

    if(ListDelete(head,4,&x) == 0)  //刪除下標爲四(值爲5)的數據元素
    {
        printf("錯誤!\n");
        return ;
    }

    for(i = 0;i < ListLength(head); i++)    //顯示當前的數據元素中的值
    {
        if(ListGet(head,i,&x) == 0) //取元素值到x變量中
        {
            printf("錯誤!\n");
            return ;
        }
        else
        {
            printf("%d ",x);    //顯示
        }
    }
    printf("\n");

    Destroy(&head);     //撤消單鏈表
    return 0;
}
/*
1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10
Press any key to continue
*/

這裏寫圖片描述

雙向鏈表
雙向鏈表是每個結點除後繼指針域外還有一個前驅指針域。與單鏈表類同,雙向鏈表也有帶頭結點結構和不帶頭結點結構2種,帶頭結點的雙向鏈表更爲常用。
在雙向鏈表中,每個結點包括三個域,分別是data域,next域和prior域,其中data域爲數據域,next域爲指向後繼結點的指針,prior域爲指向前驅結點的指針。

//雙向鏈表的實現

typedef struct Node
{
    DataType data;
    struct Node *next;
    struct Node *prior;
}DLNode;

//1.初始化
void ListInitiate(DLNode** head)
{
    if((*head=(DLNode*)malloc(sizeof(DLNode))) == NULL) exit(0);
    (*head)->prior = *head; //構成前驅指針循環鏈
    (*head)->next = *head;  //構成後繼指針循環鏈
}

//2.插入數據元素
int ListInsert(DLNode*head,int i,DataType x)
{
    //在帶頭結點的雙向循環鏈表head的第i(0<=i<=size)個結點前
    //插入一個存放數據元素x的結點。插入成功時返回1,失敗返回0
    DLNode*p,*s;
    int j;

    p = head->next;
    j = 0;
    while(p != head && j < i)
    {
        p = p->next;
        j++;
    }

    if(j != i)
    {
        printf("插入位置參數出錯!");
        return 0;
    }

    if((s = (DLNode*)malloc(sizeof(DLNode))) == NULL) exit(0);
    s->data = x;

    s->prior = p->prior;    //修改新增結點 prior域指向
    p->prior->next = s;     //修改新增結點前一個結點的next域指向
    s->next = p;            //修改新增結點的next域
    p->prior = s;           //修改新增結點的下一個結點的prior域指向
    return 1;
}

//3.刪除數據元素
int ListDelete(DLNode*head,int i,DataType*x)
{
    //刪除帶頭結點雙向循環鏈表head的第i(0<=i<=size-1)個結點
    //被刪除結點的數據元素域值由x帶回。刪除成功時返回1,失敗返回0
    DLNode*p;
    int j;
    p = head->next;
    j = 0;
    while(p->next != head && j < i) //尋找第i個結點
    {
        p = p->next;
        j++;
    }
    if(j != i)
    {
        printf("刪除位置參數出錯!");
        return 0;
    }

    p->prior->next = p->next;
    p->next->prior = p->prior;
    *x = p->data;
    free(p);

    return 1;
}

//4.求循環鏈表長度
int ListLength(SLNode*head)
{
    SLNode*p = head;
    int size = 0;
    while(p->next != head)
    {
        p = p->next;
        size++;
    }
    return size;
}

//5.撤消內存空間
void Destroy(DLNode**head)
{
    DLNode *p,*p1;
    int i,n = ListLength(*head);    //爲求長度函數

    p = *head;
    for(i = 0;i <= n; i++)
    {
        p1 = p;
        p = p->next;
        free(p1);
    }
    *head = NULL;
}

靜態鏈表

在鏈式存儲結構中,實現數據元素之間的次序關係依靠指針。我們也可以用數組來構造鏈表,方法是:在數組中增加一個(或兩個)指針域,這些指針域用來存放下一個(或上一個)數據元素在數組中的下標,從而構成用數組構造的單鏈表(或雙向鏈表)。由於相對於申請結點內存空間的動態性來說數組內存空間的申請方式是靜態的,所以這種存儲結構稱作靜態鏈表。由於靜態鏈表中增加的指針仿真了鏈式存儲結構中的指針,所以靜態鏈表中的指針也稱作仿真指針。

算法舉例

順序表算法舉例

/************************************************************************/
/*1.構造一個順序表的刪除算法,把順序表L中數據元素x刪除。
算法思想:此刪除問題可通過一個循環比較過程來實現      
*/
/************************************************************************/
#include"SeqList.h"

int ListDataDelete(SeqList*L,DataType x)
{
    int i,j;
    for(i = 0;i < L->size; i++) //尋找元素x
        if(x == L->list[i]) break;
    if(i == L->size) return 0;  //未尋找到元素x
    else                        //尋找到元素x
    {
        for(j = i;j < L->size; j++)     //元素依次前移
            L->list[j] = L->list[j+1];
        L->size--;                      //元素個數減1
        return 1;
    }
}

/* 
2.構造一個順序表的刪除算法,把順序表L中所有值相同的數據元素x全部刪除
算法思想:刪除算法的基礎上,再增加一重循環來實現值相同數據元素x的全部刪除。
*/
int ListMoreDataDelete(SeqList*L,DataType x)
{
    int i,j;
    int tag = 0;    //初始刪除標記置爲0

    for(i = 0;i < L->size; i++)
    {
        if(x == L->list[i])     //尋找到元素x
        {
            for(j = i;j < L->size; j++) //依次前移
                L->list[j] = L->list[j+1];
            L->size--;          //元素個數減1
            i--;                //保證2個相鄰 的元素均爲x時正確刪除
            tag = 1;            //設置刪除成功標記
        }
    }

    return tag;
}

單鏈表算法舉例

/************************************************************************/
/* 1.設頭指針爲head,並設帶頭結點單鏈表中的數據元素遞增有序,編寫算法將數據
元素x插入到帶頭結點單鏈表的適當位置上,要求插入後保持單鏈表數據元素的遞增有序。
算法思想:
從鏈表的第1個數據元素結點開始,逐個比較每個結點的data域值和x的值,當data小於等於x時,
進行下一個結點的比較;否則,就找到了插入結點的合適位置,此時申請新結點把x存入,
然後把新結點插入;當比較到最後一個結點仍有data小於等於x時,則把新結點插入單鏈表尾。
*/
/************************************************************************/
void LinListInsert(SLNode*head,DataType x)
{
    SLNode*curr,*pre,*q;
    //循環初始化
    curr = head->next;      //curr指向第一個數據元素結點
    pre = head;             //pre指向頭結點
    //循環定位插入位置
    while(curr != NULL && curr->data <= x)
    {
        pre = curr;
        curr = curr->next;
    }
    //申請一個結點並把x存入data域
    if((q = (SLNode*)malloc(sizeof(SLNode))) == NULL) exit(1);
    q->data = x;

    //把新結點插入pre所指結點後
    q->next = pre->next;
    pre->next = q;
}

/*
2.設head爲單鏈表的頭指針,並設單鏈錶帶有頭結點,編寫
算法將單鏈表中的數據元素按照其值遞增有序的順序進行就地排序
*/
void LinListSort(SLNode *head)
{
    SLNode*curr,*pre,*p,*q;
    p = head->next;
    head->next = NULL;

    while(p != NULL)
    {
        curr = head->next;
        pre = head;
        while(curr != NULL && curr->data <= p->data)
        {
            pre = curr;
            curr = curr->next;
        }
        q = p;
        p = p->next;
        q->next = pre->next;
        pre->next = q;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章