K:线性表的实现—顺序表
所谓顺序表,就是顺序存储的线性表。顺序存储就是用一组地址连续的存储单元依次存放线性表中各个数据元素的存储结构。
线性表中所有数据元素的类型是相同的,所以每一个数据元素在存储器中占用相同的大小的空间。假设每一个数据元素占b个存储单元,且a0(即线性表中的第一个数据元素)的存储地址为Loc(a0)(此地址也称为线性表的基地址),则第i个数据元素的地址可表示为:
Loc(ai)=Loc(a0)+i*b 其中0<=i&&i<=n-1
从以上可知,只要顺序表的基地址和每一个数据元素所占的存储空间的大小,就可以计算出第i个数据元素的地址,即顺序表具有按数据元素的位序号随机存取的特点。
顺序表的特点:
-
线性表中逻辑上响铃的数据元素,在物理存储位置上也是相邻的
-
存储密度高,但需要预先分配“足够应用”的存储空间(也就是够用的存储空间),这可能将会造成存储空间的浪费;其中,存储密度=数据元素本身值所需的存储空间/该数据元素实际所占用的空间
-
便于随机存取
-
不便于插入和删除操作,这是因为在顺序表上进行插入和删除操作会引起大量的数据元素的移动
因为数组也具有随机存取的特点。为此,一般,顺序表采用数组来进行实现
顺序表的实现代码:
public class ArrayList<T> implements List<T>
{
private Object[] listElem;//顺序表的存储空间
private int curLen;//顺序表当前长度
public ArrayList(int maxSize)
{
this.listElem=new Object[maxSize];
this.curLen=0;
}
public void clear()//清空
{
this.curLen=0;
}
public boolean isEmpty()//判空
{
return this.curLen==0;
}
public int length()//求长度
{
return this.curLen;
}
public T get(int i)throws Exception//按位查找(在查找前判断位置是否合法),时间复杂度为O(1)
{
if(i<0||i>this.curLen-1)
throw new Exception(“查找的位置不合法”);
return (T)this.listElem[i];
}
public int indexOf(T x)//按值查找,时间复杂度为O(n)
{
int j=0;
while(j<this.curLen&&!((T)this.listElem[j]).equals(x))
j++;
if(j<this.curLen)
return j;
else
return -1;
}
public void insert(int i,T x)throws Exception//插入,时间复杂度为O(n)
{
if(this.curLen==listElem.length)//判断其否无法再加入元素
throw new Exception(“顺序表已满”);
if(i<0||i>this.curLen)
throw new Exception(“插入位置不合法”);
for(int j=this.curLen;j>i;j--)//将后面的元素往后移动
{
listElem[j]=listElem[j-1];
}
listElem[i]=x;
this.curLen++;
}
public void remove(int i)throws Exception//删除操作,时间复杂度为O(n)
{
//if(isEmpty())
//throw new Exception(“顺序表为空”);
if(i<0||i>this.curLen-1)
{
throw new Exception(“删除位置不合法”);
}
for(int j=i;j<this.curLen-1;j++)
listElem[j]=listElem[j+1];
this.curLen--;
}
}
从以上的实现中可以看出,对于顺序表而言,其插入,删除,以及按值查找的操作的时间复杂度为O(n),从而可知,顺序表有如下局限性:
-
若要将线性表扩充存储空间,则需要重新创建一个地址连续的更大的存储空间,并把所有的数据元素都复制到新的存储空间中;
-
因为顺序存储要求逻辑上相邻的数据元素,在物理存储位置上也是要相邻的,这就使得要增删数据元素则会引起平均约一半的数据元素的移动。
为此,顺序表较适合表示“静态”的线性表,即线性表一旦形成之后,就很少进行插入和删除操作。对于需要频繁执行插入和删除操作的“动态”线性表,通常采用链式存储结构。