数据结构与算法学习笔记二---顺序表的动态存储表示和实现(C语言)

目录

前言

1.顺序表的动态存储过程

1.初始化

2.销毁

3.清空

4.表长

5.取值

6.直接前驱

7.直接后继

8.插入

9.删除  

10.遍历

11.完整代码

2.顺序表操作时间复杂度分析

1.取值

2.查找

3.插入

4.删除


前言

        这篇博客主要是顺序表的动态分配存储表示。

1.顺序表的动态存储过程

        线性表的顺序存储示意图如下:

               图1.线性表的顺序存储示意图

        我们使用静态数据来存储顺序表中的数据元素,静态顺序表定义如下:

//----- 线性表的静态分配顺序存储结构 - - - - -
#define MaxSize 100
typedef int ElementType;
typedef int Status;

typedef struct {
    ElementType data[MaxSize]; // 存储空间基址
    int length;            // 当前长度
}StaticSqList;

1.初始化

        初始化的时候给顺序表动态分配存储空间,设置表长为0.

//初始化
Status initSqList(SqList * sqList){
    sqList->element = (ElementType *)malloc(sizeof(ElementType) * LIST_INIT_SIZE);
    if (!sqList->element) {
        printf("内存分配失败!");
        return 0;
    }
    sqList->length = 0;
    sqList->listSize = LISTINCREMENT;
    return 1;
}

2.销毁

        释放动态分配的存储空间,置空表长和存储空间大小。

// 销毁
void destroySqList(SqList *sqList){
    free(sqList->element);
    sqList->length = 0;
    sqList->listSize = 0;
}

3.清空

        表长置空即可。

// 清空表
void clearSqList(SqList *sqList){
    sqList->length = 0;
}

4.表长

        因为我们在顺序表中定义了表长,直接返回表长即可。

// 表长
int getSqListLength(SqList *sqList){
    return sqList->length;
}

5.取值

        取值操作是根据指定的位置序号i,获取顺序表中第i个数据元素的值。由于顺序存储结构具有随机存取的特点,可以直接通过数组下标定位得到,elem[i-1]单元存储第i个数据元素。

        具体算法:

  1. 判断指定位置是否合法,不合理直接返回0
  2. 位置合法,直接通过下标获取数据元素,然后返回数据元素
Status getElementForSqList(SqList *sqList, int pos, ElementType *element){
    if (pos < 1 || pos > sqList->length) {
        return 0;
    }
    *element = sqList->element[pos - 1];
    return 1;
}

        数据类型(data type)是一个值的集合和定义在这个集合上的一组操作的总称。

6.直接前驱

        遍历顺序表,这里注意的是顺序表中的第一个数据元素没有直接前驱。

// 我这里返回的是数据元素的真实下标,从1开始
Status getSqListLocate(SqList * sqList,ElementType element,int * location){
    for (int i = 0; i < sqList->length; i++) {
        if (sqList->element[i] == element) {//找到数据元素
            * location = i;
            return 1;
        }
    }
    return 0;
}

7.直接后继

         遍历顺序表,这里注意的是顺序表中的最后一个数据元素没有直接后继。

// 获取直接前驱 获取element的前一个数据元素priorElement
Status priorSqListElement(SqList * sqList,ElementType element,ElementType * priorElement){
    for (int i = 0; i<sqList->length; i++) {
        if (sqList->element[i] == element) {//找到数据元素
            if (i>=1) {
                * priorElement = sqList->element[i-1];
                return 1;
            }
        }
    }
    return 0;
}

8.插入

               图2.顺序表插入数据元素示意图

        首先判断要插入的数据元素的位置是否合法,然后判断顺序表是否已满,已满的话需要给顺序表重新分配存储空间。

Status insertSqList(SqList * sqList,int pos,ElementType element){
    if (pos < 1 || pos > sqList->length + 1) {//非法位置
        return 0;
    }
    if (sqList->length >=sqList->listSize ) {//
        ElementType * newElement = (ElementType *)realloc(sqList->element,(sqList->listSize + LISTINCREMENT) * sizeof(ElementType));
        if (!newElement) {
            return 0;
        }
        sqList->listSize+= LISTINCREMENT;
        sqList->element = newElement;
    }
    for (int i = sqList->length; i > pos - 1; i--) {
        sqList->element[i] = sqList->element[i-1];
    }
    sqList->element[pos -1] = element;
    sqList->length++;
    return 1;
}

9.删除  

        

       图3.删除元素示意图

        判断位置是否合法,遍历顺序表。

Status deleteSqList(SqList *sqList,int pos,ElementType * element){
    if (pos < 1 || pos > sqList->length) {
        return 0;
    }
    for (int i = pos - 1; i < sqList->length - 1; i++) {
        sqList->element[i] = sqList->element[i+1];
    }
    * element = sqList->element[pos -1];
    sqList->length--;
    return 1;
}

10.遍历

// 遍历顺序表
void travseSqList(SqList *sqList){
    for (int i = 0; i<sqList->length; i++) {
        printf("%d\t",sqList->element[i]);
    }
    printf("\n");
}

11.完整代码

#include <stdio.h>
#include <stdlib.h>

//----- 线性表的静态分配顺序存储结构 - - - - -
#define LIST_INIT_SIZE  100   // 线性表存储空间的初始分配量
#define LISTINCREMENT 10    // 线性表存储空间的分配增量
typedef int ElementType;
typedef int Status;

typedef struct {
    ElementType *element; // 存储空间的基址
    int length;            // 当前长度
    int listSize;          // 当前存储空间的大小(以sizeof(ElementType)为单位)
} SqList;

//初始化
Status initSqList(SqList *sqList){
    sqList->element = (ElementType *)malloc(sizeof(ElementType) * LIST_INIT_SIZE);
    if (!sqList->element) {
        printf("内存分配失败!\n");
        return 0;
    }
    sqList->length = 0;
    sqList->listSize = LIST_INIT_SIZE;
    return 1;
}

// 销毁
void destroySqList(SqList *sqList){
    free(sqList->element);
    sqList->length = 0;
    sqList->listSize = 0;
}

// 清空表
void clearSqList(SqList *sqList){
    sqList->length = 0;
}

// 表长
int getSqListLength(SqList *sqList){
    return sqList->length;
}

Status sqListIsEmpty(SqList *sqList){
    return sqList->length == 0;
}

Status getElementForSqList(SqList *sqList, int pos, ElementType *element){
    if (pos < 1 || pos > sqList->length) {
        return 0;
    }
    *element = sqList->element[pos - 1];
    return 1;
}

// 我这里返回的是数据元素的真实下标,从0开始
Status getSqListLocate(SqList *sqList, ElementType element, int *location){
    for (int i = 0; i < sqList->length; i++) {
        if (sqList->element[i] == element) {
            *location = i;
            return 1;
        }
    }
    return 0;
}

Status insertSqList(SqList *sqList, int pos, ElementType element){
    if (pos < 1 || pos > sqList->length + 1) {
        return 0;
    }
    if (sqList->length >= sqList->listSize) {
        ElementType *newElement = (ElementType *)realloc(sqList->element, (sqList->listSize + LISTINCREMENT) * sizeof(ElementType));
        if (!newElement) {
            return 0;
        }
        sqList->listSize += LISTINCREMENT;
        sqList->element = newElement;
    }
    for (int i = sqList->length; i > pos - 1; i--) {
        sqList->element[i] = sqList->element[i - 1];
    }
    sqList->element[pos - 1] = element;
    sqList->length++;
    return 1;
}

// 获取直接前驱
Status priorSqListElement(SqList *sqList, ElementType element, ElementType *priorElement){
    for (int i = 0; i < sqList->length; i++) {
        if (sqList->element[i] == element) {
            if (i >= 1) {
                *priorElement = sqList->element[i - 1];
                return 1;
            }
        }
    }
    return 0;
}

// 获取直接后继
Status nextSqListElement(SqList *sqList, ElementType element, ElementType *nextElement){
    for (int i = 0; i < sqList->length; i++) {
        if (sqList->element[i] == element) {
            if (i < sqList->length - 1) {
                *nextElement = sqList->element[i + 1];
                return 1;
            }
        }
    }
    return 0;
}

// 删除
Status deleteSqList(SqList *sqList, int pos, ElementType *element){
    if (pos < 1 || pos > sqList->length) {
        return 0;
    }
    *element = sqList->element[pos - 1];
    for (int i = pos - 1; i < sqList->length - 1; i++) {
        sqList->element[i] = sqList->element[i + 1];
    }
    sqList->length--;
    return 1;
}

// 遍历顺序表
void travseSqList(SqList *sqList){
    for (int i = 0; i < sqList->length; i++) {
        printf("%d\t", sqList->element[i]);
    }
    printf("\n");
}

void commonSqListPrint(const char *string){
    printf("\n**********\t%s测试\t**********\n", string);
}

//测试顺序表的方法
void testSqList(void){
    SqList sqList;
    commonSqListPrint("顺序表的静态存储表示");
    if (initSqList(&sqList)) {
        printf("顺序表初始化成功\n");
    }
    commonSqListPrint("判断顺序表是否为空");
    if (sqListIsEmpty(&sqList)) {
        printf("顺序表为空, 顺序的长度:%d\n", getSqListLength(&sqList));
    }

    commonSqListPrint("顺序表批量插入测试");
    for (int i = 1; i <= 5; i++) {
        if (insertSqList(&sqList, i, i)) {
            printf("数据元素%d插入成功!\n", i);
        } else {
            printf("数据插入失败!\n");
        }
    }

    printf("插入之后的数组为:\n");
    travseSqList(&sqList);

    commonSqListPrint("单个数据元素插入");

    if (insertSqList(&sqList, 5, 11)) {
        printf("数据元素插入成功\n");
    } else {
        printf("插入失败\n");
    }
    printf("插入之后的数组:\n");
    travseSqList(&sqList);

    commonSqListPrint("顺序表表长:");
    printf("顺序的长度:%d\n", getSqListLength(&sqList));

    commonSqListPrint("顺序表删除");
    int deleteElement;
    if (deleteSqList(&sqList, 5, &deleteElement)) {
        printf("数据元素%d删除成功\n", deleteElement);
    } else {
        printf("删除失败\n");
    }

    printf("删除之后的顺序表:\n");
    travseSqList(&sqList);

    commonSqListPrint("获取顺序表中的数据元素");
    for (int i = 0; i <= 11; i++) {
        int location;
        if (getSqListLocate(&sqList, i, &location)) {
            printf("数据元素%d的下标获取成功, 数据元素下标为:%d\n", i, location);
        } else {
            printf("第%d个数据元素下标获取失败\n", i);
        }
    }

    commonSqListPrint("顺序表直接前驱");
    for (int i = 1; i <= sqList.length; i++) {
        ElementType priorElem;
        if (priorSqListElement(&sqList, i, &priorElem)) {
            printf("数据元素%d存在, 直接前驱为:%d\n", i, priorElem);
        } else {
            printf("数据元素%d存在, 直接前驱不存在\n", i);
        }
    }

    commonSqListPrint("顺序表直接后继");
    for (int i = 1; i <= sqList.length; i++) {
        ElementType nextElem;
        if (nextSqListElement(&sqList, i, &nextElem)) {
            printf("数据元素%d存在, 直接后继为:%d\n", i, nextElem);
        } else {
            printf("数据元素%d存在, 直接后继不存在\n", i);
        }
    }

    commonSqListPrint("顺序表销毁测试");
    destroySqList(&sqList);
    printf("顺序表已销毁\n");
}

int main(int argc, const char *argv[]) {
    testSqList();
    return 0;
}

2.顺序表操作时间复杂度分析

1.取值

        顺序表取值的时候,时间复杂度为O(1)。

2.查找

        当在顺序表中查找一个数据元素时,其时间主要耗费在数据的比较上,而比较的次数取决于被查元素在线性表中的位置。
        在查找时,为确定元素在顺序表中的位置,需和给定值进行比较的数据元素个数的期望值称为查找算法在查找成功时的平均查找长度(Average Search Length,ASL)。
        假设p是查找第1个元素的概率,C为找到表中其关键字与给定值相等的第i个记录时,和给定值已进行过比较的关键字个数,则在长度为n的线性表中,查找成功时的平均查找长度为:

        图4.平均查找长度

        从顺序表查找的过程可见,C;取决于所查元素在表中的位置。例如,查找表中第一个记录时,仅需比较一次;而查找表中最后一个记录时,则需比较n次。一般情况下C等于i。
        假设每个元素的查找概率相等,即P(i)=1/n

图5.平均查找时间复杂度

3.插入

        当在顺序表中某个位置上插入一个数据元素时,其时间主要耗费在移动元素上,而移动元素的个数取决于插入元素的位置。
        假设p是在第1个元素之前插入一个元素的概率,与为在长度为n的线性表中插入一个元素时所需移动元素次数的期望值(平均次数),则有

图6.移动数据元素的平均次数

4.删除

        当在顺序表中某个位置上删除一个数据元素时,其时间主要耗费在移动元素上,而移动元素的个数取决于删除元素的位置。
        假设p是删除第i个元素的概率,E为在长度为n的线性表中删除一个元素时所需移动元素次数的期望值(平均次数),则有

        假设在线性表的任何位置上删除元素的概率都是等概率的,则

        P(i) = 1/n;

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫柱子哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值