介绍线性表的定义与基本操作
目录
前言
线性结构的特点是数据元素之间的关系是线性的,数据元素可以被看成排列在一条线上或一个环上。线性表是最简单的最常用的一种数据结构。值得注意的是数组在某种意义上也可被看作一种线性结构。
正文
1、线性表的定义
线性表是n(n>=0)个数据元素组成的有限序列。
一般用L表示线性表记作:
L=(a1,a2,a3,a4.....an)
表中的元素可以是一个数、一个符号或是由多个数据项组成的复杂信息,但同一线性表中的元素必须属于同一个数据对象。例如:英文字母(A,B,C,D,E,F,G,H....).
线性表的逻辑结构是通过元素之间的相邻关系来体现的。a1为起始节点,an为终端节点,其中对任意一对相邻的节点ai和ai+1,ai被称为ai+1的直接前驱,ai+1被称为ai的直接后继(1<=i<=n-1)。线性表中元素的个数(n)称为该表的长度。长度为零(n=0)的表示空表。
2、线性表的基本操作
线性表是一种灵活的数据结构,可以在任意位置上进行插入和删除,其表长也根据不同的操作增加或缩短。对于线性表有以下几种常用的基本操作:
3、顺序表
线性表的顺序存储称为顺序表,它是由一组连读的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个数据元素,物理位置上也相邻。
由于线性表中的所有数据元素属于同一种数据类型,所有每个元素所占的内存空间大小也相同。如下表,假设下标为0的内存地址为LOC(A),每个元素所占的空间大小为sizeof(ElemType),则下标为i-1的元素地址为:LOC(A)+(i-1)*sizeof(ElemType)
顺序表的特点:
- 顺序表最主要的特点事随机存取,即通过首地址和元素序号可在时间o(1)内找到指定的元素
- 顺序表的存储密度高,每个节点只存储数据元素
- 顺序表逻辑上相邻的元素物理上也相邻,所有插入和删除操作需要移动大量元素。
4、顺序表的实现
1、顺序表的定义
1)静态顺序表,使用定长数组来存储元素
缺陷:给小了不够用,给大了可能浪费,非常不实用
#define MaxSize 50 // 线性表的最大长度
typedef int sq_type; // 类型重命名,方便后续使用其他类型是修改
typedef struct{
sq_type data[MaxSize]; // 定义定长数组
sq_type size; // 有效数据个数
}Sqlist;
2)动态顺序表,根据长度进行开辟新的空间
动态顺序表可以根据我们的需要来设置默认以及增加空间
#define InitSize 20 // 定义线性表的最大长度
typedef int sq_type; // 类型重命名,方便后续使用其他类型是修改
typedef struct{
sq_type* data; // 定义定长数组
sq_type Maxsize; // 顺序表的最大长度
sq_type size; // 当前有效数据个数
}Sqlist;
2、顺序表的初始化
将定义顺序表中的成员初始化
void InitList(Sqlist* plist) {
puts("正在初始化顺序表中");
plist->data = (sq_type*)malloc(InitSize * sizeof(sq_type)); // 开辟一个大小为InitSize的内存空间并让data指向它
plist->Maxsize = InitSize; // 为当前最大长度赋值为初始最大长度
plist->size = 0; // 记录当前顺序表中有效值为零
puts("初始化成功....");
}
3、为表中添加数据
void add(Sqlist* plist) {
puts("请输入数据(输入负数截至):");
sq_type num; // 元素值
scanf("%d", &num);
int i = 0;
plist->size=0; // 重置有效值个数
while (num >= 0) {
if (plist->size < plist->Maxsize){ // 判断当前有效值是否小于当前最大长度
plist->data[i++] = num; //为data添加输入的值
plist->size += 1; // 添加成功后有效值加一
}
else { // 当有效值>=最大长度时停止添加数据
printf("当前以输入数据量%d,已达最大长度,如需增加长度请运行相应代码\n", plist->size);
break;
}
printf("当前以输入数据量%d,还可输入%d个:\n", plist->size, plist->Maxsize - plist->size);
scanf("%d", &num); // 再次输入为添加下一个值做准备
}
}
4、 输出顺序表
void PrintList(Sqlist* plist) {
puts("以下为当前顺序表中的数据");
for (int i = 0; i < plist->size;i++) { // 使用当前有效个数进行输出,因为下标从零开始所以小于有效个数即可
printf("%d\t", plist->data[i]);
if ((i+1) % 10 == 0) { // 判断当前输出元素个数是否为10或10的倍数,是则换行,方便查看数据
puts("");
}
}
puts("");
}
5、插入数据
// 成功返回1,失败返回0
int ListInsert(Sqlist* plist) {
// 检查是否达到当前那最大空间
if (plist->size == plist->Maxsize) {
puts("已达最大空间,请添加空间在尝试");
return 0;
}
// 检查顺序表是否为空
if (plist == NULL) {
puts("顺序表为空!请先初始化顺序表");
return 0;
}
// 输入需要插入的位置
puts("请输入需要插入的位置(从零开始):");
int i = -1;
scanf("%d", &i);
// 当指定的位置为负,或者超过当前顺序表长度则重新输入
// 输入的i==plist->size时表示插入在最后
while (i < 0 || i > plist->size) {
puts("输入错误,请重新输入!");
scanf("%d", &i);
}
puts("请输入需要插入的元素:");
sq_type e;
scanf("%d", &e);
// 从后往前,将数据往后移一位,到需要插入的位置后,停止
for(int j = plist->size-1;j>=i;j--){
plist->data[j + 1] = plist->data[j];
}
// 将数据添加到指定位置
plist->data[i] = e;
plist->size += 1; // 有效值加1
return 1;
}
6、删除数据
// 指定位置删除顺序表中的元素
// 删除指定位置上面的元素,并返回该元素
sq_type LisstDelete(Sqlist* plist) {
// 判断顺序表是否为空
if (plist == NULL) {
puts("顺序表为空!请先初始化顺序表");
return 0;
}
puts("请输入需要删除的元素的位置(从零开始):");
int i;
scanf("%d", &i);
// 位置需要大于零,并且小于当前有效值,在这里设置一个限制防止输入错误
while (i < 0 || i >= plist->size) {
puts("输入位置错误,请重新输入!");
scanf("%d",&i);
}
// 得到需要删除的位置后,将元素赋给result
int result=plist->data[i];
// 将删除的位置,后面的元素往前移动
for (int j = i; j < plist->size; j++) {
plist->data[j] = plist->data[j + 1];
}
plist->size--; // 删除之后有效值减1
return result; // 返回被删除的元素
}
7、增加顺序表最大长度
// 增加顺序表的最大长度
void ListAddSize(Sqlist* plist) {
// 定义指针来指向当前有效数据所在的空间
sq_type *p = plist->data;
printf("当前顺序表最大长度为:%d\n",plist->Maxsize);
puts("请输入需要添加长度的个数:");
int len;
scanf("%d", &len);
// 增加的长度需要大于零
while (len < 0) {
puts("输入错误请重新输入!");
scanf("%d", &len);
}
// 重新为data开辟一个新的空间,空间大小为当前最大长度加需要增加的长度
plist->data = (sq_type*)malloc(sizeof(sq_type) * (plist->Maxsize + len));
// 将原来的数据赋值到新开辟的空间中
for (int i = 0; i < plist->size; i++) {
plist->data[i] = p[i]; // 注意p=plist->data 所以可以直接使用p来调用之前的数据
}
// 修改用于保存最大长度成员
plist->Maxsize += len;
free(p); // 释放原来的空间
printf("添加成功!\n当前顺序表最大长度为:%d\n", plist->Maxsize);
}
5、完整代码
本次顺序表使用分别使用了index.c、函数.c、头文件.h三个文件来实现,一下为每个文件中的代码
头文件.h
#pragma once // 防止头文件被二次调用
#include<stdio.h> //perror, printf
#include<stdlib.h> // malloc
#include<string.h> // 导入字符串库 使用pust来写提示
#pragma warning(disable: 4996) // 禁用 scanf 相关警告
#define InitSize 12 // 定义线性表的最大长度
typedef int sq_type; // 类型重命名,方便后续使用其他类型是修改
typedef struct{
sq_type* data; // 定义定长数组
sq_type Maxsize; // 顺序表的最大长度
sq_type size; // 当前有效数据个数
}Sqlist;
// 初始化顺序表
void InitList(Sqlist* plist);
// 增加数据
void add(Sqlist* plist);
// 输出顺序表
void PrintList(Sqlist* plist);
// 指定位置插入
int ListInsert(Sqlist* plist);
// 指定位置删除
sq_type LisstDelete(Sqlist* plist);
// 扩容 添加顺序表的最大长度
void ListAddSize(Sqlist* plist);
index.c
#include "头文件.h"
int main() {
Sqlist* head = (Sqlist*)malloc(sizeof(Sqlist));
InitList(head);
add(head);
PrintList(head);
int num=0;
while(num>=0){
puts("\n请选择您需要的功能(输入负数退出):");
puts("1、输出顺序表");
puts("2、输出顺序表的长度");
puts("3、指定位置插入数据");
puts("4、指定位置删除数据");
puts("5、扩容:增加顺序表最大长度");
puts("6、重新输入所有数据");
scanf("%d", &num);
switch (num) {
case 1:PrintList(head); break;
case 2:printf("当前顺序表的长度为:%d\n", head->size); break;
case 3:
if (ListInsert(head))
puts("插入成功!");
else
puts("插入失败");
break;
case 4:printf("已删除指定位置的元素,该元素为:%d",LisstDelete(head)); break;
case 5:ListAddSize(head); break;
case 6:add(head); break;
case -1: break;
default:puts("选择出错啦,请重新选择!"); break;
}
}
return 0;
}
函数.c
#include"头文件.h"
void InitList(Sqlist* plist) {
puts("正在初始化顺序表中");
plist->data = (sq_type*)malloc(InitSize * sizeof(sq_type));
plist->Maxsize = InitSize;
plist->size = 0;
puts("初始化成功....");
}
// 添加数据
void add(Sqlist* plist) {
puts("请输入数据(输入负数截至):");
sq_type num;
scanf("%d", &num);
int i = 0;
plist->size=0; // 重置有效值个数
while (num >= 0) {
if (plist->size < plist->Maxsize){
plist->data[i++] = num;
plist->size += 1;
}
else {
printf("当前以输入数据量%d,已达最大长度,如需增加长度请运行相应代码\n", plist->size);
break;
}
printf("当前以输入数据量%d,还可输入%d个:\n", plist->size, plist->Maxsize - plist->size);
scanf("%d", &num);
}
}
// 输出顺序表
void PrintList(Sqlist* plist) {
puts("以下为当前顺序表中的数据");
for (int i = 0; i < plist->size;i++) {
printf("%d\t", plist->data[i]);
if ((i+1) % 10 == 0) {
puts("");
}
}
puts("");
}
// 顺序表的插入
// 成功返回1,失败返回0
int ListInsert(Sqlist* plist) {
// 检查是否达到当前那最大空间
if (plist->size == plist->Maxsize) {
puts("已达最大空间,请添加空间在尝试");
return 0;
}
// 检查顺序表是否为空
if (plist == NULL) {
puts("顺序表为空!请先初始化顺序表");
return 0;
}
// 输入需要插入的位置
puts("请输入需要插入的位置(从零开始):");
int i = -1;
scanf("%d", &i);
// 当指定的位置为负,或者超过当前顺序表长度则重新输入
// 输入的i==plist->size时表示插入在最后
while (i < 0 || i > plist->size) {
puts("输入错误,请重新输入!");
scanf("%d", &i);
}
puts("请输入需要插入的元素:");
sq_type e;
scanf("%d", &e);
for(int j = plist->size-1;j>=i;j--){
plist->data[j + 1] = plist->data[j];
}
plist->data[i] = e;
plist->size += 1;
return 1;
}
// 指定位置删除顺序表
// 删除指定位置上面的元素,并返回该元素
sq_type LisstDelete(Sqlist* plist) {
// 判断顺序表是否为空
if (plist == NULL) {
puts("顺序表为空!请先初始化顺序表");
return 0;
}
puts("请输入需要删除的元素的位置(从零开始):");
int i;
scanf("%d", &i);
while (i < 0 || i >= plist->size) {
puts("输入位置错误,请重新输入!");
scanf("%d",&i);
}
int result=plist->data[i];
for (int j = i; j < plist->size; j++) {
plist->data[j] = plist->data[j + 1];
}
plist->size--;
return result;
}
// 增加顺序表的最大长度
void ListAddSize(Sqlist* plist) {
sq_type *p = plist->data;
printf("当前顺序表最大长度为:%d\n",plist->Maxsize);
puts("请输入需要添加长度的个数:");
int len;
scanf("%d", &len);
while (len < 0) {
puts("输入错误请重新输入!");
scanf("%d", &len);
}
plist->data = (sq_type*)malloc(sizeof(sq_type) * (plist->Maxsize + len));
for (int i = 0; i < plist->size; i++) {
plist->data[i] = p[i]; // 注意p=plist->data 所以可以直接使用p来调用之前的数据
}
plist->Maxsize += len;
free(p);
printf("添加成功!\n当前顺序表最大长度为:%d\n", plist->Maxsize);
}
总结
理解之后一点都不难嘛,和链表区别不大。
欢迎指针!共同进步!