数据结构02 - 顺序表

线性表(List):零个或多个数据元素的有限序列;

线性表可以记为(a1, a2, …, ai-1, ai, ai+1, .., an),表中ai-1领先于ai,称ai-1为ai的直接前驱元素;ai领先于ai+1,称ai+1为ai的直接后继元素
当i=1,2,…,n-1时,ai有且仅有一个直接后继;当i=2,3,…n时,ai有且仅有一个直接前驱;
线性表中元素的个数n(n>=0)定义为线性表的长度;n=0时,线性表为空表
线性表中每个元素都有确定的位置,ai是第i个元素,i称为ai元素的位序

线性表的抽象数据类型:
ADT List
Data
  (a1, a2, …, ai-1, ai, ai+1, .., an)
Operation
  InitList(*L); //初始化操作,建立空表L;
  ListEmpty(L); //判断表L是否空表;
  ListLength(L); //获取表L长度;
  ClearList(*L); //清空表L;
  GetElem(L,i,*e); //返回表L的第i个位置元素给e;
  LocateElem(L,e); //返回表L中值与e相等的元素位序;
  ListInsert(*L,i,e); //在表L的第i个位置插入新元素e;
  ListDelete(*L,i,*e); //删除表L的第i个位置元素并用e返回其值;
endADT

顺序表
线性表的顺序存储结构,是指用一段地址连续的存储单元依次存储线性表的数据元素,也称作顺序表;

C语言中可以使用一维数组来实现顺序存储结构;
 #define MAXSIZE 100 //存储空间的最大长度;
 typedef int ElemType; //数据元素的数据类型;
 typedef struct
 {
   ElemType data[MAXSIZE]; //存储数据元素的数组;
   int length; //线性表当前长度;
 }SqList;

顺序表中的地址计算
由于顺序表的存储空间是连续的,假设每个数据元素需要占m个存储单元,则:
LOC(ai+1) = LOC(ai) + m
LOC(ai) = LOC(a1) + m*(i-1)
也就是说,获取任意一个元素的地址,时间复杂度都是O(1),所以顺序存储结构也称为随机存取结构

顺序表的代码描述:

#include <stdio.h>

#define MAXSIZE 100
#define OK      1
#define ERROR   0
#define TRUE    1
#define FALSE   0

typedef int ElemType;

typedef struct
{
    ElemType data[MAXSIZE];
    int length;
}SeqList;

int InitList(SeqList *L);
int ListLength(SeqList L);
int ListEmpty(SeqList L);
int ClearList(SeqList *L);
int PrintList(SeqList L);
int GetElem(SeqList L, int pos, ElemType *e);
int LocateElem(SeqList L, ElemType e);
int ListInsert(SeqList *L, int pos, ElemType e);
int ListDelete(SeqList *L, int pos, ElemType *e);

//初始化操作,建立空表L;
int InitList(SeqList *L)
{
    L->length = 0;
    return OK;
}

// 判断表L是否空表;
int ListEmpty(SeqList L)
{
    if (0 == L.length) {
        return TRUE;
    } else {
        return FALSE;
    }
}

// 获取表L长度;
int ListLength(SeqList L)
{
    return L.length;
}

// 清空表L;
int ClearList(SeqList *L)
{
    int i;
    for (i = 0; i < MAXSIZE; i++)
    {
        L->data[i] = 0;
    }
    L->length = 0;
    return OK;
}

// 打印表L; 
int PrintList(SeqList L)
{
    if (0 == L.length) {
        printf("空表\n");
    } else {
        int i;
        for (i = 0; i < L.length; i++)
        {
            printf("%d  ", L.data[i]);
        }
        printf("\n");
    }
    return OK;
}

// 返回表L的第pos个元素给e;
int GetElem(SeqList L, int pos, ElemType *e)
{
    if (pos < 1 || pos > L.length) {
        printf("非法位序!!!\n");
        return ERROR;
    } else {
        *e = L.data[pos - 1];
        return OK;
    }
}

// 返回表L中值与e相等的元素位序;
int LocateElem(SeqList L, ElemType e)
{
    int i;
    int pos = -1;
    if (L.length > 0)
    {
        for (i = 0; i < L.length; i++)
        {
            if (L.data[i] == e)
            {
                pos = i + 1;
                break;
            }
        }
    }
    return pos;
}

// 在表L的第pos个位置插入新元素e;
int ListInsert(SeqList *L, int pos, ElemType e)
{
    if (MAXSIZE == L->length) {
        printf("顺序表已经满了!!!\n");
        return ERROR;
    }

    if (pos < 1 || pos > L->length + 1) {
        printf("非法位序不能插入数据!!!\n");
        return ERROR;
    }

    int i;
    // 插入数据的位置不在表尾; 
    if (pos <= L->length)
    {
        // 从第pos个元素到最后一个元素, 全部向后移动一位; 
        for (i = L->length - 1; i >= pos - 1; i--)
        {
            L->data[i + 1] = L->data[i];
        }
    }
    L->data[pos - 1] = e;
    L->length++;

    return OK;
}

// 删除表L的第pos个元素并用e返回其值;
int ListDelete(SeqList *L, int pos, ElemType *e)
{
    if (0 == L->length) {
        printf("顺序表是空的!!!\n");
        return ERROR;
    }

    if (pos < 1 || pos > L->length) {
        printf("非法位序不能删除数据!!!\n");
        return ERROR;
    }

    *e = L->data[pos - 1];

    int i;
    // 删除的数据位置不在表尾; 
    if (pos < L->length)
    {
        // 从第pos+1个元素到最后一个元素, 全部向前移动一位; 
        for (i = pos - 1; i <= L->length - 1; i++)
        {
            L->data[i] = L->data[i + 1];
        }
    }
    L->length--;

    return OK;
}

int main()
{
    SeqList sList;

    InitList(&sList);

    int len = ListLength(sList);
    printf("len = %d\n", len);

    if (TRUE == ListEmpty(sList)) {
        printf("是个空表!!!\n");
    } else {
        printf("不是空表.\n");
    }

    // 赋值;
    len = 10;
    int i;
    for (i = 0; i < len; i++)
    {
        sList.data[i] = i + 1;
    }
    sList.length = len;

    PrintList(sList);

    // 取值; 
    int ret, val;
    ret = GetElem(sList, 6, &val);
    if (OK == ret) {
        printf("val = %d\n", val);
    } else {
        printf("GetElem ERROR!!!\n");
    }

    // 插入; 
    ret = ListInsert(&sList, 6, -1);
    if (OK == ret) {
        PrintList(sList);
    } else {
        printf("ListInsert ERROR!!!\n");
    }

    // 删除; 
    ret = ListDelete(&sList, 3, &val);
    if (OK == ret) {
        printf("Delete val = %d\n", val);
        PrintList(sList);
    } else {
        printf("ListDelete ERROR!!!\n");
    }

    return 0;
}

顺序表插入操作的复杂度

  • 对于顺序表L=(a1, a2, …, ai-1, ai, ai+1, .., an):
    在第1个位置插入新元素时,需要将n个元素全部往后移动一位;
    在第2个位置插入新元素时,需要将n-1个元素全部往后移动一位;
    ……
    在第n个位置插入新元素时,需要将1个元素往后移动一位;
    在第n+1个位置(表尾)插入新元素时,不需要移动元素;
    根据概率原理,在每个位置插入元素的概率是相同的,平均移动次数为 n/2;

因此时间复杂度为O(n)

顺序表删除操作的复杂度

  • 对于顺序表L=(a1, a2, …, ai-1, ai, ai+1, .., an):
    删除第1个元素时,需要将n-1个元素全部往前移动一位;
    删除第2个元素时,需要将n-2个元素全部往前移动一位;
    ……
    删除第n-1个元素时,需要1个元素往前移动一位;
    删除第n个元素时,不需要移动元素;
    根据概率原理,在每个位置插入元素的概率是相同的,平均移动次数为 (n-1)/2;

因此时间复杂度也为O(n)

顺序表的优缺点

  • 优点:可以快速存储表中任一位置的元素;没有为了表示各元素之间的逻辑关系而增加额外的存储空间;
  • 缺点:插入和删除操作需要移动大量元素;表长度变化较大时难以确定存储空间的容量;

猜你喜欢

转载自blog.csdn.net/NotSoSerious/article/details/81390656