数据结构顺序表

1.线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上(内存中)并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.顺序表
2.1概念及结构
顺序表是用一段 物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。使用顺序表的原因在于,数组有一些缺点。比如c99之前的不支持变长数组。
顺序表是一种储存数据的方式。在语法上允许不连续存放,但顺序表的储存方式要求数据连续存放。
往往会创建两个项目来实现数据结构,一个源文件,一个头文件。使用数据结构时,函数调用即可。
数据结构对细节方面的要求很宽泛,实现方式有很多。
顺序表对储存方式有要求:储存的数据从0(相当于下标,表示位置)开始,依次存储
首先创建SeqList.h和SeqList.c文件
在.h文件中进行结构的定义
#define  N  100
struct  SeqList——sequence顺序
{
    int  a[N];
    int  size; //记录储存了多少数据
};
这里定义的是一个静态的顺序表,不太实用。因为开小了,不够用;开大了,浪费。
动态顺序表的定义:
struct  SeqList
{
    int*  a; //使用动态内存开辟来开辟空间
    int  size; //记录储存了多少数据,即有效数据个数
    int  capacity; //表示存储空间的大小,capacity——容量
};
这样也只能存储int类型的数据,为了存储其他类型的数据,在开头使用typedef重命名:typedef  int  SLDateType,顺序表中int* a;改为SLDateType* a;
其中SL是SeqList的简写DateType是起的名字,为了防止与后面链表的名字冲突,最好加上前缀。
这样储存其他类型数据时,修改被typedef的类型就行了。
数据结构就是你定义出某种结构出来,可以是顺序表,可以是链表来对数据进行管理。
实现数据结构就是实现对数据增,删,查,改的函数
第一步是初始化函数声明:
void SeqListInit(struct SeqList* psl); //可以typedef结构体为SeqList// void SeqListInit(SeqList* psl);
然后涉及动态开辟,使用完后还要销毁,进行销毁函数的声明。
void SeqListDestroy(SeqList* psl);
进行初始化函数定义
void SeqListInit(SeqList* psl)
{
    psl->a = NULL;
    psl->size = 0;
    psl->capacity = 0;
}
还有一种初始化定义方式: //初始化的方式自由度很高
void SeqListInit(SeqList* psl, int capacity) //capacity大小为4
{
    psl->a = (int*)malloc(capacity);
    psl->size = 0;
    psl->capacity = 4;
}
写一定量的代码后一定要进行测试,不要写完后进行测试,错误会多到不想改。
然后进行顺序表尾部的数据插入函数(简称尾插)的声明和书写
声明:
void SeqListPushBack(SeqList* psl, SLDateType x); //尾插
void SeqListPopBack(SeqList* psl); //尾删
void SeqListPushFront(SeqList* psl, SLDateType x); //首插
void SeqListPopFront(SeqList* psl); //首删
书写
void SeqListPushBack(SeqList* psl, SLDateType x); //尾插
{
    if(psl->size == psl->capacity) //如果相同代表有效数据存满了,需要扩容
    {
        size_t  Newcapacity = psl->capacity == 0 : 4 ? psl -> capacity*2;
         //扩容的方式很自由,每次乘2是一个相对合理的方式,视储存的数据而定,选择乘3来扩容,加1来扩容也可以。
        SLDateType* tmp = (int*)realloc(p->a, sizeof(SLDateType)* Newcapacity);
        if(tmp == NULL)
        {
            printf("realloc  fail\n");
            exit(-1); //终止程序
        }
        else
        {
            psl->a = tmp;
            psl -> capacity = Newcapacity;
        }
    }
    psl -> a[psl -> size] = x; //储存数据部分
    psl -> size++; //记录有效数据个数
}
至此首插函数书写完毕,进行函数测试
test.c中
SeqListPushBack(&s , 1);
SeqListPushBack(&s , 2);
SeqListPushBack(&s , 3);
SeqListPushBack(&s , 4);
SeqListPushBack(&s , 5); //最好测试到5,进行一次扩容
SeqListPrint(&s); //写一个顺序表打印函数
在SeqList.h和SeqList.c完成打印函数的书写
声明:void SeqListPrint(SeqList* psl);
书写:
void SeqListPrint(SeqList* psl)
{
    for(int i = 0; i < psl -> szie; i++)
    {
        printf("%d ",psl->a[i]);
    }
    printf("\n");
}
然后写尾删函数:
void SeqListPopBack(SeqList* psl) //尾删
{
    psl - > size--; //直接减少有效数据个数
}
进行测试:
SeqListPopBack(&s);
SeqListPopBack(&s);
SeqListPrint(&s);
发现可以正常运行,但尾删的重点在下面
在test.c中进行多次删除
SeqListPopBack(&s);
SeqListPopBack(&s);
SeqListPopBack(&s);
SeqListPopBack(&s);
SeqListPopBack(&s);
SeqListPopBack(&s);
再录入数据
SeqListPushBack(&s , 10);
SeqListPushBack(&s , 20);
SeqListPrint(&s);
发现问题,此时应该打印出10,20,但什么都没有打印,原因在于尾删进行--时,次数多了size变成负的了
而且这里应该是越界访问的,但没有报错,原因在于越界访问是一种抽查方式,不是一越界就报错的。越界检查方式通常是在数组后面加一些标志符来检查,越界的太多会检查不出来
对尾删进行改进:
void SeqListPopBack(SeqList* psl) //尾删
{
    if(psl -> size > 0)
    {
        psl - > size--; //直接减少有效数据个数
    }
}
完成测试,对程序做一点改进:
如果这样:SeqListPrint(NULL);在程序量特别大时,很容易找不到错误在哪
所以最好对每个传指针函数前加上一个断言
下面介绍首插:
首插时,同样面临一个问题,空间不够会越界,所以一样要检查是否扩容
所以要把上面的扩容部分的代码拷贝过来吗?
最好不要,因为有这样会造成代码的冗余,如果要修改的话,就要修改不止一处。
所以选择用函数实现空间的扩容
声明(SeqList.h):void SeqListCheckCapacity(SeqList* psl);
//声明可以不写的,因为在首插尾插函数上面,但考虑到有时需要在SeqList.h查看大致的结构,最好还是加上
实现(SeqList.c):
void SeqListCheckCapacity(SeqList* psl)
{
    if (psl->size == psl->capacity) //如果相同代表有效数据存满了,需要扩容
    {
        size_t  Newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
        SLDateType* tmp = (int*)realloc(psl->a, sizeof(SLDateType) * Newcapacity);
        if (tmp == NULL)
        {
            printf("realloc  fail\n");
            exit(-1); //终止程序,-1是返回码,0代表正常返回,其他都是异常结束
        }
        else
        {
            psl->a = tmp;
            psl->capacity = Newcapacity;
        }
    }
}
然后在需要检查的部分写上:SeqListCheckCapacity(psl);
在test.c中要测试的会不断增多,同样用函数来解决main中代码过多的问题
一个小技巧:写代码命名最好和情况符合
void SeqListPushFront(SeqList* psl, SLDateType x) //首插
{
    assert(psl);
    SeqListCheckCapacity(psl); //检查容量
    int end = size; //从末尾开始移值
    while(end)
    {
        psl->a[end] = psl->a[end-1];
    }
    psl->a[end] = x;
    psl->size++;
}
接着测试:
测试结束后解决首删。
void SeqListPopFront(SeqList* psl) //首删
{
    assert(psl);
    int begin = 0;
    for(begin = 0; begin < psl->size - 1 ; begin++)
    {
        psl->a[begin] = psl->a[begin+1];
    }
    if(psl -> size > 0)
    {
        psl->size--;
    }
}
下一个,在任意位置插入删除数据
void SeqListInsert(SeqList* psl, size_t pos, SLDateType x)
{
    assert(psl);
    assert(pos >= 0 && pos <= psl->size);
    SeqListCheckCapacity(psl);
    size_t end = psl->size;
    while (end > pos)
    {
        psl->a[end] = psl->a[end - 1];
        end--;
    }
    psl->a[pos] = x;
    psl->size++;
}
这是第一次写出来的,有什么问题吗?
大问题没有,细节问题有不少,首先pos是size_t类型,不需要说明pos>=0,其次psl->size是int类型,与pos类型不匹配,虽然也能得到结果。大部分人第一次写不会考虑pos的范围问题,如果不加断言,pos随便传一个值,就会造成数组越界,断言是相当粗暴的检查方式,大多用于绝对不能出现的情况,可以考虑使用if来较温和的判断,不过每个人有每个人检查的方式,所以具体看个人。堆区的报错,往往是在free的时候,所以内存忘记释放,还可能导致不报错,在free时报错时都是数组越界。当取尾值部分这样写时size_t end = psl->size - 1;while (end >= pos)时,这两行代码会有问题,end是无符号,size是0时,end为-1转为无符号类型是42亿多(在传第一个值时size是0,end =-1还是会变成42亿多),改end为int类型,在和pos比较时,还是会转换为无符号类型,而且pos是下标,在大多数函数中下标都是以size_t类型传的,非要pos改为用int类型传,在if判断范围时还要加上pos小于0的情况。还可以将while中的pos强转为int类型,最好的还是目前的代码,end>pos不用考虑-1的情况。
void SeqListInsert(SeqList* psl, size_t pos, SLDateType x)
{
    assert(psl);
    if(pos > psl->size)
    {
        printf("pos越界\n");
        return;
    }
    SeqListCheckCapacity(psl);
    size_t end = psl->size;
    while (end > pos)
    {
        psl->a[end] = psl->a[end - 1];
        end--;
    }
    psl->a[pos] = x;
    psl->size++;
}
任意位置删除:
void SeqListErase(SeqList* psl, size_t pos)
{
    assert(psl);
    assert(pos < (size_t)psl->size); //不能等于,等于的位置没有数据
    size_t begin = pos + 1;
    while (begin < (size_t)psl->size)
    {
        psl->a[begin - 1] = psl->a[begin];
        begin++;
    }
    psl->size--;
}
完毕
本章节只是对顺序表的学习,用顺序表时c++有现成的。
test.c中
#include"SeqList.h"
void SeqListTest1()
{
    SeqList  s; //虽然可以直接初始化,但规范是通过增删查该的函数进行数据的修改
    SeqListInit(&s);
    SeqListPushBack(&s , 1);
    SeqListPushBack(&s , 2);
    SeqListPushBack(&s , 3);
    SeqListPushBack(&s , 4);
    SeqListPushBack(&s , 5); //最好测试到5,进行一次扩容情况的测试
    SeqListPrint(&s);
    SeqListPopBack(&s);
    SeqListPopBack(&s);
    SeqListPrint(&s);
    SeqListPopBack(&s);
    SeqListPopBack(&s);
    SeqListPopBack(&s);
    SeqListPopBack(&s);
    SeqListPopBack(&s);
    SeqListPopBack(&s);
    SeqListPushBack(&s , 10);
    SeqListPushBack(&s , 20);
    SeqListPrint(&s);
}
void SeqListTest2()
{
    SeqList s;
    SeqListInst(&s);
    SeqListPushBack(&s, 10);
    SeqListPushBack(&s, 20);
    SeqListPushBack(&s, 30);
    SeqListPushBack(&s, 40);
    SeqListPushFront(&s,11);
    SeqListPrint(&s);
}
int main()
{
    SeqListTest2();
    return 0;
}
SeqList.h中
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef  int  SLDateType;
typedef struct  SeqList
{
    SLDateType*  a; //使用动态内存开辟来开辟空间
    int  size; //记录储存了多少数据,即有效数据个数
    int  capacity; //表示存储空间的大小,capacity——容量
}SeqList;
void SeqListInit(SeqList* psl);
void SeqListDestroy(SeqList* psl);
SeqListCheckCapacity(SeqList* psl); //检查容量
void SeqListPushBack(SeqList* psl, SLDateType x); //尾插
void SeqListPopBack(SeqList* psl); //尾删
void SeqListPushFront(SeqList* psl, SLDateType x); //首插
void SeqListPopFront(SeqList* psl); //首删
void SeqListPrint(SeqList* psl); //顺序表打印
void SeqListInsert(SeqList* psl, size_t pos, SLDateType x);
void SeqListErase(SeqList* psl, size_t pos);
SeqList.c中
#include"SeqList.h"
void SeqListInit(SeqList* psl)
{
    assert(psl);
    psl->a = NULL;
    psl->size = 0;
    psl->capacity = 0;
}
void SeqListDestroy(SeqList* psl) //释放realloc开辟的空间
{
    assert(psl);
    free(psl->a);
    psl->a = NULL;
    psl->capacity = psl->size = 0;
}
void SeqListPrint(SeqList* psl) //顺序表打印
{
    assert(psl);
    for (int i = 0; i < psl->size; i++)
    {
        printf("%d ", psl->a[i]);
    }
    printf("\n");
}
void SeqListCheckCapacity(SeqList* psl)
{
    assert(psl);
    if (psl->size == psl->capacity) //如果相同代表有效数据存满了,需要扩容
    {
        size_t  Newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
        SLDateType* tmp = (int*)realloc(psl->a, sizeof(SLDateType) * Newcapacity);
        if (tmp == NULL)
        {
            printf("realloc  fail\n");
            exit(-1); //终止程序
        }
        else
        {
            psl->a = tmp;
            psl->capacity = Newcapacity;
        }
    }
}
void SeqListPushBack(SeqList* psl, SLDateType x) //尾插
{
    assert(psl);
    SeqListCheckCapacity(psl); //检查容量
    psl->a[psl->size] = x; //储存数据部分
    psl->size++; //记录有效数据个数
}
void SeqListPopBack(SeqList* psl) //尾删
{
    assert(psl);
    //有人会选择赋0来表达删除的意思,但这样有问题,比如如果存储的数据就是0,或者存储的数据类型是double
    if (psl->size > 0)
    {
        psl -> size--; //直接减少有效数据个数
    }
}
void SeqListPushFront(SeqList* psl, SLDateType x) //首插
{
    assert(psl);
    SeqListCheckCapacity(psl); //检查容量
    int end = psl->size;
    while(end)
    {
        psl->a[end] = psl->a[end-1];
        end--;
    }
    psl->a[end] = x;
    psl->size++;
}
void SeqListPopFront(SeqList* psl) //首删
{
    assert(psl);
    int begin = 0;
    for(begin = 0; begin < psl->size - 1 ; begin++)
    {
        psl->a[begin] = psl->a[begin+1];
    }
    if(psl -> size > 0)
    {
        psl->size--;
    }
}
void SeqListInsert(SeqList* psl, size_t pos, SLDateType x) //在pos位置的插入x
{
    assert(psl);
    if(pos > psl->size)
    {
        printf("pos越界\n");
        return;
    }
    SeqListCheckCapacity(psl);
    size_t end = psl->size;
    while (end > pos)
    {
        psl->a[end] = psl->a[end - 1];
        end--;
    }
    psl->a[pos] = x;
    psl->size++;
}
void SeqListErase(SeqList* psl, size_t pos)
{
    assert(psl);
    assert(pos < (size_t)psl->size); //不能等于,等于的位置没有数据
    size_t begin = pos + 1;
    while (begin < (size_t)psl->size)
    {
        psl->a[begin - 1] = psl->a[begin];
        begin++;
    }
    psl->size--;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值