顺序表的连续空间有两种初始化方式一种是直接定义数组,即固定大小分配,也就是我们固定了其最大长度。另一种是使用动态内存分配,如C中的malloc(sizeof(int)*n)与c++中的new int[n],动态分配的内存是堆内存,在C/C++中没有自动GC,需要我们手动释放,free()与delete,使得我们对其最大长度进行可控
1、
构造一个空的顺序表L,最大长度为Maxsize,时间复杂度为O(1)、空间复杂度O(1)
2、获取顺序表指定元素,时间复杂度O(1)、空间复杂度O(1)
3、查找表内是否有某个元素int e,时间复杂度O(n)、空间复杂度O(1)
4、在指定位置插入指定元素,时间复杂度O(n)、空间复杂度O(1)
5、删除顺序表中指定位置的元素,时间复杂度O(n)、空间复杂度O(1)
6、销毁顺序表,时间复杂度O(1)、空间复杂度O(1)
7、打印顺序表到终端,时间复杂度O(n)、空间复杂度O(1)
/**
* @file main.cpp
* @author gaowanl (you@domain.com)
* @brief 线性表-顺序表
* @version 0.1
* @date 2022-03-01
*
* @copyright Copyright (c) 2022
*
*/
#include <iostream>
using namespace std;
#define Maxsize 100 //每个顺序表最大存储Maxsize个元素
/**
* @brief int数据顺序表
*
*/
typedef struct
{
int *elem;
int length; //记录线性表末尾用到那里了,初始为0表示用到了0前面一个位置也就没有任何使用
} SqList;
/**
* @brief 构造一个空的顺序表L,最大长度为Maxsize,时间复杂度为O(1)、空间复杂度O(1)
*
* @param L 顺序表数据结构SqList引用
* @return true 初始化成功
* @return false 初始化失败
*/
bool InitList(SqList &L) //在此使用C++引用,否则在C语言使用二维指针
{
.elem = new int[Maxsize]; //为顺序表分配Maxsize个空间
L
if (!L.elem)
return false; //存储分配失败
.length = 0; //空表长度为0
L
return true;
}
/**
* @brief 获取顺序表指定元素,时间复杂度O(1)、空间复杂度O(1)
*
* @param L 顺序表数据结构
* @param i 获取第i个元素
* @param e int类型引用
* @return true int&e获取第i个位置元素引用成功
* @return false int&e获取第i个位置元素引用成功
*/
bool GetElem(SqList L, int i, int &e)
{
if (i < 1 || i > L.length)
return false;
//判断i值是否合理,若不合理,返回false
= L.elem[i - 1]; //第i-1的单元存储着第i个数据
e return true;
}
时间复杂度
最好情况:元素正好在第一个位置,比较一次就查找成功,时间复杂度为O(1)
最坏情况:元素正好在最后一个位置,比较n次查找成功,时间复杂度为O(1)
平均情况:查找第一个元素必须1次比较、第二个元素要2次比较、第i个元素需要i此比较,把每种情况比较次数乘以其查找概率P(i)并求和,即为平均复杂度.(1/n)(1+2+3+…+n)=(n+1)/2,在此情况是那个元素被查找的概率是1/n,故平均复杂度为O((n+1)/2)
即 O(n)
空间复杂度
除存储处理数据本身,常量空间,故为O(1)
/**
* @brief 查找表内是否有某个元素int e,时间复杂度O(n)、空间复杂度O(1)
*
* @param L 顺序表
* @param e 要查找的元素int e
* @return int 当搜索到目标元素e 返回int n 表示为顺序表中第n个元素
* @return -1 表示没有找到目标元素int e
*/
int LocateELem(SqList L, int e)
{
//遍历所有元素
for (int i = 0; i < L.length; i++)
if (L.elem[i] == e)
return i + 1; //第几个元素,例如第5个元素,下标其实为4
return -1;
}
时间复杂度
在插入时,有n+1个位置供我们进行插入,例如第一个元素的前面,我们则需要移动n个元素,在最后一个元素后插入需要移动0个元素,故平均时间复杂度为(n+(n-1)+(n-2)+…+0)/(n+1),每个位置插入的概率为1/(n+1)
/**
* @brief 在指定位置插入指定元素,时间复杂度O(n)、空间复杂度O(1)
*
* @param L 顺序表
* @param i 表示在第i个位置插入新的元素,i>=1且i<=L.length
* @param e 要插入的元素
* @return true 插入成功
* @return false 插入失败
*/
bool ListInsert_Sq(SqList &L, int i, int e)
{
if (i < 1 || i > L.length + 1) // i值不合法 i必须在 1<=i<=L.length+1
return false;
if (L.length == Maxsize) //存储空间已满
return false;
//最后一个元素的下标为L.length-1 需要 将目标下标i-1 及后面的元素向后移动一个位置
for (int j = L.length - 1; j >= i - 1; j--)
.elem[j + 1] = L.elem[j]; //从最后一个元素开始后移,直到第i个元素后移
L.elem[i - 1] = e; //将新元素e放入第i个位置
L.length++; //表长增1
Lreturn true;
}
复杂度分析
时间复杂度分析与插入元素类似,((n-1)+…+1+0)/n=(n-1)/2,即O(n)
/**
* @brief 删除顺序表中指定位置的元素,时间复杂度O(n)、空间复杂度O(1)
*
* @param L 顺序表
* @param i 删除第i位置的元素 i>=1 且 i<L.length
* @param e 将删除额元素赋值给e
* @return true 删除成功
* @return false 删除失败
*/
bool ListDelete_Sq(SqList &L, int i, int &e)
{
if ((i < 1) || (i > L.length)) // 1<=i<=L.length-1
return false; // i值不合法
= L.elem[i - 1]; //将欲删除的元素保留在e中
e //从下标i-1开始将后面的元素向前移动一个位置
for (int j = i; j <= L.length - 1; j++)
.elem[j - 1] = L.elem[j]; //被删除元素之后的元素前移
L.length--; //表长减1
Lreturn true;
}
/**
* @brief 销毁顺序表,时间复杂度O(1)、空间复杂度O(1)
*
* @param L 顺序表
*/
void DestroyList(SqList &L)
{
if (L.elem)
delete[] L.elem; //释放手动分配的堆存储空间
}
/**
* @brief 打印顺序表到终端,时间复杂度O(n)、空间复杂度O(1)
*
* @param L 顺序表
*/
void PrintList(SqList &L)
{
//遍历顺序表每个元素
for (int i = 1; i <= L.length; i++)
{
int el;
(L, i, el); //获取第i个位置的元素值
GetElem<< el << " ";
cout if (i == L.length)
<< "\n";
cout }
}
int main(int argc, char **argv)
{
; //定义顺序表
SqList L(L); //初始化顺序表空间
InitListfor (int i = 1; i < 5; i++)
(L, i, i); //插入元素i到第i个位置
ListInsert_Sq(L); //打印顺序表
PrintList<< LocateELem(L, 3) << endl; //打印第3个元素
cout int temp;
(L, 1, temp); //删除第1个元素
ListDelete_Sq<< temp << endl; //打印刚刚删除的第1个位置的元素
cout (L); //打印顺序表
PrintList(L, 1, 100); //插入元素100到第1个位置
ListInsert_Sq(L); //打印顺序表
PrintList(L); //释放顺序表空间
DestroyListreturn 0;
}
/*
1 2 3 4
3
1
2 3 4
100 2 3 4
*/