数据结构- 顺序表的实现

目录

 

顺序表的简单介绍:

文件之间的关系:

顺序表的实现:

头文件中符号 ""  和  <> 的区别问题:

函数功能的具体实现:

初始化函数:

判满函数: 

扩容函数: 

增加元素函数: 

打印函数: 


在写这个项目之前,我们先要对顺序表和文件之间的关系进行了解并用代码实现。

顺序表的简单介绍:

在计算机内部存储一张线性表(线性结构的数表),最为方便简单的就是用一组连续的地址的内存单元来存储整张线性表。这种存储结构称为顺序存储结构。这种存储结构下的线性表就称为顺序表。

一张顺序表包括这几个特征:

有一个唯一的表名来标识该顺序表;

内存单元连续存储,也就是说一张顺序表必须要占据一块连续的内存空间;

数据顺序存放,元素之间有先后关系;

定义一张顺序表也就是在内存中开辟一段连续的存储空间,同时我们定义的顺序表也应该实现增删查改这四个功能。

文件之间的关系:

在编程过程中,文件编译的顺序一般是从上往下的,如果我们编写的函数在main函数之前,那么在main函数里面我们可以直接进行调用,但是如果我们把函数写在主函数之后,当我们继续在主函数里调用函数时,程序就会报错,这里我们举一个简单的例子。

例如我在这里定义一个Add函数,但是我把它写在了主函数的后面:

程序报错显示使用了未定义的关键字Add,此时我们要消除掉这个报错的话,方法就是在主函数前面进行函数的声明,添加语句:

int Add(int a,int b);

注意后面必须要有分号!

在文件中,同样也需要进行函数声明,函数声明和函数功能的实现分别是在不同的文件中完成的。这里引入两个文件形式,分别是:.h和.cpp后缀的文件:

.h文件形式名为头文件,当我们在实现一个功能比较复杂的程序的时候,我们可以将所有的函数声明都写在.h文件里面,并在文件中进行结构体的设计以及常变量和宏常量的定义;

然后我们再在cpp文件里面再进行具体函数功能的实现,并在开头以头文件的形式对,h文件进行引用,这样下来的一个组合就称之为编译器中的一个工程项目,例如我们在接下来即将耀实现的顺序表。

头文件中符号 ""  和  <> 的区别问题:

注意,在这里我们必须要分清楚在引用头文件时 ""  和  <>的区别。

但是我们如果直接这样写:#include<Contiguous_List.h>的话,是会报错的,原因就在于使用<>符号时,编译器会直接在系统变量路径中查找头文件,例如我们平时书写的程序前面就有#include<stdio.h>标准输入输出的头文件,而Contiguous_List.h文件是我们自定义的头文件,系统变量中是没有的,这时我们就要使用""符号来将文件名包括进去:

#include"Contiguous.h"

这样书写程序就不会报错了,但是我们在引用C本身的头文件时其实也是可以用""符号的,这里就牵扯到了一个先后顺序的问题:

<>符号只用于直接在系统变量路径中查找头文件;

""符号既可以用于查找用户自定义的头文件,也可以用于查找系统变量路径中的头文件,但在查找顺序上,优先进行用户项目路径查找,然后再进行系统变量路径查找。

所以当我们在上方引用用户自定义头文件之后添加#include"stdio.h"也是不会报错的。

声明:

在上文中已经提到,一个完整的顺序表程序应该具有四个功能,这四个功能分别为:

创建(Create)、更新(Update)、读取(Read)、删除(Delete)

简单说起来也就是CURD这四个字母,CURD定义了用于处理数据的基本原子操作,我们在这里创建一个头文件,用于声明函数并在头文件通过函数功能来对上面的操作进行详细划分。

头文件(Contiguous.h)中的函数声明及逻辑说明:

逻辑说明:

前文中说到,顺序表是我们使用一组连续的地址的内存单元来存储整张线性表,所以我们设计一个结构体List并将其重命名为SeqList,并使用指针Plist来指向这个结构体,存储他的地址。List中一共有三个成员分别为element、size、length

element用于指向通过动态内存管理函数在堆区申请的一连串连续的内存;

size用于记录顺序表中有效数据的个数;

length用于记录顺序表的总长度;

在此之前我们还需要设置顺序表的初始长度:

#define INITCAPACITY 100

头文件中的所有函数声明:

#include<cstdio>
#include<cassert>
//结构体设计
typedef struct List
{
    int* element;//动态内存开辟
    int size;//有效数据个数
    int length;//记录总大小(长度)
}SeqList,*PList;
//函数声明
void Init(PList plist);//初始化函数
bool IsFull(PList plist);//判满函数
bool Grow(PList plist);//1.5倍扩容
void AddValue(PList plist,int value);//增加元素
void Show(PList plist);//打印函数
void AddIndex(PList plist,int index,int value);//指定下标添加元素函数【增】
void AddHead(PList plist,int value);//头部添加元素函数函数
void AddTail(PList plist,int value);//尾部添加元素函数函数

void RemoveValue(PList plist,int value);//删除元素
void RemoveIndex(PList pList,int index,int value);//删除指定下标元素函数【删】
void RemoveHead(PList plist);//删除头部元素函数
void RemoveTail(PList plist);//删除尾部元素函数

int SearchValue(PList plist,int value);//在顺序表中查找元素,返回值为下标【查】
void change(PList plist,int oldvalue,int newvalue);//该元素函数【改】

源文件(Contiguous.cpp)中的函数功能具体实现:

初始化函数:

在顺序表程序中,首先我们要做的就是通过向系统申请一段连续的内存空间用于构建顺序表,并将这段内存空间初始化,然后我们将有效数据元素个数size也进行初始化,初始化为0个,在开始的时候我们可以定义一个宏,设定顺序表的初始长度:

#define INITCAPACITY 100//初始数据为100
void init(PList plist)//初始化功能函数
{
    assert(plist != NULL);//安全性判断
    plist->element = (int*)malloc(sizeof(int)*INITCAPACITY);//为结构体中element变量开辟内存空间
    assert(plist->element != NULL);
    plist->size = 0;//初始化有效数据为0个
    plist->length = INITCAPACITY;//长度为初始长度
}

判满函数: 

在顺序表中进行元素添加操作时,我们首先需要确定的是,顺序表中有足够的内存空间能够存放我们要添加的元素,所以我们需要写一个判断顺序表空间是否已经满了的函数,如果顺序表已满,我们就对顺序表的内存空间进行扩容后再进行添加元素操作,如果没有满,我们就直接进行元素添加操作:

//判满操作,通过比较size和length,如果size == length则进行扩容
bool IsFull(PList plist)
{
    assert(plist != NULL);//断言plist指针不为空
    return plist->size == plist->length;//如果顺序表中的有效元素个数等于顺序表的总长度,那么就判定次顺序表已满。
}

扩容函数: 

当进行元素添加操作且顺序表已满的时候,这时就要进行顺序表的扩容操作,我们在之前使用的是malloc函数进行堆区的内存申请,所以此时的扩容操作我们使用realloc函数进行内存空间的扩充,并使用一个新的指针p返回扩容后的内存空间地址,当p指针为空时,直接退出程序。使用p来更新原先的element值,定义len来存放扩容后的顺序表的总长度。

bool Grow(PList plist)//1.5倍扩容
{
    assert(plist != nullptr);
    int len = (int)1.5 * plist->length;
    int *p = (int*)realloc(plist->element,sizeof(int)*len);//对原先开辟的内存空间进行扩容操作
    if(p == nullptr)
        exit(EXIT_FAILURE);
    plist->element = p;//使element指向新开辟的内存
    plist->length = len;
    return true;
}

增加元素函数: 

当顺序表已满且进行完扩容操作之后,这时我们就要进行元素添加操作,同样,如果顺序表内还有足够的内存空间,我们也可以直接进行元素添加操作:

void AddValue(PList plist,int value)//添加元素函数
{
    assert(plist != NULL);
    if(IsFull(plist)){//如果顺序表已满
        Grow(plist);//对顺序表进行扩容
    }
    plist->element[plist->size++] = value;//将元素直接添加值element空间中
}

打印函数: 

利用循环将顺序表中的元素全部输出到屏幕上

void Show(PList plist)
{
    for(int i = 0;i < plist->size;i++){
        printf("%3d",plist->element[i]);
    }
}

删除元素函数:

void RemoveValue(PList plist,int value)//删除元素
{
    for(int i = 0;i < plist->size;i++){
        if(value == plist->element[i]){
            //移动数据覆盖元素
            for(int j = i;j < plist->size - 1;j++){
                plist->element[j] = plist->element[j+1];
            }
            plist->size--;
        }
        else{
            i++;
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ritchie_Zeng

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

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

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

打赏作者

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

抵扣说明:

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

余额充值