1.介绍
物理顺序结构采用一段物理地址连续的存储单元依次存储数据元素的线性结构,来表示逻辑的线性结构。语言上可以采用数组存储。在数组上完成数据的增删查改。
物理结构形似下图:
顺序表是随机存储,即访问任一元素都是消耗的时间都相同。
假设表首元素地址为pFirst,每个元素大小为d,则我们想要读写一个元素,假设位序为i,则利用address=pFirst+(i-1)d,即可转到所需要的元素地址。
C++数组下标从0开始,因此公式简化为address=pFirst+id。
2.存储方式
我们设置的类名为SqList,意思为sequence list,即顺序表。
(1)静态存储
采用语言内置的数组表示,因此元素个数一旦确定,不可更改。
因此,受到一定限制,不可动态增长。
template<typename T>
class SqLsit
{
static const int maxSize = 100;
T data[maxSize];
int length;
};
(2)动态存储
可以自动扩容,以适应程序中大小不同的需求。
操作:
(1)插入
每当顺序表插入一个位置时,我们首先确保这个元素和其他元素紧密排在一起,也就是说插入之后仍然要保持一个顺序表的结构。
因此,下面的插入的元素2的情况就不合格。
用if语句确保插入的下标在[0,length]范围内。
每当顺序表插入时,首先需要确保我们申请的内存足以再存一个元素。如果不够,就需要先扩容。
我们需要将插入下标内的元素和其之后的元素往后挪一位。如下图。
挪动的元素个数决定了时间复杂度。
如果,插入在表尾,则不需要挪动元素。最佳时间复杂度O(1)。
如果,插入在表头,则需要挪动全部n个元素。最坏时间复杂度O(n)。
平均情况下,插入的情况有n+1种(注意别忘了位序为数组长度的位置也可以插入),分别是挪动0个元素到挪动n个元素的情况、则平均为
(1+2+3+...+n)/(n+1)=n/2,则平均时间复杂度为O(n)。
(2)删除
如果,删除表尾,则不需要挪动元素。最佳时间复杂度O(1)。
如果,删除表头,则需要挪动全部n-1个元素。最坏时间复杂度O(n)。
平均情况下,挪动的情况有n种,分别是挪动0个元素到挪动n-1个元素的情况、则平均为
(1+2+3+...+n-1)/n=(n-1)/2,则平均时间复杂度为O(n)。
其他操作较为简单,涉及代码如下:
#pragma once
#include<iostream>
template<typename T>
class SqList
{
T* data;
unsigned maxSize;
unsigned length;
void resize();//每次扩容两倍
public:
SqList(unsigned m=10);//创建
~SqList();//销毁
void insert(unsigned i,const T& val);//将val插入到数组下标为i的地方
void remove(unsigned i);//删除数组下标i元素
void change(unsigned i, const T& val);//修改数组下标i元素为val
int search(const T& val) const;//查找第一次与之相同的元素,返回数组下标
void print()const;//输出数组
bool empty()const;//判断是否是个空表
unsigned size()const;//返回表长
};
//每次resize()时间复杂度都为O(n)
template<typename T>
void SqList<T>::resize()
{
//扩容两倍的原因,是防止一旦动态数组满了,就会使插入元素变成O(n)级,
//这样,每次插入就不需要 频繁的调动resize
//扩容多倍又很容易浪费内存,因此扩容两倍是个好的选择。
T* p = data;
data = new T[maxSize * 2];
for (unsigned i = 0; i < length; i++)
data[i] = p[i];
maxSize *= 2;
delete[]p;
}
//空间复杂度O(n)
template<typename T>
SqList<T>::SqList(unsigned m)
{
maxSize = m;
data = new T[maxSize];
length = 0;
}
//时间复杂度O(1)
template<typename T>
SqList<T>::~SqList()
{
delete[] data;
}
//insert时间复杂度O(n)
template<typename T>
void SqList<T>::insert(unsigned i,const T& val)
{
//等于小于length都没问题,因为i是非负类型,不用考虑小于0的情况
if (i > length)
throw("out of range!");
//自动扩容
if (length == maxSize)
resize();
//先将i和i之后的元素后移一位
for (unsigned u = length; u > i; u--)
{
data[u] = data[u - 1];
}
//插入
data[i] = val;
length++;
}
//remove时间复杂度O(n)
template<typename T>
void SqList<T>::remove(unsigned i)
{
if (i >= length)
throw("out of range!");
//将i之后的元素前移一位
for (unsigned u = i; u < length-1; u++)
data[u] = data[u + 1];
length--;
}
//change时间复杂度O(1),也可以重载下标运算符[]
template<typename T>
void SqList<T>::change(unsigned i, const T& val)
{
if (i >= length)
throw("out if range!");
data[i] = val;
}
//search时间复杂度O(n)
template<typename T>
int SqList<T>::search(const T& val) const
{
for (int i = 0; i < length; i++)
{
if (data[i] == val)
return i;
}
//返回-1表示没找的到
return -1;
}
//print时间复杂度O(n)
template<typename T>
void SqList<T>::print()const
{
for (unsigned i = 0; i < length; i++)
{
std::cout << data[i] << " ";
}
std::cout << std::endl;
}
//empty时间复杂度O(1)
template<typename T>
bool SqList<T>::empty()const
{
if (!length)
return true;
else return false;
}
//size时间复杂度O(1)
template<typename T>
unsigned SqList<T>::size()const
{
return length;
}