【数据结构】顺序表的动态分配(步骤代码详解)

在这里插入图片描述

🎈个人主页:豌豆射手^
🎉欢迎 👍点赞✍评论⭐收藏
🤗收录专栏:数据结构
🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步!

【数据结构】顺序表的动态分配的实现步骤

  • 引言
  • 一 初始化顺序表结构体:
    • 1.1 代码:
    • 1.2 代码分析:
  • 二 分配内存空间:
    • 2.1 代码:
    • 2.2 代码分析:
  • 三 设置属性:
    • 3.1 代码:
  • 四 检查内存分配
  • 五 空间不足时重新分配:
    • 5.1 代码:
    • 5.2 代码分析
  • 六 元素操作:
    • 6.1 代码
    • 6.2 代码分析
  • 七 销毁顺序表:
  • 总结

在这里插入图片描述

引言

在计算机科学中,数据结构是组织和存储数据的方式,它决定了数据如何被存储、检索和操作。

顺序表作为一种线性数据结构,其内部元素在物理存储上按照顺序连续存放。然而,静态的顺序表在创建时就需要确定其大小,这在实际应用中往往不够灵活。

因此,实现顺序表的动态分配变得尤为重要。动态分配允许我们在运行时根据需要调整顺序表的大小,从而更加高效地管理和使用内存。

本文将详细阐述顺序表动态分配的实现步骤,包括初始化结构体、分配内存空间、设置属性、检查内存分配、空间不足时重新分配、元素操作以及销毁顺序表等关键步骤

顺序表中的动态分配涉及一系列步骤以确保在程序执行时能够根据需要分配内存空间,从而管理线性表的数据元素。以下是顺序表动态分配的具体步骤:

一 初始化顺序表结构体:

首先,需要创建一个顺序表的结构体,其中通常包含指向动态分配数组的指针、顺序表的最大容量以及当前的长度等属性。

1.1 代码:

typedef struct {  int *data;        // 指向数据数组的指针  int length;       // 顺序表当前长度  int capacity;     // 顺序表最大容量  
} SeqList;

1.2 代码分析:

这段代码定义了一个名为SeqList的结构体,用于表示一个顺序表(线性表的顺序存储结构)。以下是每个步骤的详细介绍:

  1. 定义结构体类型

    typedef struct {
    

    使用typedef关键字结合struct关键字定义了一个新的结构体类型SeqList。这样,后续代码中可以直接使用SeqList来声明该类型的变量,而不必每次都写出struct关键字。

  2. 数据数组指针

    int *data;        // 指向数据数组的指针
    

    在结构体中定义了一个指向int类型的指针data。这个指针用于指向顺序表存储数据的数组。当顺序表被初始化时,data指向一个动态分配的内存块,用于存储顺序表中的元素。

  3. 顺序表当前长度

    int length;       // 顺序表当前长度
    

    length变量用于记录顺序表中当前存储的元素个数。它反映了顺序表的实际大小,与顺序表的容量(capacity)不同,容量是顺序表能够容纳的最大元素数量

  4. 顺序表最大容量

    int capacity;     // 顺序表最大容量
    

    capacity变量表示顺序表的最大容量,即它能够存储的元素的最大数量。这个值在顺序表初始化时确定,并且可以通过动态内存分配进行扩展

  5. 结束结构体定义

    } SeqList;
    

    这个分号标志着结构体定义的结束。此时,SeqList已经成为了一个有效的类型,可以在后续代码中用于声明变量。

通过使用SeqList这个结构体,可以方便地管理顺序表的存储和状态。通过修改data指针、lengthcapacity的值,可以实现顺序表的动态内存分配、元素的插入和删除等操作。同时,由于data是一个指针,顺序表可以灵活地调整其大小,以适应不同数量的元素存储需求

二 分配内存空间:

使用malloc或类似的函数在内存中为顺序表的结构体和数据数组分配一块连续的空间

这个空间的大小可以根据需要动态确定,通常初始时分配一个默认的大小

2.1 代码:

SeqList* list = (SeqList*)malloc(sizeof(SeqList));  
if (list == NULL) {  perror("Failed to allocate memory for SeqList");  exit(EXIT_FAILURE);  
}  
list->data = (int*)malloc(INIT_CAPACITY * sizeof(int));  
if (list->data == NULL) {  perror("Failed to allocate memory for data array");  free(list);  exit(EXIT_FAILURE);  
}

2.2 代码分析:

这段代码实现了顺序表结构体的动态内存分配,以及顺序表内部数据数组的初始分配。以下是每个步骤的详细介绍:

  1. 分配顺序表结构体的内存
SeqList* list = (SeqList*)malloc(sizeof(SeqList));

使用malloc函数为SeqList类型的结构体分配内存空间。

sizeof(SeqList)计算了SeqList结构体所占用的字节数malloc函数根据这个大小在堆上分配内存,并返回指向这块内存的指针。该指针被强制类型转换SeqList*类型,并赋值给list变量。

在C语言中,malloc 函数用于在堆上动态分配指定字节数的内存,并返回指向这块内存的指针。

这个返回的指针类型是 void*,即指向任意类型的指针。在C语言中,void* 类型的指针可以赋给任何其他类型的指针,但是为了避免类型不匹配导致的潜在问题,并且为了使代码更加清晰,通常我们会将 void* 类型的指针显式转换为目标类型的指针。

  1. 检查内存分配是否成功
if (list == NULL) {  perror("Failed to allocate memory for SeqList");  exit(EXIT_FAILURE);  
}

检查malloc函数是否成功分配了内存。

如果malloc返回NULL,表示内存分配失败。这时,perror函数用于打印出系统错误信息,说明内存分配失败的原因。然后,程序调用exit(EXIT_FAILURE)终止执行,并返回非零的退出状态码,表示程序异常结束。

  1. 分配数据数组的内存
list->data = (int*)malloc(INIT_CAPACITY * sizeof(int));

为顺序表的数据数组分配内存空间。INIT_CAPACITY是一个预先定义的常量,表示顺序表初始时的容量大小。`

malloc函数根据INIT_CAPACITY * sizeof(int)`计算出需要分配的总字节数,并在堆上分配相应的内存空间。

返回的指针被强制类型转换为int*类型,并赋值给list->data,即顺序表结构体的data成员。

list->data 这条语句在C语言中表示通过结构体指针 list 来访问其指向的结构体中的 data 成员。这里,list 是一个指向 SeqList 类型结构体的指针,而 dataSeqList 结构体中的一个成员,其类型为 int*(指向整数的指针)。

具体来说:

  • list 是一个指针,它存储了某个 SeqList 结构体在内存中的地址。
  • -> 是一个结构体指针的成员访问运算符。它用于通过结构体指针来访问其指向的结构体中的成员。
  • dataSeqList 结构体中的一个成员,它是一个指向整数数组的指针。

因此,list->data 的意思就是取 list 指针指向的 SeqList 结构体中的 data 成员的值,即这个结构体所关联的数据数组的指针。通过这个指针,你可以访问或修改顺序表中的数据。

例如,如果你想访问顺序表中的第一个元素,你可以这样做:

int firstElement = *(list->data);

这里,list->data 获取数据数组的指针,* 运算符用于解引用这个指针,从而得到数组中的第一个元素。

如果你想设置顺序表中的第一个元素为某个值,比如10,你可以这样做:

*(list->data) = 10;

这样,你就通过 list->data 成功地修改了顺序表中的数据。

  1. 再次检查内存分配是否成功
if (list->data == NULL) {  perror("Failed to allocate memory for data array");  free(list);  // 释放之前为顺序表结构体分配的内存exit(EXIT_FAILURE);  
}

再次检查malloc函数是否成功分配了内存。

如果malloc返回NULL,说明数据数组的内存分配失败。

此时,程序首先调用free(list)释放之前为顺序表结构体分配的内存,避免内存泄漏。

然后,使用perror打印出错误信息,并通过exit(EXIT_FAILURE)终止程序执行。

通过上述步骤,代码成功地为顺序表结构体和数据数组分配了内存,并进行了必要的错误检查。如果所有内存分配都成功,那么list指针现在指向一个有效的顺序表结构体,其data成员指向一个能够存储INIT_CAPACITY个整数的数组。之后,就可以使用这个顺序表进行元素的插入、删除、查找等操作了。

三 设置属性:

将分配的内存地址赋值给顺序表结构体的相应指针,并设置顺序表的最大容量和当前长度为初始值。

3.1 代码:

list->length = 0;  
list->capacity = INIT_CAPACITY;

四 检查内存分配

在每次内存分配后,都需要检查是否分配成功。如果malloc返回NULL,则表示内存分配失败,此时需要进行错误处理,如打印错误信息并退出程序。上面的代码已经包含了这一步。

五 空间不足时重新分配:

随着顺序表中元素的增加,当空间不足时,需要动态地重新分配更大的内存空间。

我们需要执行以下步骤:

1 分配新的内存块,大小为所需的新容量。

2 将旧内存块中的数据复制到新内存块中。

3 释放旧内存块。

4 更新指针和容量。

5.1 代码:

if (list->length >= list->capacity) {    int new_capacity = list->capacity * 2; // 扩大为原来的两倍    int *new_data = (int*)malloc(new_capacity * sizeof(int));    if (new_data == NULL) {    perror("Failed to allocate memory for new data array");    free(list->data);    free(list);    exit(EXIT_FAILURE);    }  // 将旧数据复制到新分配的内存中  for (int i = 0; i < list->length; i++) {  new_data[i] = list->data[i];  }  // 释放旧内存  free(list->data);  // 更新指针和容量  list->data = new_data;  list->capacity = new_capacity;  
}

5.2 代码分析

这段代码的主要目的是在动态数组(或称为顺序表)list的当前容量不足以存储更多元素时,对其容量进行扩展。以下是代码各步骤的详细解释:

  1. 检查容量是否足够

    if (list->length >= list->capacity) {
    

    这行代码检查list的当前长度(list->length)是否已经达到或超过了其当前容量(list->capacity)。如果是,则需要进行内存扩展。

  2. 计算新容量

    int new_capacity = list->capacity * 2; // 扩大为原来的两倍
    

    这里将新容量设置为当前容量的两倍。这是一种常见的扩展策略,因为它简单且通常足够应对增长需求。然而,具体的扩展策略可能会根据应用需求的不同而有所变化。

  3. 分配新内存

    int *new_data = (int*)malloc(new_capacity * sizeof(int));
    

    使用malloc函数为新的数据数组分配内存。new_capacity * sizeof(int)计算了新数组所需的字节数。如果malloc成功,它将返回一个指向新分配内存的指针,否则返回NULL

  4. 检查内存分配是否成功

    if (new_data == NULL) {perror("Failed to allocate memory for new data array");free(list->data);free(list);exit(EXIT_FAILURE);
    }
    

    如果malloc返回NULL,说明内存分配失败。此时,代码打印一个错误消息,释放任何已经分配给listlist->data的内存,然后退出程序。

  5. 复制旧数据到新内存

    for (int i = 0; i < list->length; i++) {new_data[i] = list->data[i];
    }
    

    通过一个循环,将旧数据数组list->data中的元素逐个复制到新分配的内存new_data中。

  6. 释放旧内存

    free(list->data);
    

    释放指向旧数据数组的指针所引用的内存。这是非常重要的步骤,因为如果不释放旧内存,就会导致内存泄漏。

  7. 更新指针和容量

    list->data = new_data;
    list->capacity = new_capacity;
    

    list结构中的data指针更新为指向新分配的内存,并将capacity更新为新容量。这样,list现在就指向一个容量更大的数据数组,并且可以继续添加更多元素。

通过以上步骤,代码成功地在不改变原list结构指针的情况下,扩大了其内部数据数组的容量,并确保所有现有数据都被保留下来。这种技术在动态数据结构的实现中非常常见,特别是在处理可能快速增长的数据集时。

六 元素操作:

在动态分配的空间上执行顺序表的插入、删除、查找等操作。这些操作需要根据顺序表的当前状态(如长度和容量)来正确执行,并确保数据的完整性和一致性。

6.1 代码

元素操作的代码会根据具体的操作而有所不同,例如插入、删除和查找等。这里提供一个插入操作的示例:

int InsertList(SeqList *list, int index, int elem) {  if (index < 0 || index > list->length) {  return -1; // 插入位置无效  }  // 动态分配(如果必要)已在前面的步骤中完成  // 移动元素,为新元素腾出空间  for (int i = list->length; i > index; i--) {  list->data[i] = list->data[i - 1];  }  list->data[index] = elem; // 插入新元素  list->length++; // 更新顺序表长度  return 0;  
}

6.2 代码分析

这段代码定义了一个函数InsertList,用于在顺序表(或称为动态数组)list的指定位置index插入一个元素elem。顺序表通过结构体SeqList来定义,其中至少包含指向数据数组的指针data、数组当前长度length和数组容量capacity。下面是对代码中每个步骤的详细解释:

  1. 检查插入位置的有效性

    if (index < 0 || index > list->length) {  return -1; // 插入位置无效  
    }
    

    这里首先检查传入的index是否在有效的插入范围内。如果index小于0或者大于list的当前长度list->length,则意味着插入位置无效,函数返回-1表示错误。

  2. 注释:动态分配(如果必要)已在前面的步骤中完成

    // 动态分配(如果必要)已在前面的步骤中完成
    

    这是一个注释,说明在调用InsertList函数之前,已经确保了list有足够的容量来存储新元素。这通常意味着在某个地方(可能是在插入操作之前,或者当添加元素导致list容量不足时)已经进行了内存分配或重新分配。由于这段代码没有直接包含这部分逻辑,所以这是一个前提假设。

  3. 移动元素,为新元素腾出空间

    for (int i = list->length; i > index; i--) {  list->data[i] = list->data[i - 1];  
    }
    

    这个循环的目的是将index位置及其之后的所有元素向后移动一个位置,从而为新元素腾出空间。循环从list->length开始(即数组的最后一个元素的下一个位置),逐步向前移动到index + 1。在每次迭代中,都将当前位置的元素值赋给其后面的位置,这样就实现了元素的向后移动。

  4. 插入新元素

    list->data[index] = elem;
    

    在已经腾出的index位置上,将新元素elem的值赋给list->data[index],从而完成了新元素的插入。

  5. 更新顺序表长度

    list->length++;
    

    由于已经成功插入了一个新元素,因此需要更新list的长度。将list->length加1以反映新元素的添加。

  6. 返回成功标志

    return 0;
    

    如果所有步骤都成功执行,函数返回0,表示插入操作成功。

整个InsertList函数遵循了顺序表插入操作的标准步骤:检查插入位置的有效性、为新元素腾出空间、插入新元素、更新长度,并返回操作结果。这样的设计保证了顺序表在插入操作后的正确性和一致性。

七 销毁顺序表:

当不再需要顺序表时,需要释放其占用的内存空间。这通常涉及使用free函数来释放之前通过mallocrealloc分配的内存块。

void DestroyList(SeqList *list) {  free(list->data); // 释放数据数组的内存  free(list); // 释放顺序表结构体的内存  
}

通过上述步骤,顺序表能够实现动态的内存分配和管理,从而根据程序的需求高效地存储和访问线性表的数据元素。需要注意的是,动态内存分配涉及到内存管理的复杂性,因此在编写代码时需要仔细处理各种边界条件和错误情况,以确保程序的正确性和稳定性。

总结

通过本文的详细阐述,我们了解了顺序表动态分配的实现步骤。从初始化顺序表结构体开始,到分配内存空间、设置属性、检查内存分配,再到空间不足时的重新分配、元素操作,以及最终的销毁顺序表,每一步都至关重要。

动态分配的实现使得顺序表在实际应用中更加灵活和高效,能够根据实际需求动态调整大小,避免了静态顺序表在大小确定上的局限性。

同时,我们也需要注意在内存分配和释放过程中的安全性和正确性,以避免内存泄漏和野指针等问题。

通过掌握这些实现步骤和注意事项,我们可以更好地利用顺序表这一数据结构,为实际应用提供有力的支持。

这篇文章到这里就结束了

谢谢大家的阅读!

如果觉得这篇博客对你有用的话,别忘记三连哦。

我是豌豆射手^,让我们我们下次再见

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/788562.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

某盾滑块拼图验证码增强版

介绍 提示&#xff1a;文章仅供交流学习&#xff0c;严禁用于非法用途&#xff0c;如有不当可联系本人删除 最近某盾新推出了&#xff0c;滑块拼图验证码&#xff0c;如下图所示&#xff0c;这篇文章介绍怎么识别滑块距离相关。 参数attrs 通过GET请求获取的参数attrs, 决…

Python 与机器学习,在服务器使用过程中,常用的 Linux 命令包括哪些?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 本博客旨在分享在实际开发过程中&#xff0c;开发者需要了解并熟练运用的 Linux 操作系统常用命令。Linux 作为一种操作系统&#xff0c;与 Windows 或 MacOS 并驾齐驱&#xff0c;尤其在服务器和开发环…

时序分解 | Matlab实现GSWOA-VMD改进鲸鱼优化算法优化变分模态分解时间序列信号分解

时序分解 | Matlab实现GWO-CEEMDAN基于灰狼算法优化CEEMDAN时间序列信号分解 目录 时序分解 | Matlab实现GWO-CEEMDAN基于灰狼算法优化CEEMDAN时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现GSWOA-VMD改进鲸鱼优化算法优化变分模态分解时间序…

单元测试——Junit (断言、常用注解)

单元测试 Junit单元测试框架 使用 断言测试 使用Assert.assertEquals(message, 预期值, 实际值); 这段代码是用于在测试中验证某个方法的返回值是否符合预期。其中&#xff0c;"方法内部有bug"是用于在断言失败时显示的提示信息。4是预期的返回值&#xff0c;index…

买了云服务器不会用?教你使用京东云!

1. 前言 最近出现了许多云服务器的活动&#xff0c;由于活动期间优惠的价格、极高的性价比&#xff0c;因此&#xff0c;无论是企业&#xff0c;还是私人用户&#xff1b;无论是云服务器玩的溜的老手&#xff0c;还是新手小白都直接冲了起来&#xff01;但是对于一些还未使用过…

数字未来:探索 Web3 的革命性潜力

在当今数字化的时代&#xff0c;Web3作为互联网的新兴范式正逐渐崭露头角&#xff0c;引发了广泛的关注和探讨。本文将深入探索数字未来中Web3所蕴含的革命性潜力&#xff0c;探讨其对社会、经济和技术的深远影响。 1. Web3&#xff1a;数字世界的下一个阶段 Web3是一个正在崛…

AWS入门实践-S3对象存储的基本用法

AWS S3(Simple Storage Service)是亚马逊云服务提供的一种高度可扩展、安全且经济高效的对象存储服务。它允许用户在任何位置存储和检索任意数量的数据,非常适合存储和分发静态文件、备份数据以及作为数据湖的存储层。 一、S3上传和下载文件&#xff08;AWS门户&#xff09; …

Excel列匹配VLookUp功能使用

生活中很多关于excel多列数据进行匹配计算等场景,其中最常用的一个函数就是VLookUp了,下面直接上图: 得到结果如下: 得到结果如下: 注意: 1.在需要把计算完的数据粘贴到另一列或者另个sheet时,复制后,不要直接ctrlv粘贴,这样会把计算公式粘贴到对应的列.正确做法是:右键粘贴,选…

游戏引擎架构01__引擎架构图

根据游戏引擎架构预设的引擎架构来构建运行时引擎架构 ​

数据库-root密码丢失的重置方案(win11环境)

当在windows系统中安装的mysql由于操作不当&#xff0c;或者密码遗忘&#xff0c;今天测试了一下&#xff0c;可以用以下方法重置root的密码。 mysqlwindows环境root密码重置问题 在win10/11环境下mysql8密码遗忘后的重置密码方案。 停止mysql服务 查找windows中的mysql服务名称…

springboot之RESTful接口与Swagger

一、RESTful GET获取资源、POST新建资源、PUT更新资源、DELETE删除资源。 RESTful两大特性 1、安全性&#xff1a;GET请求不会引起资源本身改变。 2、幂等性&#xff1a;对一个接口请求和多次请求返回的资源应该一致。 2xx&#xff1a;成功 4xx&#xff1a;客户端错误。 …

03 | Swoole 源码分析之 Http Server 模块

首发原文链接&#xff1a;Swoole 源码分析之 Http Server 模块 大家好&#xff0c;我是码农先森。 Http 模块的注册初始化 这次我们分析的就是 Swoole 官网的这段代码&#xff0c;看似简单&#xff0c;实则不简单。 在 Swoole 源码文件 swoole_http_server.c 中有这样一个函数…

gin源码分析(1)--初始化中间件,路由组与路由树

目标 关于gin.Default()&#xff0c;gin.New()&#xff0c;gin.Use()group与子group之间的关系&#xff0c;多group与middleware之间关系中间件的类型&#xff0c;全局&#xff0c;group&#xff0c;get&#xff0c;不同类型的中间件什么时候执行。中间件 next 和abort行为如何…

Go-Gin中优雅的实现参数校验,自定义错误消息提示

问题描述 在参数校验的时候我们一般会基于"github.com/go-playground/validator/v10"这个库给结构体加标签实现校验参数&#xff0c;当参数校验错误的时候&#xff0c;他的提示一般是英文的&#xff0c;怎么自定义参数错误提示呢&#xff1f;跟着我一步步来 注册校…

OpenAI 宣布, ChatGPT 网页端无需注册就能立即使用(2024年4月1日)

今天&#xff0c;OpenAI宣布&#xff0c;为了让更多人轻松体验人工智能的强大功能&#xff0c;现在无需注册账户即可立即使用 ChatGPT。这一变化是他们使命的核心部分&#xff0c;即让像 ChatGPT 这样的工具广泛可用&#xff0c;让世界各地的人们都能享受到 AI 带来的好处。 网…

PostgreSQL的学习心得和知识总结(一百三十五)|深入理解PostgreSQL数据库之查找 PostgreSQL C 代码中的内存泄漏

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

【苍穹外卖】SkyApplication类启动报错

报的这个错 The PoM for com.sky:sky-common:jar:1.0-SNAPSHoT is missing, no dependency information available Maven里重新install一下就好

01-​JVM学习记录-类加载器

一、类加载器子系统 1. 作用-运输工具&#xff08;快递员&#xff09; 负责从文件系统或者网络中加载Class文件&#xff08;DNA元数据模板&#xff09;&#xff0c;Class文件开头有特定标识&#xff0c;魔术&#xff0c;咖啡杯壁&#xff08;class文件存于本地硬盘&#xff0c…

Java 设计模式系列:备忘录模式

简介 备忘录模式是一种软件设计模式&#xff0c;用于在不破坏封闭的前提下捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。 备忘录模式提供了一种状态恢复的实现机制&#xff0c;使得用户可以方便地回到一个特定…

微信小程序开发学习笔记——4.8【小案例】初识wx.request获取网络请求并渲染至页面

>>跟着b站up主“咸虾米_”学习微信小程序开发中&#xff0c;把学习记录存到这方便后续查找。 课程连接&#xff1a;4.8.【小案例】初识wx.request获取网络请求并渲染至页面_哔哩哔哩_bilibili up主提供的网络请求常用接口&#xff1a; 随机猫咪&#xff0c;用来获取一些…