C语言实现顺序表

        顺序表是线性表的一种,它是用一段物理地址连续的空间来存储数据的一种线性结构,本质就是数组,顺序表有静态顺序表和动态顺序表,当空间不够时,动态顺序表可以实现增容,因此我们此处要实现的是动态顺序表的增删查改

头文件包含,函数声明

结构体的成员变量是我们要关注的重点, a指向了我们动态内存开辟空间的起始地址,size记录了顺序表中当前有多少个元素,capacity表示顺序表的容量,当元素个数达到容量时,我们便需要扩容,动态顺序表的意义也就在于此。

#define _CRT_SRECURE_NO_WARNINGFS

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLDataType;

#define DEFAULT_SIZE 3

#define INC_SIZE 2

typedef struct SeqList
{
	SLDataType* a;
	int size;
	int capacity;
}SL;

//初始化
void SLInit(SL* pc);

//尾插
void SLPushBack(SL* pc, SLDataType x);

//打印
void SLPrint(SL* pc);

//头插
void SLPushFront(SL* pc, SLDataType x);

//销毁
void SLDestory(SL* pc);

//尾删
void SLPopBack(SL* pc);

//头删
void SLPopFront(SL* pc);

//任意位置插入
void SLInsert(SL* pc,int pos,SLDataType x);

//任意位置删除
void SLErase(SL* pc, int pos);

//查找
int SLFind(SL* pc, SLDataType x);

//修改
void SLModify(SL* pc, int pos, SLDataType x);

顺序表功能的实现

1.顺序表初始化

初始化的时候,我们选择开辟一块空间,默认大小为DEFAULT_SIZE, 然后把开辟空间的首地址放到a变量,容量改成DEFAULT_SIZE;

void SLInit(SL* pc)
{
	pc->size = 0;
	SLDataType* tmp = (SLDataType*)malloc(sizeof(SLDataType) * DEFAULT_SIZE);
	if (tmp == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	pc->a = tmp;
	pc->capacity = DEFAULT_SIZE;
}

2.尾插

由于插入操作会增加顺序表元素,因此判断顺序表是否已满使我们每次要判断的,而且满的话是需要扩容的,因此我们把其封装成函数

······判断顺序表是否为空:

void SLCheckCapacity(SL* pc)
{
	assert(pc);
	if (pc->size == pc->capacity)
	{
		SLDataType* tmp = (SLDataType*)realloc(pc->a, sizeof(SLDataType) * (pc->capacity + INC_SIZE));
		if (tmp == NULL)
		{
			printf("%s", strerror(errno));
			return;
		}
		pc->a = tmp;
		pc->capacity += INC_SIZE;
	}
}

重点关注判断顺序表已满的条件:元素个数 = 容量

这里扩容时,我们每次选择增加两个元素,定义变量为 INC_SIZE

·······尾插:

void SLPushBack(SL* pc, SLDataType x)
{
	assert(pc);
	SLCheckCapacity(pc);
	pc->a[pc->size] = x;
	pc->size++;
}

注意,size是当前顺序表元素个数,也恰好是要尾插元素的下标

3.打印顺序表

void SLPrint(SL* pc)
{
	assert(pc);
	for (int i = 0;i < pc->size;i++)
	{
		printf("%d ", pc->a[i]);
	}
}

4.销毁顺序表


void SLDestory(SL* pc)
{
	assert(pc);
	pc->size = 0;
	pc->capacity = 0;
	free(pc->a);
	pc->a = NULL;
}

5.头插

void SLPushFront(SL* pc, SLDataType x)
{
	assert(pc);
	SLCheckCapacity(pc);
	int end = pc->size - 1;
	while (end >= 0)
	{
		pc->a[end + 1] = pc->a[end];
		end--;
	}
	pc->a[0] = x;
	pc->size++;
}

6.尾删

注意尾删的限制条件,必须有元素才能尾删(size > 0),否则会导致 size 的值 变为负数,产生数组越界行为,可以采用暴力方式进行断言,也可以使用if条件进行判断

void SLPopBack(SL* pc)
{
	assert(pc);
	//法一:
	if (pc->size > 0)
	{
		pc->size--;
	}
	//法二:
	/*assert(pc->size);
	pc->size--;*/
}

7.头删

注意头删条件,和尾删一样,不再赘述

void SLPopFront(SL* pc)
{
	if (pc->size > 0)
	{
		int start = 0;
		while (start < pc->size - 1)
		{
			pc->a[start] = pc->a[start + 1];
			start++;
		}
		pc->size--;
	}
}

8.任意位置插入

void SLInsert(SL* pc, int pos, SLDataType x)
{
	assert(0 <= pos <= pc->size - 1);
	SLCheckCapacity(pc);
	int end = pc->size - 1;
	while (end >= pos)
	{
		pc->a[end + 1] = pc->a[end];
		end--;
	}
	pc->a[pos] = x;
	pc->size++;
}

9.任意位置删除

void SLErase(SL* pc, int pos)
{
	assert(pos>=0 && pos<pc->size);
	//assert(pc->size >= 0); 在这里面这句话没用
	int start = pos+1;
	while (start < pc->size)
	{
		pc->a[start-1] = pc->a[start];
		start++;
	}
	pc->size--;
}

10.查找元素

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

11.修改

void SLModify(SL* pc, int pos, SLDataType x)
{
	assert(pos >= 0 && pos < pc->size);
	pc->a[pos] = x;
}

顺序表功能的测试

#include "SeqList.h"

int main()
{
	//SL s1;

	//SLInit(&s1);

	//SLPushBack(&s1, 1);
	//SLPushBack(&s1, 2);
	//SLPushBack(&s1, 3);
	//SLPushBack(&s1, 4);

	SLPrint(&s1);

	///*printf("\n");*/

	SLPushFront(&s1, 3);
	SLPushFront(&s1, 2);
	SLPushFront(&s1, 1);

	///*SLPrint(&s1);*/



	SLPopBack(&s1);
	SLPopBack(&s1);
	SLPopBack(&s1);
	SLPopBack(&s1);
	SLPopBack(&s1);

	///*SLPushBack(&s1, 5);*/

	//SLPrint(&s1);

	//SLPopFront(&s1);
	//SLPopFront(&s1);
	//SLPopFront(&s1);
	//SLPopFront(&s1);
	//SLPopFront(&s1);

	//printf("\n");

	//SLPrint(&s1);

	//SLPushBack(&s1, 6);

	//SLDestory(&s1);


	SL s2;

	SLInit(&s2);
	SLPushBack(&s2, 1);
	SLPushBack(&s2, 2);
	SLPushBack(&s2, 3);
	SLPushBack(&s2, 4);
	SLPushBack(&s2, 5);
	SLPushBack(&s2, 6);
	
	SLPrint(&s2);
	/*SLErase(&s2, 2);

	printf("\n");

	SLPrint(&s2);


	SLPopBack(&s2);


	printf("\n");

	SLPrint(&s2);

	SLModify(&s2, 2, 10);

	printf("\n");

	SLPrint(&s2);

	int pos = SLFind(&s2, 10);
	
	SLModify(&s2, pos, 20);

	printf("\n");

	SLPrint(&s2);*/
}

顺序表整体来说是比较简单的,关键就是对数组和指针的掌握,同时应该注意一些可能出错的细节,如数组越界,挪动数据时循环的结束条件等等。

顺序表示有它的优点的,但它的缺点也很明显,比如说每次都要申请固定内存的空间,申请小了不够用导致频繁扩容,申请大了空间浪费;还有就是头插时挪动数据消耗很多。

因此我们后面会引出链表这样的数据结构,会弥补这些缺点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值