顺序表功能完整实现(附完整代码)

发布时间:2024年01月10日


1、线性表

定义

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。

简单来说,线性表就是在逻辑上呈一条直线的形式,用于存放n个具有相同特性的数据的结构。

2、顺序表

1.概念及结构

顺序表(sequence list)是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可以分为: 1. 静态顺序表:使用定长数组存储元素。 2.动态顺序表:使用动态开辟的数组进行存储,在需要时可以进行扩容。

1)静态顺序表

#define MAXSIZE 7 //数组最大长度
typedef int SeqListType; //定义顺序表数据类型,容易做修改

typedef struct SeqList
{
	SeqListType data[MAXSIZE];//定长数组
	int size;	 //有效数据个数
};

2)动态顺序表

typedef int SeqListType; //定义顺序表数据类型,容易做修改

typedef struct SeqList
{
	SeqListType* data;//指向动态开辟的数组
	int size;	 //有效数据个数
	int capacity;//容量
}SL;

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空 间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。

2.动态顺序表实现

动态顺序表要求:

a.插入数据,空间不够了要增容

b.要求数据是依次存储的

缺陷:

a.空间不够进行增容;增容会付出一定的性能消耗,其次可能存在一定的空间浪费,

b.在头部或者中部左右的插入效率低,时间复杂度 o(N)

解决方法:

a.空间上,按需给空间

b.不要求物理空间上的连续,就不需要移动数据

具体实现:

1)结构定义:动态顺序表的结构中由三部分组成:其一是一个指向动态开辟数组的指针;其二是有效数据的个数,便于对数组中的元素进行定位;其三是容量,便于在数组空间进行判定在不够时进行扩容。

typedef int SeqListType; //定义顺序表数据类型,容易做修改

typedef struct SeqList
{
	SeqListType* data;//指向动态开辟的数组
	int size;	 //有效数据个数
	int capacity;//容量
}SL;

2)初始化:动态顺序表的初始化主要就是开辟一个动态数组用于存放数据,并对有效数据个数与容量赋值。

//初始化函数
void SLInit(SL* p) {
	p->data = (SeqListType*)calloc(sizeof(SeqListType), MAXSIZE);//动态内存开辟,MAXSIZE为自定义的数值
	if (p->data == NULL) {
		perror("calloc");
		return;
	}
	p->size = 0;
	p->capacity = MAXSIZE;
}

3)尾插:将目标元素插入在顺序表中的尾部。注意每次插入元素都要进行容量判断,判断顺序表时需要进行扩容。

//扩容判定
void CheckCapacity(SL* p) {
	if (p->capacity == p->size) {
		SeqListType* tmp = (SeqListType*)realloc(p->data,2*sizeof(SeqListType)*p->capacity);//开辟更大空间的动态内存
		if (p->data == NULL) {
			perror("realloc");
			return;
		}
		p->capacity *= 2;//增加容量
		p->data = tmp;//让指针指向新的动态内存区域
	}
}

//尾插
void SLPushBack(SL* p,SeqListType x) {
	//判定是否需要扩容,在需要时要进行扩容
	CheckCapacity(p);

	p->data[p->size] = x;//将最后一个位置赋值为插入的元素
	p->size++;//有效数据个数加一

}

4)头插:将目标元素插入在顺序表起始位置。在顺序表中,插入到起始位置,需要将当前起始位置及之后的所有元素都向后移一位,为目标元素腾出空间。

//头插
void SLPushFront(SL* p,SeqListType x) {

	CheckCapacity(p);
	//将顺序表中的所有元素向后移动一位
	for (int i = p->size; i > 0;i--) {
		p->data[i] = p->data[i - 1];
	}
	//将首元素位置的数据令为插入数据
	p->data[0] = x;
	//有效数据个数加一
	p->size++;

}

5)尾删:删除顺序表尾部的元素。可以直接将有效数据减一,最后一个元素虽然依然存在但用户无法进行访问。

//尾删
void SLPopBack(SL* p) {
	//顺序表中有元素才可以删除
	assert(p->size > 0);
	//有效数据个数减一,最后一个元素就访问不到了
	p->size--;

}

6)头删:删除顺序表首位元素,需要将顺序表中的所有元素向前移动一位。

//头删
void SLPopFront(SL* p) {
	assert(p->size > 0);
	//将顺序表中的所有元素向前移动一位
	for (int i = 0; i < p->size - 1; i++) {
		p->data[i] = p->data[i + 1];
	}

	p->size--;

}

7)按位置插入:定位目标所在位置,并将目标位置及其之后的元素都往后移动一位。注意:数组下标是从零开始的,要在传输前或传输后对目标位置进行处理将目标位置减一,此处我在传输前就对目标位置进行了减一,所以采用正常情况进行处理。

//按位置插入
void SLInsert(SL* p,int pos,SeqListType x) {
	//插入位置在顺序表中存在
	assert(pos <= p->size);
	//扩容判定
	CheckCapacity(p);
	//将目标位置及之后的元素都向后移动一位
	int end = p->size - 1;
	while (end >= pos) {
		p->data[end + 1] = p->data[end];
		end--;
	}
	//为目标位置赋值
	p->data[pos] = x;
	p->size++;

}

8)按位置删除:锁定目标位置,将该位置之后的元素都向前移动一位,对该位置的值进行覆盖。

//按位置删除
void SLErase(SL* p,int pos) {
	assert(pos < p->size);
	//将目标位置之后的数据都向前移动一位
	int start = pos;
	while (start < p->size - 1) {
		p->data[start] = p->data[start + 1];
		++start;
	}

	p->size--;

}

9)按位置查找:直接对目标位置进行定位,将目标位置的值返回。

//按位置查找
SeqListType SLFindByLocation(SL* p,int pos) {
	assert(pos < p->size);
	//返回该位置元素
	return p->data[pos];

}

10)按值查找:从起始位置开始对目标值进行判定,将满足目标的首个位置返回。


//按值查找
int SLFindByValue(SL* p,SeqListType x) {
	//在整个顺序表中对该值所处位置进行查找,如有多个位置,则只会显示最前面的一个位置
	for (int i = 0; i < p->size;i++) {
		if (p->data[i] == x) {
			return i;
		}
	}
	return -1;
}

11)打印:使用循环将顺序表中的元素全都打印在显示窗口中。

//打印
void SLPrintf(SL* p) {
	//将顺序表中左右元素打印出来
	for (int i = 0; i < p->size;i++) {
		printf("%d ", p->data[i]);
	}
	printf("\n");

}

12)修改:直接根据有效数据个数对目标数据进行定位,然后对其值进行修改。

//修改
void SLModify(SL* p,int pos,SeqListType x) {
	assert(pos < p->size);
	//对指定位置的元素的值进行覆盖
	p->data[pos] = x;

}

13)销毁:将动态开辟的内存空间进行释放。

//销毁
void SLDestroy(SL* p) {
	//将动态内存释放
	free(p);
	p = NULL;
}

14)保存:这样设计表的数据完整的保存在一个文本文件中,功能需要用到文件操作的相关函数。

//保存
void SLSave(SL* p) {
	//打开一个文件进行写入
	FILE* f = fopen("SeqList.txt","wb");
	if (f == NULL) {
		perror("fopen");
		return;
	}
	//将顺序表数据都写入其中
	for (int i = 0; i < p->size;i++) {
		fwrite(p->data+i,sizeof(SeqListType),1,f);
	}
	//关闭文件
	fclose(f);
	f = NULL;
}

完整代码

1、SeqList.h

#pragma once

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

#define MAXSIZE 2

typedef int SeqListType; //定义顺序表数据类型,容易做修改

typedef struct SeqList
{
	SeqListType* data;//指向动态开辟的数组
	int size;	 //有效数据个数
	int capacity;//容量
}SL;

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

//尾插
void SLPushBack(SL* p, SeqListType x);

//头插
void SLPushFront(SL* p, SeqListType x);

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

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

//插入
void SLInsert(SL* p, int pos, SeqListType x);

//按位置删除
void SLErase(SL* p, int pos);

//按位置查找
SeqListType SLFindByLocation(SL* p, int pos);

//按值查找
int SLFindByValue(SL* p, SeqListType x);

//打印
void SLPrintf(SL* p);

//修改
void SLModify(SL* p, int pos, SeqListType x);

//保存
void SLSave(SL* p);

//销毁
void SLDestroy(SL* p);

2、SeqList.c

#define _CRT_SECURE_NO_WARNINGS

#include"SeqList.h"


//初始化函数
void SLInit(SL* p) {
	p->data = (SeqListType*)calloc(sizeof(SeqListType), MAXSIZE);//动态内存开辟,MAXSIZE为自定义的数值
	if (p->data == NULL) {
		perror("calloc");
		return;
	}
	p->size = 0;
	p->capacity = MAXSIZE;
}


//扩容判定
void CheckCapacity(SL* p) {
	if (p->capacity == p->size) {
		SeqListType* tmp = (SeqListType*)realloc(p->data,2*sizeof(SeqListType)*p->capacity);//开辟更大空间的动态内存
		if (p->data == NULL) {
			perror("realloc");
			return;
		}
		p->capacity *= 2;//增加容量
		p->data = tmp;//让指针指向新的动态内存区域
	}
}

//尾插
void SLPushBack(SL* p,SeqListType x) {
	//判定是否需要扩容,在需要时要进行扩容
	CheckCapacity(p);

	p->data[p->size] = x;//将最后一个位置赋值为插入的元素
	p->size++;//有效数据个数加一

}

//头插
void SLPushFront(SL* p,SeqListType x) {

	CheckCapacity(p);
	//将顺序表中的所有元素向后移动一位
	for (int i = p->size; i > 0;i--) {
		p->data[i] = p->data[i - 1];
	}
	//将首元素位置的数据令为插入数据
	p->data[0] = x;
	//有效数据个数加一
	p->size++;

}

//尾删
void SLPopBack(SL* p) {
	//顺序表中有元素才可以删除
	assert(p->size > 0);
	//有效数据个数减一,最后一个元素就访问不到了
	p->size--;

}

//头删
void SLPopFront(SL* p) {
	assert(p->size > 0);
	//将顺序表中的所有元素向前移动一位
	for (int i = 0; i < p->size - 1; i++) {
		p->data[i] = p->data[i + 1];
	}

	p->size--;

}

//按位置插入
void SLInsert(SL* p,int pos,SeqListType x) {
	//插入位置在顺序表中存在
	assert(pos <= p->size);
	//扩容判定
	CheckCapacity(p);
	//将目标位置及之后的元素都向后移动一位
	int end = p->size - 1;
	while (end >= pos) {
		p->data[end + 1] = p->data[end];
		end--;
	}
	//为目标位置赋值
	p->data[pos] = x;
	p->size++;

}



//按位置删除
void SLErase(SL* p,int pos) {
	assert(pos < p->size);
	//将目标位置之后的数据都向前移动一位
	int start = pos;
	while (start < p->size - 1) {
		p->data[start] = p->data[start + 1];
		++start;
	}

	p->size--;

}

//按位置查找
SeqListType SLFindByLocation(SL* p,int pos) {
	assert(pos < p->size);
	//返回该位置元素
	return p->data[pos];

}


//按值查找
int SLFindByValue(SL* p,SeqListType x) {
	//在整个顺序表中对该值所处位置进行查找,如有多个位置,则只会显示最前面的一个位置
	for (int i = 0; i < p->size;i++) {
		if (p->data[i] == x) {
			return i;
		}
	}
	return -1;
}


//打印
void SLPrintf(SL* p) {
	//将顺序表中左右元素打印出来
	for (int i = 0; i < p->size;i++) {
		printf("%d ", p->data[i]);
	}
	printf("\n");

}

//修改
void SLModify(SL* p,int pos,SeqListType x) {
	assert(pos < p->size);
	//对指定位置的元素的值进行覆盖
	p->data[pos] = x;

}

//销毁
void SLDestroy(SL* p) {
	//将动态内存释放
	free(p);
	p = NULL;
}

//保存
void SLSave(SL* p) {
	//打开一个文件进行写入
	FILE* f = fopen("SeqList.txt","wb");
	if (f == NULL) {
		perror("fopen");
		return;
	}
	//将顺序表数据都写入其中
	for (int i = 0; i < p->size;i++) {
		fwrite(p->data+i,sizeof(SeqListType),1,f);
	}
	//关闭文件
	fclose(f);
	f = NULL;
}

3、Test.c

#define _CRT_SECURE_NO_WARNINGS

#include"SeqList.h"


void menu() {
	printf("*******************************\n");
	printf("***1、头插     2、尾插      ***\n");
	printf("***3、头删     4、尾删      ***\n");
	printf("***5、打印     6、按位置查找***\n");
	printf("***7、按值查找 8、修改      ***\n");
	printf("***9、插入     10、删除     ***\n");
	printf("***11、保存    12、销毁     ***\n");
	printf("***-1、退出                 ***\n");
	printf("*******************************\n");
}

int main() {
	SL s;
	SLInit(&s);

	int input = 0;
	SeqListType x;
	int pos;
	SeqListType ret1;
	int ret2;

	do{
		menu();
		printf("请输入你想进行的操作:");
		scanf("%d",&input);
		switch (input) {
		case 1:
			printf("请输入你要插入的数据,以-1结束\n");
			do {
				scanf("%d", &x);
				if (x != -1)
				{
					SLPushFront(&s, x);
				}
			} while (x != -1);
			break;
		case 2:
			printf("请输入你要插入的数据,以-1结束\n");
			do {
				scanf("%d", &x);
				if (x != -1)
				{
					SLPushBack(&s, x);
				}
			} while (x != -1);
			break;
		case 3:
			SLPopFront(&s);
			break;
		case 4:
			SLPopBack(&s);
			break;
		case 5:
			SLPrintf(&s);
			break;
		case 6:
			printf("请输入你想要查找的位置:");
			scanf("%d",&pos);
			ret1 = SLFindByLocation(&s, --pos);
			printf("该处的值为:%d\n",ret1);
			break;
		case 7:
			printf("请输入你想要查找的值:");
			scanf("%d", &x);
			ret2 = SLFindByValue(&s, x);
			printf("该值的序号为:%d\n", ++ret2);
			break;
		case 8:
			printf("请输入你想要修改的位置:");
			scanf("%d", &pos);
			printf("请输入修改后的值:");
			scanf("%d",&x);
			SLModify(&s, --pos,x);
			break;
		case 9:
			printf("请输入你想要插入的位置:");
			scanf("%d", &pos);
			printf("请输入你想要插入的值:");
			scanf("%d", &x);
			SLInsert(&s, --pos, x);
			break;
		case 10:
			printf("请输入你想要删除的位置:");
			scanf("%d", &pos);
			SLErase(&s, --pos);
			break;
		case 11:
			SLSave(&s);
			break;
		case 12:
			SLDestroy(&s);
			break;
		case -1:
			break;
		}
	} while (input != -1);


	return 0;
}

文章来源:https://blog.csdn.net/wjfwonderful/article/details/135508032
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。