顺序表的创建——初始化、插入、删除、查找操作
▍抽象数据结构的定义
ADT List{
数据对象:D={ai|ai∈ElemType,i=1,2…,n,n≥0}
数据关系:R1={<a(i-1),ai>| a(i-1),ai∈D,i=1,2…,n }
基本操作:
InitList_Sq(&Z,len)
操作结果:构造一个空的线性表,并初始化。
ListInsert(&L,i,e0)
初始条件:线性表已存在,且1≤i≤len+1。
操作结果:在顺序表L中第i个位置之前插入新的元素e。
ListDelete(&L,i)
初始条件:线性表已存在且非空,且1≤i≤len。
操作结果:删除顺序表L中第i个位置的元素并返回,L的长度减1。
FindList(L,e0)
初始条件:线性表已存在。
操作结果:(按值查找)查找顺序表中第一个出现的该元素并定位,返回该值 所在的位置序号。
}ADT List
▍代码
//Sqlist.h
#pragma once
//exit(0) 正常运行并退出程序
//exit(1) exit(-1) 非正常运行导致程序退出
#define OVERFLOW -1 //状态量:存储分配失败,结束进程
#define OK 0 //状态量:存储分配成功,即完成初始化
#define ERROR 1 //状态量:输入不合法,出现错误,结束进程
typedef int Status; //定义返回状态
typedef float ElemType; //帮助定义顺序表中元素的数据类型
typedef int Length; //帮助定义顺序表的长度
typedef struct {
ElemType* e; //存储空间的基地址
Length len; //当前顺序表长度
Length Listsize; //分配的存储容量——总长度
}Sqlist;
Status InitList_Sq(Sqlist& L, Length len); //对顺序表进行初始化
Status ListInsert(Sqlist* L, int i, ElemType e0); //将某个数据元素插入到顺序表中
ElemType ListDelete(Sqlist* L, int i); //对顺序表内的某一数据元素实行删除操作
Length Findlist(Sqlist* L, ElemType e0); //对顺序表进行查找
void Print(Sqlist* L); //对顺序表进行输出
//list.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"Sqlist.h"
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
#define LIST_INIT_SIZE 100 //顺序表初始长度
#define LIST_INCREMENT 10 //顺序表的分配增量——扩增容量
ElemType* q; //q为插入元素所在位置
ElemType* p; //p表示元素所在位置
//初始化操作
Status InitList_Sq(Sqlist& L,Length len){
L.e = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType)); //先使用malloc函数给线性表分配初始空间
if (!L.e) {
printf("[warning]--OVERFLOW! please run again-");
exit(OVERFLOW); //存储分配失败提示,结束进程
}
//存储空间分配成功后
L.len = 0; //定义空表长度为0
L.Listsize = LIST_INIT_SIZE; //定义初始存储容量为100
printf("请输入浮点型数据元素(空格隔开即可):");
for (int j = 0; j < len; j++) //逐个输入数据元素
scanf("%f", &L.e[j]);
L.len += len; //输入完毕后顺序表的长度
return OK; //初始化顺序表已完成
}
//插入一位元素的操作-在顺序表L中第i个位置之前插入新的元素e
Status ListInsert(Sqlist* L, int i, ElemType e0) {
if (i<1 || i>=L->len + 1) //判断第i个位置是否合法
return ERROR;
//判断存储容量是否足够(如果成立,则当前存储容量已满,需要扩容
if (L->len >= L->Listsize) {
L->e = (ElemType*)realloc(L->e, (L->Listsize + LIST_INCREMENT) * sizeof(ElemType)); //realloc函数进行扩容操作
if (!L->e) { //为防止内存泄漏,需要判断(如果泄露则结束进程)
printf("[warning]--OVERFLOW! please run again-");
exit(OVERFLOW); //存储分配失败提示,结束进程
}
L->Listsize += LIST_INCREMENT; //更新存储容量
}
q = &(L->e[i - 1]); //q为插入元素所在位置
for (p = &(L->e[L->len - 1]); p >= q; --p) {
*(p + 1) = *p; //把插入元素和其后的元素后移一位
}
*q = e0; //q位置放入插入的元素
++L->len; //顺序表长度增加1位
return OK;
}
//删除一位元素的操作-删除顺序表L中第i个位置的元素并返回
ElemType ListDelete(Sqlist* L, int i) {
if (i<1 || i>L->len) //判断第i个位置是否合法(1~n)
return ERROR;
p = &(L->e[i-1]); //第i个元素的位置
ElemType e1 = *p; //接收第i个元素并返回
q = &(L->e[L->len - 1]); //倒数第1个元素地址
for (; p < q; ++p) {
*p = *(p + 1); //把删除元素之后的元素前移
}
--L->len;
return e1;
}
//查找顺序表中第一个出现的该元素并定位
Length Findlist(Sqlist* L,ElemType e0) {
for (int i = 0; i < L->len; i++) {
if (L->e[i] == e0)
return i + 1;
}
return 0;
}
//输出顺序表
void Print(Sqlist* L) {
printf("浮点型顺序表可展示为:");
for (int i = 0; i < L->len; i++) {
printf("%6.4f ", L->e[i]); //cout << L->e[i] << " ";
}
printf("\n\n");
}
//界面优化函数
void interface() {
printf(" “查找”请输入1\n “插入”请输入2\n “删除”请输入3\n “退出”请输入4\n");
}
int main() {
Sqlist L; //构建一个顺序表
ElemType e; //元素值——存放插入元素、查找元素、删除元素
Length len; //顺序表的长度
int ii = 0; //进行删除、查找、插入元素的序号
string c; //操作选择:1-查找 2-插入 3-删除 4-退出
//界面优化
printf("——Welcome to our linear list——\n");
printf("【已初始化】正在创建中,请输入创建的数据元素个数(len<101):len=");
scanf("%d", &len);
InitList_Sq(L, len); //初始化顺序表-给顺序表分配空间
Print(&L); //输出初始化好的顺序表
interface(); //界面优化函数
printf("--please enter your choice:");
cin >> c;
do {
//错误输入提示,可重新输入
if (c != "1" && c != "2" && c != "3" && c != "4") {
printf("[warning]WRONG input,please enter again:");
cin >> c;
}
//查找元素的操作(按序号查找)
if (c == "1") {
printf("请输入要查找的元素值 e=");
scanf("%f", &e);
ii = Findlist(&L, e);
if (ii) {
printf("查找元素的位置在第%d位", ii);
}
else {
printf("[warning]查找元素不存在!");
}
}
//插入元素的操作(按序号插入)
else if (c == "2") {
printf("请输入想要插入元素所在的序号(1≤i≤%d):i=", len);
scanf("%d", &ii);
if(ii >= 1 && ii <= len) {
printf("请输入想要插入的数据元素值(请输入浮点数):e=");
scanf("%f", &e);
ListInsert(&L, ii, e);
printf("——插入完成!\n");
Print(&L);
}
else {
printf("[warning]Insert out of range!");
}
}
//删除元素操作
else if (c == "3") {
printf("请输入要删除的元素所在序号 i=");
scanf("%d", &ii);
e = ListDelete(&L, ii);
if (e == ERROR) {
puts("[warning]Delete out of range!");
}
else {
printf("——删除完成!删除的元素是:%6.4lf\n", e);
Print(&L);
}
}
else if (c == "4")break;
printf("\n———--please enter your choice again:");
cin >> c;
} while (c == "1" || c == "2" || c == "3");
return 0;
}
▍时间复杂度与空间复杂度分析
- 元素的查找(FindList)
在按值查找的函数中,算法的时间耗费在比较数据元素上,删除元素的位置共有n个且是等可能的,即p=1/n,而需要查找 i 个元素。
则语句频度为∑p×i (1≤i≤n) =(n+1)/2
则算法的时间复杂度为O(n);在查找时,内存空间并没有发生变化,所以算法的空间复杂读为O(1)。
- 元素的插入(ListInsert)
ListInsert函数中,最基本的操作是for循环中的语句,也就是移位操作,输入想要插入的第i个位置,那么有n-i+1个元素需要发生移动,而i的取值在1~n之间,那么i是1~n之间的概率是等可能的,都为p=1/n,则
语句频度为∑p×(n-i+1) (1≤i≤n) =(n+1)/2
则算法的时间复杂度为O(n);又因为每次移动都动态申请内存空间变化,所以算法的空间复杂读也为O(n)。
- 元素的删除(ListDelete)
与插入操作相同,最基本的操作是移位操作
改变的是有n-i 个元素需要发生移动
(而i的取值在1~n之间,那么i是1~n之间的概率是等可能的,都为p=1/n)则语句频度为∑p×(n-i) (1≤i≤n) =(n-1)/2
则算法的时间复杂度为O(n);又因为每次移动都动态申请内存空间变化,所以算法的空间复杂读也为O(n)。
▍实验事例验证与分析
①首先选择输入的数据元素个数len=2;
②其次输入len=2个浮点型数据 1.2 1.3 ,回车可以看到顺序表展示以及需要进行的操作;
③进入操作页面:操作选择:1-查找 2-插入 3-删除 4-退出
a)选择查找功能:此时输入1.2和1.200没有影响
如果输入其他没有输入的数据,则会提示该数据元素不存在:
b)选择插入功能:提示给出有序号范围
如果超出元素个数的序号,会提示超出范围
c)选择删除功能:删除刚刚插入的元素1.345
如果输入序号超出元素个数,会提示超出范围
d)退出操作页面
直接输入4即可退出程序,如下图是一系列操作。