数据结构(1)——顺序表的实现

本文将通过C语言模拟顺序表,实现基本函数。

顺序表是线性表的顺序表示,即用一组地址连续的存储单元依次存储线性表的数据元素。

物理结构大概如下图所示:
在这里插入图片描述

通过数据表,我们将实现数据的增删查改等功能,每个功能都由单独的接口函数来实现。
按照正规工程的写法,我们还是分成三个文件来写:
Seqlist.h 顺序表结构体的创建、各函数的声明
Seqlist.c 各函数的实现
Test.c 用于测试

一、Seqlist.h
为了能使该顺序表存各种类型的数据,使用typedef重命名。比如要存char类型的,直接将 typedef int DataType; 改为typedef char DataType;即可

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

typedef int DataType;
struct Seqlist
{
	DataType* a;
	int size;//有效数据的个数
	int capacity;//容量  size 一定<=capacity
};

下面是顺序表中需要实现的各个功能函数。
如果在函数中要改变顺序表结构体内部成员的值,就得用传地址

//1.初始化     
void SeqlistInit(struct Seqlist* seq);

//2.销毁
void SeqlistDestroy(struct Seqlist* seq);//销毁

//3.打印
void SeqlistPrint(struct Seqlist* seq);

//4.检查是否超出容量,超出则扩容 
//这不属于接口,是我们程序内部使用的
void SeqlistCheckCapacity(struct Seqlist* seq);

//5.尾插
void SeqlistPushBack(struct Seqlist* seq, DataType x);

//6.头插
void SeqlistPushFront(struct Seqlist* seq, DataType x);

//7.尾删
void SeqlistPopBack(struct Seqlist* seq);

//8.头删
void SeqlistPopFront(struct Seqlist* seq);

//9.查找
int SeqlistFind(struct Seqlist* seq , DataType x);

//10.指定位置插入
void SeqlistInsert(struct Seqlist* seq, DataType x,int pos);

//11.指定位置删除
void SeqlistErease(struct Seqlist* seq, int pos);

//12.修改指定位置数据
void SeqlistModify(struct Seqlist* seq, DataType x, int pos);

接下来,我们在Seqlist.c文件中具体实现各个功能函数:
二、Seqlist.c

  1. 初始化
void SeqlistInit(struct Seqlist* seq)
{
   //通过断言,可以在程序出错时更快地找出错误
	assert(seq != NULL);
	
	seq->a = NULL;
	seq->capacity = 0 ;
	seq->size = 0;
}
  1. 销毁
void SeqlistDestroy(struct Seqlist* seq)
{
	assert(seq != NULL);
	
	//手动释放空间并置空
	free(seq->a);
	seq->a=NULL;
	seq->size = 0;
	seq->capacity = 0;
}
  1. 打印
void SeqlistPrint(struct Seqlist* seq)
{
	assert(seq);
	for (int i = 0; i < seq->size; i++)
	{
		printf("%d\t",seq->a[i]);
	}
	printf("\n");
}

4.检查是否超出容量,超出则扩容
各个插入函数都要用到此功能,为了节省代码量,将此功能独立出来

void SeqlistCheckCapacity(struct Seqlist* seq)
{
	assert(seq != NULL);
	//扩容,这里采用扩2倍的方式
	if (seq->capacity == seq->size)
	{
	    //这里用三目操作符:第一次调用时,seq->capacity是0,如果单纯地乘上2,那永远都是0,无法成功增容
		int capacityNew = (seq->capacity == 0) ? 4 : (seq->capacity * 2);
		//注意realloc的返回值是新开辟空间的地址,所以必须用struct Seqlist*类型的变量去接收
		struct Seqlist* seqNew = realloc(seq->a, sizeof(struct Seqlist) * capacityNew);

		if (seqNew==NULL)
		{
			printf("增容失败");
			exit(-1);
		}

		seq->a = seqNew;
		seq->capacity = capacityNew;
	}
}

5.尾插

void SeqlistPushBack(struct Seqlist* seq, DataType x)
{
	SeqlistCheckCapacity(seq);

	seq->a[seq->size] = x;
	seq->size++;
}

6.头插
头插较为复杂,在插入前需要依次移位

void SeqlistPushFront(struct Seqlist* seq, DataType x)
{
	SeqlistCheckCapacity(seq);

	int end = seq->size - 1;
	while (end >= 0)
	{
		seq->a[end +  1] = seq->a[end];
		end--;
	}
	seq->a[0] = x;
	seq->size++;
}

7.尾删
删除函数在断言seq不为空时,还要断言size>0

void SeqlistPopBack(struct Seqlist* seq)
{
	assert(seq);
	assert(seq->size > 0);

	seq->size--;
}

8.头删

//基本思想也是挪动,但注意要从前往后挪
void SeqlistPopFront(struct Seqlist* seq)
{
	assert(seq);
	assert(seq->size > 0);

	int begin = 0;
	while (begin < (seq->size - 1))
	{
		seq->a[begin] = seq->a[begin + 1];
		begin++;
	}

	seq->size--; 
}

9.查找
返回下标。如果有多个相同的数据,返回第一个数据的下标;无相同数据返回-1

int SeqlistFind(struct Seqlist* seq, DataType x)
{
	assert(seq);

	for (int i = 0; i < seq->size; i++)
	{
		if (seq->a[i] == x)
		{
			return i;
		}
	}

	return -1;
}

10.指定位置插入

void SeqlistInsert(struct Seqlist* seq, DataType x,int pos)
{
	assert(seq);
	assert(pos >= 0 && pos < seq->size);
	
    //检查容量是否已满
	SeqlistCheckCapacity(seq);

	int end = seq->size - 1;
	while (end >= pos)//pos之后的元素都要依次往后移
	{
		seq->a[end + 1] = seq->a[end];
		--end;
	}

	seq->a[pos] = x;
	seq->size++;
}

11.指定位置删除

void SeqlistErease(struct Seqlist* seq, int pos)
{
	assert(seq);
	assert(pos >= 0 && pos < seq->size);

	int begin = 0;
	//从pos开始,后一个移到前一个
	for (begin = pos; begin < seq->size - 1; begin++)
	{
		seq->a[begin] = seq->a[begin + 1];
	}

	seq->size--;
}

12.修改指定位置数据

void SeqlistModify(struct Seqlist* seq, DataType x, int pos)
{
	assert(seq);
	assert(pos >= 0 && pos < seq->size - 1);

	seq->a[pos] = x;
}

读到这里,细心的小伙伴或许已经发现,可以在SeqlistInsert中通过复用实现头插和尾插,在SeqlistErease中通过复用实现头删和尾删:

//改进版尾插
void Improve_SeqlistPushBack(struct Seqlist* seq, DataType x)
{
	SeqlistCheckCapacity(seq);
    SeqlistInsert(seq, x, seq->size);
}
//改进版头插
void Improve_SeqlistPushFront(struct Seqlist* seq, DataType x)
{
	SeqlistCheckCapacity(seq);
    SeqlistInsert(seq, x, 0);
}

尾删头删类似,这里不再赘述。

三、Test.c
我们现在test1中进行基本的接口可行性的测试。

void TestSeqlist1()
{
	struct Seqlist s;
	SeqlistInit(&s);

	printf("尾插: ");
	SeqlistPushBack(&s, 1);
	SeqlistPushBack(&s, 2);
	SeqlistPushBack(&s, 3);
	SeqlistPushBack(&s, 4);
	SeqlistPushBack(&s, 5);
	SeqlistPrint(&s);

	printf("头插: ");
	SeqlistPushFront(&s, -1);
	SeqlistPushFront(&s, -2);
	SeqlistPushFront(&s, -3);
	SeqlistPrint(&s);

	printf("尾删: ");
	SeqlistPopBack(&s);
	SeqlistPopBack(&s);
	SeqlistPrint(&s);

	printf("头删: ");
	SeqlistPopFront(&s);
	SeqlistPopFront(&s);
	SeqlistPrint(&s);

	printf("在位置3插入2: ");
	SeqlistInsert(&s, 3, 2);
	SeqlistPrint(&s);

	printf("删除位置2的数据: ");
	SeqlistErease(&s, 2);
	SeqlistPrint(&s);

	printf("修改位置0的值: "); 
	SeqlistModify(&s, 0, 0);
	SeqlistPrint(&s);

	SeqlistDestroy(&s);
}
int main()
{
    TestSeqlist1();
	return 0;
}

运行结果:
在这里插入图片描述
如果想增加代码的实现效果,可以在Seqlist.c中引入菜单函数:

void menu()
{
	printf("*****************************\n");
	printf("1.尾插数据  2.头插数据\n");
	printf("3.尾删数据  4.头删数据\n");
	printf("5.查找数据  6.打印数据\n");
	printf("       0.退出          \n");
	printf("*****************************\n");
}

然后再main函数中使用switch for语句根据选项对应操作:

int main()
{
	struct Seqlist s;
	SeqlistInit(&s);
	
	int input = 0;
	int data = 0;//用于输入数据
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 0:
     		printf("退出程序\n");
			exit(-1);
		case 1:
			printf("请输入想要尾插的值:\n");
			scanf("%d", &data);
			SeqlistPushBack(&s, data);
			break;
		case 2:
			printf("请输入想要头插的值:\n");
			scanf("%d", &data);
			SeqlistPushFront(&s, data);
			break;
		case 3:
			printf("尾删数据\n");
			SeqlistPopBack(&s);
			break;
		case 4:
			printf("头删数据\n");
			SeqlistPopFront(&s);
			break;
		case 5:
			printf("请选择想要查找的值:\n");
			if (SeqlistFind(&s, data) != -1)
			{
				printf("要找的数在第 %d 个位置\n", SeqlistFind(&s, data));
			}
			else
			{
				printf("找不到\n");
			}
			break;
		case 6:
			printf("打印数据\n");
			SeqlistPrint(&s);
			break;
		default:
			printf("无此选项,请重新输入\n");
			break;
		}
	} while (input != EOF);
	
    SeqlistDestroy(&s);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值