数据结构与算法--顺序表
简介
以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元,依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中
顺序表API设计
为该顺序表设计几个常用API操作方法
clear()
:清空顺序表isEmpty()
:判断顺序表是否为空arrayNumbers()
:获取顺序表中元素个数arrayLength()
:获取顺序表中数组的长度get(int i)
:获取顺序表中第 i 个位置的值insert(int i,T t)
:在顺序表的第 i 个位置插入元素 tinsert(T t)
: 在顺序表末尾插入元素 tremove(int i)
: 移除顺序表中第 i 个位置的元素indexOf(T t)
: 返回顺序表中第一次出现元素 t 的下标值,不存在则返回 -1
顺序表的遍历
作为容器存储数据,需要向外部提供遍历的方式,因此需要给顺序表提供遍历方式。
-
让顺序表实现
Iterable
接口,重写iterator()
方法 -
顺序表类中提供一个私有类,实现
Iterator
接口,重写hasNext()
方法和next()
方法
顺序表的扩容和缩减
使用顺序表时,先创建一个顺序表对象,创建对象时就需要指定数组容器的大小,初始化指定大小的数组来存储元素,当插入元素时,如果已经插入了指定容量的元素,还要继续插入数据,则需要扩容操作。同时在移除顺序表的元素时也要考虑顺序表中的数组是否存在多余的容量,若存在则需要对数组容量进行缩减
添加元素时:添加元素时,应该检查当前数组的大小是否能容纳新的元素,如果不能容纳,则需要创建新的容量更大的数组,创建一个是原数组两倍容量的新数组存储元素
移除元素时: 移除元素时,应该检查当前数组的大小是否太大,比如正在用100个容量的数组存储10个元素,这样就会造成内存空间的浪费,应该创建一个容量更小的数组存储元素。如果发现数据元素的数量不足数组容量的1/4,则创建一个是原数组容量的1/2的新数组存储元素
顺序表的时间复杂度
get(i)
:不难看出,不论数据元素量有多大,只需要一次就可以获取到对应的元素,所以时间复杂度为O(1)insert(int i,T t)
:每一次插入,都需要把 i 位置后面的元素移动一次,随着元素数量的增大,移动的元素也越多,时间复杂为O(n)
由于顺序表的底层由数组实现,数组的长度是固定的,所以在操作的过程中涉及到了容器扩容操作。这样会导致顺序表在使用过程中的时间复杂度不是线性的,在某些需要扩容的结点处,耗时会突增,尤其是元素越多,这个问题越明显
顺序表的实现
import java.util.Arrays;
import java.util.Iterator;
public class SequenceList<T> implements Iterable<T>{
/**存储元素的数组*/
private T[] arrays;
/**当前顺序表中元素个数*/
private int n;
public SequenceList(int capacity){
arrays = (T[]) new Object[capacity];
this.n = 0;
}
/**清空顺序表*/
public void clear(){
//将底层数组的所有元素赋为null
Arrays.fill(arrays,null);
//将数组arrays中元素个数改为0
this.n = 0;
}
/**判断顺序表是否为空,是返回true,否返回false*/
public boolean isEmpty(){
return this.n == 0;
}
/**获取顺序表中元素的个数*/
public int arrayNumbers(){
return n;
}
/**获取顺序表中数组长度*/
public int arrayLength(){
return arrays.length;
}
/**读取并返回顺序表中的第i个元素的值*/
public T get(int i) throws Exception {
if (i < 0 || i > n-1){
throw new Exception("第 "+i+" 个元素不存在");
}
return arrays[i];
}
/**在顺序表的第i个元素之前插入一个值为t的数据元素*/
public void insert(int i,T t) throws Exception {
if (i < 0 || i > n-1){
throw new Exception("i值错误");
}
//判断是否数组需要扩容
if (n == arrays.length) {
T[] tempArrays = (T[]) new Object[this.arrays.length * 2];
System.arraycopy(arrays, 0, tempArrays, 0, n);
arrays = tempArrays;
}
//先把i索引处的元素及其后面的元素依次向后移动一位
if (n - i >= 0) {
System.arraycopy(arrays, i, arrays, i + 1, n - i);
}
//再把t元素放到i索引处即可
arrays[i] = t;
//元素个数加1
n++;
}
/**先顺序表末尾添加一个元素t*/
public void insert(T t){
//判断是否数组需要扩容,若需要则创建一个是原数组两倍容量的新数组存储元素
if (n == arrays.length) {
T[] tempArrays = (T[]) new Object[this.arrays.length * 2];
System.arraycopy(arrays, 0, tempArrays, 0, n);
arrays = tempArrays;
}
arrays[n++] = t;
}
/**删除并返回顺序表中第i个元素*/
public T remove(int i) throws Exception {
if (i < 0 || i > arrays.length - 1){
throw new Exception("不存在下标为i的元素");
}
T t = arrays[i];
for (int index = i;i < n;i++){
arrays[i] = arrays[index+1];
}
//元素个数减一
n--;
//如果发现数据元素的数量不足数组容量的1/4,则创建一个是原数组容量的1/2的新数组存储元素
if (n < arrays.length / 4) {
T[] tempArrays = (T[]) new Object[arrays.length/2];
System.arraycopy(arrays, 0, tempArrays, 0, n);
arrays = tempArrays;
}
return t;
}
/**查找t元素第一次出现的位置*/
public int indexOf(T t){
for(int i =0;i < n; i++){
if (arrays[i].equals(t)){
return i;
}
}
return -1;
}
@Override
public Iterator<T> iterator() {
return new MyIterator();
}
private class MyIterator implements Iterator<T> {
private int index;
public MyIterator(){
this.index = 0;
}
@Override
public boolean hasNext() {
return index < n;
}
@Override
public T next() {
return arrays[index++];
}
}
}