一、线性表的定义
线性表
零个或多个元素的有限序列
需要强调两点:
第一,线性表是一个序列,也就是说,元素之间是有顺序的
第二,线性表是有限的,即元素的个数是有限制的。当线性表中的元素个数(n = 0)时,线性表为空,称为空表
二、线性表的基本操作
// 初始化一个空的线性表
void init();
// 获取线性表中元素的个数
int getSize();
// 判断线性表是否为空表
boolean isEmpty();
// 插入一个元素
void add(E e);
// 删除一个元素
E remove(E e);
// 查找元素e是否存在与线性表中
boolean contains(E e);
数组 - 线性表的顺序存储结构
线性表的顺序存储结构,指用一段连续的存储单元依次存储线性表的数据元素。
线性表的存储示意图如下:
摘自《大话数据结构》
接着来看看Java中自定义数组的初始化代码:
public class Array<E> {
private E[] data;
private int size;
/**
* 设置初始容量
*/
public Array(){
this(10);
}
public Array(int capacity){
this.data = (E[]) new Object[capacity];
size = 0;
}
}
从以上代码,我们可以看出,顺序存储需要的三个属性:
存储空间的起始位置: 数组 data,它的存储位置就是存储空间的存储位置
线性表的最大存储容量: capacity
线性表的当前长度: size
地址计算方式
数组的下标从0开始,如果要访问数组的第i个元素,其下标应为 i - 1
摘自《大话数据结构》
获取数组中的一个元素
// 时间复杂度 T = O(1)
public E get(int index){
if(index < 0 || index >= size){
throw new IllegalArgumentException("Get failed.ArrayIndexOutOfBounds.");
}
return data[index];
}
向数组中插入元素
插入元素时,需要将该元素将要插入位置的元素及后面的元素全部后移一个存储单元,如图所示:
摘自《大话数据结构》
插入算法的思路:
如果插入位置不合理,则抛出异常
如果插入元素之后数组的长度大于等于数组的容量,那么进行扩容,或者抛出异常
从最后一个元素向前开始遍历到index,将这些位置上的元素都向后移动一个位置
将要插入的元素插入到index
维护size,size 加 1
代码实现如下:
/**
* 时间复杂度 T = O(n)
*/
public void add(int index,E e){
if(index < 0 || index > data.length){
throw new IllegalArgumentException("Add failed. Index might only between 0 to capacity.");
}
if(size == data.length){
resize(data.length * 2);
}
for(int i = size - 1;i >= index;i--){
// 将插入位置的后面的元素后移
data[i + 1] = data[i];
}
data[index] = e;
size ++;
}
删除数组中的元素
摘自《大话数据结构》
删除算法的思路:
如果删除位置不合理,则抛出异常
取出删除元素
从要删除元素的 index 位置开始遍历到最后一个元素,将这些元素都向前移动一个位置
维护size, size 减 1
代码实现如下:
// 时间复杂度 T = O(n)
public E remove(int index){
if(index < 0 || index >= size){
throw new IllegalArgumentException("Remove failed. Index might only between 0 to capacity.");
}
E ret = data[index];
for(int i = index + 1;i < size;i++){
data[i - 1] = data[i];
}
size --;
data[size] = null;
// 防止复杂度震荡
if(size == data.length / 4 && data.length / 2 != 0){
resize(data.length / 2);
}
return ret;
}
查找数组中的元素
/**
* T = O(n)
*/
public int find(E e){
for(int i = 0;i < size;i++){
if(data[i].equals(e)){
return i;
}
}
return -1;
}
总结
数组的优点
- 可以快速查找数组中的任一元素
数组的缺点
如果插入和删除不在尾部,那么这些操作都需要移动大量的元素,效率较低
当数组长度变化较大时,难以确定数组的存储空间的容量,会导致存储空间的浪费,造成存储空间的 ” 碎片 “