顺序表的定义
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元,依次存储线性表中的各个元素、使得线性表中再逻辑结构上响铃的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
顺序表的实现
顺序表API设计
?顺序表的代码实现
public class SequenceList<T> implements Iterable<T>{
private T[] eles; //存储元素的数组
private int N; //当前线性表的长度
//创建容量为capacity的SequenceList
public SequenceList(int capacity){
eles=(T[])new Object[capacity];
N=0;
}
//空置线性表
public void clear(){
N=0;
}
//判断线性表是否为空,是返回true 否返回false
public boolean isEmpty(){
return N==0;
}
//获取线性表中元素的个数
public int length(){
return N;
}
//读取并返回线性表中的第i个元素的值
public T get(int i){
return eles[i];
}
//在线性表的第i个元素插入一个值为t的数据元素
public void insert(int i,T t){
//把i位置空出来,i位置及其后边的元素依次向后移动一位
for(int index=N-1;index>=i;index--){
eles[index+1]=eles[index];
}
//把t放到i位置处
eles[i]=t;
//元素数量+1
N++;
}
//向线性表插入一个元素t
public void insert(T t){
eles[N++]=t;
}
//删除并返回线性表中第i个数据元素
public T remove(int i){
//记录i位置处的元素
T result=eles[i];
//把i位置后边的元素都向前移动一位
for(int index=i+1;index<N;index++){
eles[index-1]=eles[index];
}
//元素数量-1
N--;
return result;
}
//返回线性表中首次出现的指定的数据元素的位序号,若不存在,则返回-1
public int indexOf(T t){
for(int index=0;index<N;index++){
if(eles[index].equals(t)){
return index;
}
}
return -1;
}
//打印当前线性表的元素
public void showEles(){
for(int i=0;i<N;i++){
System.out.print(eles[i]+" ");
}
System.out.println();
}
@Override
public Iterator iterator() {
return new SIterator();
}
private class SIterator implements Iterator{
private int cur;
public SIterator(){
this.cur=0;
}
@Override
public boolean hasNext() {
return cur<N;
}
@Override
public T next() {
return eles[cur++];
}
}
}
//测试代码
public class SequenceListTest {
public static void main(String[] args){
SequenceList<String> s=new SequenceList<>(10);
//测试插入
s.insert("a1");
s.insert("b2");
s.insert("c3");
s.insert("d4");
s.insert(0,"a");
//测试遍历
s.showEles();
//测试获取
String s1 = s.get(1);
System.out.println(s1);
//测试删除
String remove = s.remove(2);
System.out.println(remove);
s.showEles();
//测试清空
s.clear();
System.out.println(s.length());
}
}
顺序表的容量可变
在之前的实现中,当我们使用SequenceList时,先new SequenceList(10)创建一个对象,创建对象时就需要指定容器的大小,初始化指定大小的数组来存储元素,当我们插入元素时,如果已经插入了10个元素,还要继续插入数据,则会报错,就不能插入了。这种设计不符合容器的设计理念,因此我们在设计顺序表时,应该考虑它的容量的伸缩性。
情况一:添加元素时
添加元素时,应该检查当前数组的大小是否能容纳新的元素,如果不能容纳,则需要创建新的容量更大的数组,我们这里创建一个是原数组两倍容量的新数组存储元素。
情况二:移除元素时
移除元素时,应该检查当前数组的大小是否太大,比如正在用100个容量的数组存储10个元素,这样就会造成内存空间的浪费,应该创建一个容量更小的数组存储元素。如果我们发现数据元素的数量不足数组容量的1/4,则创建一个是原数组容量的1/2的新数组存储元素。
顺序表的容量可变代码实现
public class SequenceList<T> implements Iterable<T>{
private T[] eles; //存储元素的数组
private int N; //当前线性表的长度
//创建容量为capacity的SequenceList
public SequenceList(int capacity){
eles=(T[])new Object[capacity];
N=0;
}
//空置线性表
public void clear(){
N=0;
}
//判断线性表是否为空,是返回true 否返回false
public boolean isEmpty(){
return N==0;
}
//获取线性表中元素的个数
public int length(){
return N;
}
//读取并返回线性表中的第i个元素的值
public T get(int i){
return eles[i];
}
//在线性表的第i个元素插入一个值为t的数据元素
public void insert(int i,T t){
//当数组元素达到容量时 需要扩容
if(N==eles.length){
resize(eles.length*2);
}
//把i位置空出来,i位置及其后边的元素依次向后移动一位
for(int index=N-1;index>=i;index--){
eles[index+1]=eles[index];
}
//把t放到i位置处
eles[i]=t;
//元素数量+1
N++;
}
//向线性表插入一个元素t
public void insert(T t){
//当数组元素达到容量时 需要扩容
if(N==eles.length){
resize(eles.length*2);
}
eles[N++]=t;
}
//删除并返回线性表中第i个数据元素
public T remove(int i){
//记录i位置处的元素
T result=eles[i];
//把i位置后边的元素都向前移动一位
for(int index=i+1;index<N;index++){
eles[index-1]=eles[index];
}
//元素数量-1
N--;
//当数组元素小于数组大小的1/4 则重置数组的大小
if(N>0 && N<eles.length/4){
resize(eles.length/2);
}
return result;
}
//返回线性表中首次出现的指定的数据元素的位序号,若不存在,则返回-1
public int indexOf(T t){
for(int index=0;index<N;index++){
if(eles[index].equals(t)){
return index;
}
}
return -1;
}
//打印当前线性表的元素
public void showEles(){
for(int i=0;i<N;i++){
System.out.print(eles[i]+" ");
}
System.out.println();
}
//改变容量
public void resize(int newSize){
//存上旧的数组元素
T[] temp=eles;
//创建新数组
eles=(T[])new Object[newSize];
//将旧数组的元素拷贝到新数组中
for(int i=0;i<N;i++){
eles[i]=temp[i];
}
}
@Override
public Iterator iterator() {
return new SIterator();
}
private class SIterator implements Iterator{
private int cur;
public SIterator(){
this.cur=0;
}
@Override
public boolean hasNext() {
return cur<N;
}
@Override
public T next() {
return eles[cur++];
}
}
}
//测试代码
public class SequenceListTest2 {
public static void main(String[] args){
SequenceList<String> str=new SequenceList<>(2);
str.insert("qw");
str.insert("er");
str.insert("12");
str.insert("34");
}
}
顺序表的时间复杂度分析
1.get(i):不难看出,不论数据元素量N有多大,只需要一次eles[i]就可以获取到对应的元素,所以时间复杂度为O(1); 2.insert(int i,T t):每一次插入,都需要把i位置后面的元素移动一次,随着元素数量N的增大,移动的元素也越多,时间复杂为O(n); 3.remove(int i):每一次删除,都需要把i位置后面的元素移动一次,随着数据量N的增大,移动的元素也越多,时间复杂度为O(n); 4.由于顺序表的底层由数组实现,数组的长度是固定的,所以在操作的过程中涉及到了容器扩容操作。这样会导致顺序表在使用过程中的时间复杂度不是线性的,在某些需要扩容的结点处,耗时会突增,尤其是元素越多,这个问题越明显。
|