动态顺序表的实现

动态顺序表的实现

实现动态顺序表的代码多种多样,不过大体思路一致,我们提供一个思路,重在理解,才会做题,才能看懂其他的形式。

  • 我们将代码分为三个文件实现,分别为:

SeqList.h: 包含所有需要的头文件,定义以及接口函数的声明。

SeqList: 保存所有接口函数的定义和实现

test.c: 主函数,一般为打印菜单,调用接口函数,需求多样,我们仅实现头文件和接口函数。

头文件

由于我们要实现的是动态顺序表,所以存储数据的位置是我们开辟的动态内存空间。我们先编辑SeqList.h

//SeqList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SQDateType;//未来如果想在顺序表中存储其他类型的数据,仅需要修改int为想存储的类型
typedef struct SeqList
{
	SQDateType* a;//指向存储数据的内存空间
	int size;//顺序表中的有效数据的个数
	int capacity;//顺序表的容量,也就是最大存储数据数
}SL;

接口函数实现

完成以上准备,我们可以开始准备我们的接口函数了,实现对顺序表的增删查改等操作。

  • 首先,对我们的顺序表进行初始化:

    void SeqListInit(SL* ps)
    {
        ps->a = NULL;
        ps->size = 0;
        ps->capacity = 0;
    }
    
  • 实现一个打印顺序表的函数

    void SeqListPrint(SL* ps)
    {
        for(int i = 0; i < ps->size; i++)
        {
            printf("%d ",ps->a[i]);//注意,我们在修改存储数据的类型时,同时需要修改这里的占位符
        }
        printf("\n");
    }
    
  • 我们继续分析,每次在顺序表中插入数据,我们都需要判断容量是否占满,如果占满,我们需要对我们的内存空间进行扩容。

    考虑到频繁实现这样的操作,我们专门将这一操作分装成一个独立的函数,每次插入数据时,调用这个函数即可,省去了重复书写的麻烦。

    void SeqListCheckCapacity(SL* ps)
    {
        if(ps->size == ps->capacity)//只有空间满,才需要扩容
        {
            int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
            //确定新容量,一般扩容为原来的两倍,别忘了考虑容量为0时的情况(证明没有开辟过空间),这种情况下肯定是没有办法存储数据的,同时扩容后的效果为:0 * 2 = 0,相当于没有扩容。所以我们先判断容量是否为0,为0,那么我们给容量赋一个合理的数值,作为新容量(这里我们赋值4);否则,我们将容量扩大一倍,这种逻辑刚好可以用三目操作符实现。
            SQDateType* tmp = (SQDateType*)realloc(ps->a, newcapacity * sizeof(SQDateType));
            //我们创建一个新的指针变量来接收realloc的返回值,避免了开辟失败后返回NULL覆盖原有地址的情况。
            if(tmp == NULL)//判断如果为NULL,则打印错误信息并退出函数。
            {
                printf("realloc fail!\n");
                exit(-1);
            }
            else//不为空,证明开辟(扩容)成功。
            {
                ps->capacity = newcapacity;
                ps->a = tmp;
            }
        }
    }
    
  • 好了,我们可以尝试实现一个尾插的接口函数了。

    void SeqListPushBack(SL* ps, SQDateType x)
    {
        SeqListCheckCapacity(ps);//尾插需要插入新数据,调用SeqListCheckCapacity函数
        ps->a[ps->size] = x;
        ps->size++;//插入后及时更新有效数据的个数
    }
    
  • 头插函数

    void SeqListPushFront(SL* ps, SQDateType x)
    {
        //思路:将所有元素向后移动一位,再将新元素插入到表首
        SeqListCheckCapacity(ps);
        for(int i = ps->size; i >= 1; i--)
        {
            ps->a[i] = ps->[i-1];
        }
        ps->a[0] = x;
        ps->size++;
    }
    
  • 尾删函数

    void SeqListPopBack(SL* ps)
    {
        ps->a[ps->size-1] = 0;//这句语句可以省略,因为我们打印顺序表时是以size为基准的,size--再打印不会显示最后一个元素,而如果再在那个位置插入时就直接覆盖掉了
        ps->size--;
    }
    
  • 头删函数

    void SeqListPopFront(SL* ps)
    {
        //思路:从第二个数据开始依次向左移动一位,将头数据覆盖
        for(int i = 0; i < ps->size; i++)
        {
            ps->a[i] = ps->a[i+1];
        }
        ps->size--;
    }
    
  • 插入函数

    void SeqListInsert(SL* ps, int pos, SQDateType x)
    {
        //这里的pos以及后面出现的pos不是下标,而是位置,例如下标为0的元素的位置为1
        //思路:将pos位置(即下标为pos-1的元素的位置)及之后的所有元素向后移动一位,再将新元素放在指定的位置。
        assert(pos < ps->size);//如果插入位置参数输入非法,则报错
        SeqListCheckCapacity(ps);
        for(int i = ps->size; i > pos - 1; i--)
        {
            ps->a[i] = ps->a[i-1];
        }
        ps->a[pos-1] = x;
        ps->size++;
    }
    
  • 删除函数

    void SeqListErase(SL* ps, int pos)
    {
        //思路:将pos位置后面的所有元素向前移动一位,覆盖掉pos位置的元素
        assert(pos < ps->size);
        for(int i = pos - 1; i < ps->size; i++)
        {
            ps->a[i] = ps->a[i+1];
        }
        ps->size--;
    }
    
  • 查找函数

    int SeqListFind(SL* ps, SQDateType x)
    {
        //思路:遍历即可
        for(int i = 0; i < ps->size; i++)
        {
            if(ps->a[i] == x)
            {
                return i;
            }
        }
        return -1;
    }
    
  • 修改函数

    void SeqListModity(SL* ps, int pos, SQDateType x)
    {
        //思路:找到指定位置后直接修改即可
        assert(pos < ps->size);
        ps->a[pos-1] = x;
    }
    
  • 释放函数

    void SeqListDestory(SL* ps)
    {
        free(ps->a);
        ps->a = NULL;
        ps->size = 0;
        ps->capacity = 0;
    }
    

至此我们完成了我们的接口函数定义实现部分,接口函数当然不止这些,不过这里也涵盖了最常见的接口函数,理解了这些,其他接口函数的实现也就如鱼得水。

完整代码

//SeqList.h

#pragma once
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <assert.h>
typedef int SQDateType;
typedef struct SeqList
{
	SQDateType* a;
	int size;
	int capacity;
}SL;


void SeqListInit(SL* ps);
void SeqListPrint(SL* ps);
void SeqListCheckCapacity(SL* ps);

void SeqListPushBack(SL* ps, SQDateType x);
void SeqListPushFront(SL* ps, SQDateType x);
void SeqListPopBack(SL* ps);
void SeqListPopFront(SL* ps);
void SeqListInsert(SL* ps, int pos, SQDateType x);
void SeqListErase(SL* ps, int pos);
void SeqListModity(SL* ps, int pos, SQDateType x);
int SeqListFind(SL* ps, SQDateType x);
void SeqListDestory(SL* ps);

//SeqList.c

#include "SeqList.h"
void SeqListInit(SL* ps)
{
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}


void SeqListPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}


void SeqListCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SQDateType* tmp = (SQDateType*)realloc(ps->a, newcapacity * sizeof(SQDateType));
		if (tmp == NULL)
		{
			printf("%s\n", strerror(errno));
			exit(-1);
		}
		else
		{
			ps->capacity = newcapacity;
			ps->a = tmp;
		}
	}
}


void SeqListPushBack(SL* ps, SQDateType x)
{
	SeqListCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}


void SeqListPushFront(SL* ps, SQDateType x)
{
	SeqListCheckCapacity(ps);
	for (int i = ps->size; i >= 1; i--)
	{
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[0] = x;
	ps->size++;
}


void SeqListPopBack(SL* ps)
{
	ps->a[ps->size - 1] = 0;
	ps->size--;
}


void SeqListPopFront(SL* ps)
{
	for (int i = 0; i < ps->size ; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}


void SeqListInsert(SL* ps, int pos, SQDateType x)
{
	assert(pos < ps->size);
	SeqListCheckCapacity(ps);
	for (int i = ps->size; i > pos - 1; i--)
	{
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[pos - 1] = x;
	ps->size++;
}


void SeqListErase(SL* ps, int pos)
{
	assert(pos < ps->size);
	for (int i = pos - 1; i < ps->size; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}


int SeqListFind(SL* ps, SQDateType x)
{
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}


void SeqListModity(SL* ps, int pos, SQDateType x)
{
	assert(pos < ps->size);
	ps->a[pos - 1] = x;
}


void SeqListDestory(SL* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}
  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值