动态顺序表详解(结尾附全代码)

前言

顺序表分为动态与静态,动态常用(可以改变数组大小,更合理的利用空间),故此处详解动态顺序表

动态顺序表函数

动态顺序表分不开三个定义

1数组元素

2有效个数

3最大容纳个数

 

创建文件并做好开始准备代码

创建文件

注意Seqlist.h是顺序表结构,声明顺序表的方法

        Seqlist.c是实现顺序表的方法

        test.c是测试

开始准备代码,在头文件写入

#pragma once
#include <stdio.h>
#include <stdlib.h>
typedef int SLDateType; //此处进行重命名,是为了以后要进行更换数据类型便于更换
typedef struct SeqList
{
	SLDateType* arr;
	int size;
	int capacity;//空间大小
}SL;//此处再次进行重命名,是为了后续方便后续

 

 

顺序表的初始化


必须使用指针传参,若使用传值,则会出现未初始化

  1. 在test.c的代码  后续在test.c加上代码只需在SLTest01加上函数名(&s1)
  2. #include "Seqlist.h"
    void SLTest01()
    {
    	SL s1;
    	SLInit(&s1);//后续在test.c加上代码只需在SLTest01加上函数名(&s1)
    }
    int main()
    {
    	SLTest01();
    	return 0;
    }
  1. 在Seqlist.c的代码 
  2. #include "Seqlist.h"
    void SLInit(SL*ps)
    {
    	ps->arr = NULL;
    	ps->size = ps->capacity = 0;
    }

     

  1. 在Seqlist.h的代码 
  2. #pragma once
    #include <stdio.h>
    #include <stdlib.h>
    typedef int SLDateType; //此处进行重命名,是为了以后要进行更换数据类型便于更换
    typedef struct SeqList
    {
    	SLDateType* arr;
    	int size;
    	int capacity;//空间大小
    }SL;//此处再次进行重命名,是为了后续方便后续
    //顺序表初始化
    void SLInit(SL* ps);

     

顺序表的销毁

此处简单,先讲

 

Seqlist.h的代码

//本来应是顺序表的插入,但销毁简单,所以先讲
void SLDestory(SL* ps);

Seqlist.c的代码

(free只能释放动态开辟的空间,等下在后面开辟空间会讲解,不要认为此处错了)

void SLDestory(SL* ps)
{
	if (ps->arr != NULL)
	{
		free(ps->arr);
	}
	ps->arr = NULL;

}

 

test.c

void SLTest01()
{
	SL s1;
	SLInit(&s1);//后续在test.c加上代码只需在SLTest01加上函数名(&s1)
	SLDestory(&s1);
}

后续Seqlist.h与Seqlist.c的代码加入形式不会与上面加入差距太大,都是函数名(接受的值)

故下面没有特殊情况(或编译代码时)不进行展示

顺序表的插入

共有三种插入方式

从尾部插入

即再数组的尾部插入一个数

直接给出结论,计算机扩大空间一般是2倍/3倍,此处以扩2倍为例

在顺序表插入之前,要先判断空间是否足够

如果不足,要开辟空间,这才能够进行插入,又因为不确定数据的大小,所以是动态开辟看,且使用realloc函数进行开辟

 


 

Seqlist.h的代码

//顺序表的尾插
void SLPushBack(SL* ps,SLDatetype x);

test.c的代码

表示尾插一个数字为4

//尾插
SLPushBack(&s1,4);

Seqlist.c的代码

 

判断空间是否足够和空间的开辟

void SLPushBack(SL* ps,SLDateType x)
{
	assert(ps);//先进行断言,防止NULL为空指针
	if (ps->size = ps->capacity)
	{
		int newcapacity = ps->capacity == NULL ? 4 : 2 * sizeof(ps->capacity);
		SLDateType* temp = (SLDateType*)realloc(ps->arr, 2 * newcapacity);
		if (temp == NULL)
		{
			perror("realloc");
			exit(1);
		}
	//空间申请成功
	ps->capacity = newcapacity;
	ps->arr = temp;
	}
}

当后面的两种尾插还要再写一次,过于繁琐,不如再创建一个判断空间是否足够和开辟空间的函数

此后,只需调用函数即可

void SLApply(SL* ps)
{
	if(ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == NULL ? 4 : 2 * sizeof(ps->capacity);
		SLDateType* temp = (SLDateType*)realloc(ps->arr, sizeof(SLDateType) * newcapacity);
		if (temp == NULL)
		{
			perror("realloc");
			exit(1);
		}
		//空间申请成功
		ps->capacity = newcapacity;
		ps->arr = temp;
	}
}
void SLPushBack(SL* ps,SLDateType x)
{
	assert(ps);//先进行断言,防止NULL为空指针
	SLApply(ps);
}

最后再进行插入

注意,插入一个数据,有效数据size也要++

void SLPushBack(SL* ps,SLDateType x)
{
	assert(ps);//先进行断言,防止NULL为空指针
	SLApply(ps);
	ps->arr[ps->size++] = x;//注意,插入了一个数据,有效数组扩大一位,size要++
}

顺序表的打印

如果只是一次的插入,那我们可以清晰的了解过程

但若是以下情况甚至更多呢?

 

  1. include "Seqlist.h"
    void SLTest01()
    {
    	SL s1;
    	SLInit(&s1);//后续在test.c加上代码只需在SLTest01加上函数名(&s1)
    	//尾插
    	SLPushBack(&s1,1);
    	SLPushBack(&s1,2);
    	SLPushBack(&s1,3);
    	SLPushBack(&s1,4);
    	SLPushBack(&s1,5);
    	SLDestory(&s1,6);
    }
    int main()
    {
    	SLTest01();
    	return 0;
    }
  2. 此时有两种解决方法

        1是Sleep函数

        2是每插入都打印顺序表

此处采用方法2

        则在test.c加入

此处使用的是顺序表的原始数据,所以传值即可

//尾插
SLPushBack(&s1,1);
SLprint(s1);
SLPushBack(&s1,2);
SLprint(s1);
SLPushBack(&s1,3);
SLprint(s1);
SLPushBack(&s1,4);
SLprint(s1);
SLPushBack(&s1,5);
SLprint(s1);
SLDestory(&s1,6);
SLprint(s1);

在Seqlist.c加入

void SLprint(SL ps)
{
	for (int i = 0; i < ps.size; i++)
		printf("%d ", ps.arr[i]);
    printf("\n");
}

 

接下来让我们运行代码,看看效果

结果如下,正确

从头部插入

从头部插入,经过画图知,即arr里的数据,全都要往后移一位,且是从最后一个数据开始移

实现方法有两种

  1. for循环
  2. 使用memmove函数

此处使用方法二

Seqlist.h的代码

//顺序表的头插
void SLPushFront(SL* ps, SLDateType x);

test.c的代码

SLPushFront(&s1, 6);
SLprint(s1);
SLPushFront(&s1, 7);
SLprint(s1);

 

seqlist.c的代码

void SLPushFront(SL* ps, SLDateType x)
{
	assert(ps);
	SLApply(ps);
	memmove(ps->arr + 1, ps->arr, sizeof(SLDateType) * ps->size);
	ps->arr[0] = x;//再进行插入
	++ps->size;//此时size依旧要++
}	

 

最后结果,预取一样

从指定位置插入

指定位置插入,即比前面多了一个插入位置的下标,再分析其实就是将尾插的插入与头插的挪位置结合起来

同理:挪位置与头插一样有两种方式

此处依然使用memmove函数


Seqlist.h的代码

int num表示插入位置的下标

 

//顺序表的指定位置插入
void SLPushNoknow(SL* ps,int num,SLDateType x);

test.c的代码

SLPushNoknow(&s1, 4, 3);
SLprint(s1);
SLPushNoknow(&s1, 1, 1);
SLprint(s1);

Seqlist.c的代码

void SLPushNoknow(SL* ps, int num, SLDateType x)
{
	assert(ps);
	assert( num>=0 && num <= ps->size);//此处需判断插入位置是否在有效数字之间,此处num = size也可直接开辟空间
	SLApply(ps);
	memmove(ps->arr + num + 1, ps->arr + num, sizeof(SLDateType) * (ps->size - num ));
	ps->arr[num] = x;
	++ps->size;
}

插入全部完成,接下来进入删除


从尾部删除

本质就是有效数字size减少一个,无法读取即可

Seqlist.h的代码

//顺序表的尾删
void SLCleanBack(SL* ps);

test.c的代码

//顺序表的尾删
void SLCleanBack(SL* ps);

Seqlist.c

void SLCleanBack(SL* ps)
{
	assert(ps);
	assert(ps->size);//加入判断有效数字是否为0,不为0才可删
	//尾删其实就是有效数字少一个即可
	ps->size--;
}

从头部删除

头部删除与尾部删除大致一样,只是头部删除多了一个挪动数据的步骤

同样:此处依然可以采用for循环和memmove来挪动元素,

此处依旧采用memmove


SLeqlist.h

//顺序表的头删
void SLCleanFront(SL* ps);

test.c

SLCleanFront(&s1);
SLprint(s1);
SLCleanFront(&s1);
SLprint(s1);

Seqlist.c

void SLCleanFront(SL* ps)
{
	assert(ps);
	assert(ps->size);
	memmove(ps->arr, ps->arr + 1, sizeof(SLDateType) * ps->size - 1);
	ps->size--;
}

从指定位置删除

与前面添加一样,指定位置删除,其实也是结合了头删和尾删,一样需要判断输入的下标是否有效


Seqlist.h

//顺序表的指定删除
void SLCleanNoknow(SL* ps,int n);

test.c

//指定位置删除
SLCleanNoknow(&s1, 1);
SLprint(s1);
SLCleanNoknow(&s1, 0);
SLprint(s1);

Seqlist.c

void SLCleanNoknow(SL* ps, int n)
{
	assert(ps);
	assert(ps->size);
	assert(n >= 0 && n < ps -> size);
	memmove(ps->arr + n, ps->arr + n + 1, sizeof(SLDateType) * (ps->size - n - 1));
	ps->size--;
}

顺序表查找数据

在顺序表中查找一个数字,成功则返回该数字的下标,没有则返回:找不到


Seqlist.h

//顺序表的查找
int SLFind(SL* ps, int n);

test.c

//查找数据
int find = SLFind(&s1, 10);
if (find < 0)
	printf("找不到\n");
else
	printf("找到了,下标为%d\n", find);

Seqlist.c

/如果没有,返回值应该小于0,因为下标大于等于0

 
int SLFind(SL* ps, int n)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == n)
			return i;
	}
	return -1;//如果没有,返回值应该小于0,因为下标大于等于0
}

最后附上全部代码

Seqlist.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
typedef int SLDateType; //此处进行重命名,是为了以后要进行更换数据类型便于更换
typedef struct SeqList
{
	SLDateType* arr;
	int size;
	int capacity;//空间大小
}SL;//此处再次进行重命名,是为了后续方便后续
//顺序表初始化
void SLInit(SL* ps);
//本来应是顺序表的插入,但销毁简单,所以先讲
void SLDestory(SL* ps);
//顺序表的尾插
void SLPushBack(SL* ps, SLDateType x);
//顺序表的头插
void SLPushFront(SL* ps, SLDateType x);
//顺序表的指定位置插入
void SLPushNoknow(SL* ps,int num,SLDateType x);
//顺序表的头删
void SLCleanFront(SL* ps);
//顺序表的尾删
void SLCleanBack(SL* ps);
//顺序表的指定删除
void SLCleanNoknow(SL* ps,int n);
//顺序表的查找
int SLFind(SL* ps, int n);

 

test.c

#include "Seqlist.h"
void SLTest01()
{
	//创建数据表
	SL s1;
	SLInit(&s1);//后续在test.c加上代码只需在SLTest01加上函数名(&s1)
	//尾插
	SLPushBack(&s1, 1);
	SLprint(s1);
	SLPushBack(&s1, 2);
	SLprint(s1);
	SLPushBack(&s1, 3);
	SLprint(s1);
	SLPushBack(&s1, 4);
	SLprint(s1);
	SLPushBack(&s1, 5);
	SLprint(s1);
	//头插
	SLPushFront(&s1, 6);
	SLprint(s1);
	SLPushFront(&s1, 7);
	SLprint(s1);
	SLPushNoknow(&s1, 3, 3);
	SLprint(s1);
	SLPushNoknow(&s1, 1, 1);
	//尾删
	SLCleanBack(&s1);
	SLprint(s1);
	SLCleanBack(&s1);
	SLprint(s1);
	//头删
	SLCleanFront(&s1);
	SLprint(s1);
	SLCleanFront(&s1);
	SLprint(s1);
	//指定位置删除
	SLCleanNoknow(&s1, 1);
	SLprint(s1);
	SLCleanNoknow(&s1, 0);
	SLprint(s1);
	//查找数据
	int find = SLFind(&s1, 10);
	if (find < 0)
		printf("找不到\n");
	else
		printf("找到了,下标为%d\n", find);
	//顺序表销毁
	SLDestory(&s1);
}
int main()
{
	SLTest01();
	return 0;
}

Seqlist.c

#include "Seqlist.h"
void SLInit(SL*ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
void SLDestory(SL* ps)
{
	if (ps->arr != NULL)
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
void SLprint(SL ps)
{
	for (int i = 0; i < ps.size; i++)
		printf("%d ", ps.arr[i]);
	printf("\n");
}
void SLApply(SL* ps)
{
	if(ps->size == ps->capacity)
	{
		int newcapacity = (ps->capacity == NULL ? 4 : 2 * ps->capacity);
		SLDateType* temp = (SLDateType*)realloc(ps->arr, sizeof(SLDateType) * newcapacity);
		if (temp == NULL)
		{
			perror("realloc");
			exit(1);
		}
		ps->capacity = newcapacity;
		ps->arr = temp;
	}
}
void SLPushBack(SL* ps,SLDateType x)
{
	
	assert(ps);//先进行断言,防止ps为空指针
	SLApply(ps);
	ps->arr[ps->size++] = x;//注意,插入了一个数据,有效数字扩大一位,size要++
}
void SLPushFront(SL* ps, SLDateType x)
{
	assert(ps);
	SLApply(ps);
	memmove(ps->arr + 1, ps->arr, sizeof(SLDateType) * ps->size);
	ps->arr[0] = x;//再进行插入
	++ps->size;//此时size依旧要++
}	
void SLPushNoknow(SL* ps, int num, SLDateType x)
{
	assert(ps);
	assert( num>=0 && num <= ps->size);//此处需判断插入位置是否在有效数字之间,此处num = size也可直接开辟空间
	SLApply(ps);
	memmove(ps->arr + num + 1, ps->arr + num, sizeof(SLDateType) * (ps->size - num ));
	ps->arr[num] = x;
	++ps->size;
}
void SLCleanBack(SL* ps)
{
	assert(ps);
	assert(ps->size);//加入判断有效数字是否为0,不为0才可删
	//尾删其实就是有效数字少一个即可
	ps->size--;
}
void SLCleanFront(SL* ps)
{
	assert(ps);
	assert(ps->size);
	memmove(ps->arr, ps->arr + 1, sizeof(SLDateType) * ps->size - 1);
	ps->size--;
}
void SLCleanNoknow(SL* ps, int n)
{
	assert(ps);
	assert(ps->size);
	assert(n >= 0 && n < ps -> size);
	memmove(ps->arr + n, ps->arr + n + 1, sizeof(SLDateType) * (ps->size - n - 1));
	ps->size--;
}
int SLFind(SL* ps, int n)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == n)
			return i;
	}
	return -1;//如果没有,返回值应该小于0,因为下标大于等于0
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值