1.顺序存储
通过上一章我们知道了什么是线性表,讲述了它的逻辑结构,接下来我们具体谈一谈它的物理存储结构之一顺序存储结构,先看下图
2.地址计算法
因为线性表的顺序存储是连续的,所以看下图得知
假设每一个元素占据n个字节,那么数组元素的关系就是
loc(ai+1) = loc(ai) + n
loc(ai) = loc(a1) + (i-1)*n
也就是如果ai的地址是00001,每一个元素占据4个字节,那么ai+1的地址就是00005
注:loc代表获取元素的下标
通过上述公式我们可以推断出顺序存储的线性表中的任何一个元素的地址,通过公式我们知道,无论求哪个元素的地址,他们时间复杂度都是O(1)。也称为随机存储结构。
随机存储结构:同一时间访问一组序列中的一个随意组件。反之则称循序访问,即是需要更多时间去访问一个远程组件
3.对应操作
1.线性表初始化
- 根据线性表的三要素,初始化线性表
/**
* 线性表顺序存储
* */
public class MyLinearList {
/**
* 保存线性表中的元素的数组
* */
private Model[] arr;
/**
* 线性表的最大长度
* */
private int maxLength;
/**
* 线性表的当前长度
* */
private int currentLength;
MyLinearList(int maxLength){
this.maxLength = maxLength;
arr = new Model[maxLength];
}
2.插入操作
(思路)
-
如果插入的位置不合理,则抛出异常
-
从最后一个元素起遍历到第i个位置,并分别将它们都向后移动一个位置
-
将要插入的元素填到第i的位置上
-
将当前线性表的长度+1
(代码)
/**
* 指定位置进行插入操作
* */
public void insertElementByIndex(Model m, int index){
if(index < 0 || index > maxLength - 1){
throw new RuntimeException("元素插入失败,插入的位置有瑕疵");
}
if(currentLength >= maxLength){
throw new RuntimeException("元素插入失败,线性表已满");
}
for (int n = arr.length-1; n > index; n--){
arr[n] = arr[n-1];
}
arr[index] = m;
currentLength++;
}
3.删除操作
(思路)
-
删除的位置不合理抛出异常
-
取出删除的元素
-
从删除的元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一个位置
-
将当前线性表的长度-1
(代码)
/**
* 根据位置删除元素
*
* @return 被删除的元素
*/
public Model deleteElementByIndex(int index){
if(index < 0 || index > arr.length - 1){
throw new RuntimeException("元素删除失败,删除的位置有瑕疵");
}
if(currentLength <= 0){
throw new RuntimeException("元素删除失败,该列表中暂无可删除元素");
}
// 被删除的元素
Model deleteModel = arr[index];
for (int n = index; n < arr.length - 1; n++){
arr[n] = arr[n + 1];
}
currentLength--;
return deleteModel;
}
4.查询操作
/**
* 查询元素
* */
public Model getElement(int index){
if (index < 0 || index > maxLength) {
throw new RuntimeException("查询失败,查询的位置有瑕疵");
}
if(currentLength <= 0){
throw new RuntimeException("元素查询失败,该列表中暂无可查询元素");
}
return arr[index];
}
4.分析
来根据前几章讲过的时间复杂度来分析下
-
查询:
查询只是根据元素的下标去查找,那么它的时间复杂度为O(1)
-
添加和删除:
如果要删除或者添加的元素是在末尾,那么循环只走一次,时间复杂度为O(1) 如果元素位置在头部,则时间复杂度为O(n)
中间取平均值为: O(n) / 2 那么综合而言,添加和删除的时间复杂度为O(n)
也就是说,查询的效率要高于删除或者添加的效率