顺序表概述
简介
- 顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
顺序表存储示意图
可以看出任意一个元素i的存储地址Loc(ai)为:
Loc(ai) = Loc(a1) + (i-1) * len
其中Loc(a1)被称为基地址,在顺序表创建时由系统分配,len为每个元素占用存储空间的长度
顺序表的基本实现(类设计)
类名
- SequenceList<T>:可以根据自己的喜好自定义,T是泛型,限制存入的元素类型,假设创建对象时,设置SequenceList<String>,表示该顺序表只能存放String类型的元素
成员变量
- 存放元素的数组:private T[ ] eles; (T表示泛型,根据存入元素的类型而自定义)
- 当前顺序表长度:private int N;
构造方法
- public SequenceList():开辟数组以便存放元素,初始化长度为0。
public SequenceList() {
// 初始化数组
this.eles = (T[]) new Object[50];
// 初始化长度
this.N = 0;
}
成员方法
- public void clear():将顺序表置空
public void clear() {
this.N = 0; //只需要将长度置为0即可
}
- public boolean isEmpty():判断当前线性表是否为空表
public boolean isEmpty() {
return this.N == 0; //长度为0就认定为空表
}
- public int length():获取线性表长度
public int length() {
return this.N;
}
- public T get(int i):获取指定位置的元素
public T get(int i) {
if (isEmpty()) //为空表时返回null
return null;
return eles[i];
}
- public void insert(T t):向顺序表中添加元素
public void insert(T t) {
eles[N++] = t; //加入元素并改变数组长度
}
- public void insert(int i, T t):在指定位置插入元素
public void insert(int i, T t) {
// 先把i索引及其后面的元素一次向后移动一位
for (int index=N;index>i;index--) {
eles[index] = eles[index-1];
}
// 再把t元素插入i处
eles[i] = t;
N++; //元素个数加一
}
- public T remove(int i):删除指定位置元素并返回该元素
public T remove(int i) {
// 记录索引i的值
T cur = eles[i];
// 索引i后面的元素依次往前移动一位
for (int j = i; j < N-1; j++) {
eles[j] = eles[j+1];
}
N--; //元素个数减一
return cur; //返回被删除的元素
}
- public int indexOf(T t):查找指定元素第一次出现的索引,若不存在,返回-1
public int indexOf(T t) {
for (int i = 0; i < N; i++) {
if (eles[i]==t) //找到则返回
return i;
}
return -1; //找不到则返回-1
}
优化
-
我们知道,顺序表使用了一个数组,但是定义数组的时候就必须声明容量,那么这时候我们会遇到两个问题:
1.数组容量远远大于实际存放的元素数量,这就会造成内存的浪费
2.当数组容量满了的时候,再插入就会出错 -
解决方法:容量可变
1.针对问题一,我们选择在删除一个元素后,若元素个数是否小于容量的1/4,则将容量变为当前容量的一半。
2.针对问题而,我们选择在插入元素前,若容量已满(n==eles.lenth),则将容量变为当前容量的两倍 -
定义改变容量的方法:public void resize(int newSize)
public void resize(int newSize) {
// 定义临时数组指向原数组
T[] temp = eles;
// 创建新数组,更新eles的容量
eles = (T[]) new Object[newSize];
// 拷贝回eles
for (int i = 0; i < N; i++) {
eles[i] = temp[i];
}
}
- 改变后的插入方法:
1.public void insert(T t):向顺序表添加元素
public void insert(T t) {
if (N==eles.length) {
resize(2*eles.length);
}
eles[N++] = t;
}
2.public void insert(int i, T t):向指定位置插入元素
public void insert(int i, T t) {
if (N==eles.length) {
resize(2*eles.length);
}
// 先把i索引及其后面的元素一次向后移动一位
for (int index=N;index>i;index--) {
eles[index] = eles[index-1];
}
// 再把t元素插入i处
eles[i] = t;
N++;
}
- 改变后的删除方法:
public T remove(int i):删除指定位置元素并返回该元素
public T remove(int i) {
// 记录索引i的值
T cur = eles[i];
// 索引i后面的元素依次往前移动一位
for (int j = i; j < N-1; j++) {
eles[j] = eles[j+1];
}
N--;
// 元素个数N不满足小于数组元素个数的1/4
if (N<eles.length/4) {
resize(eles.length/2);
}
return cur;
}
完整代码
public class SequenceList<T>{
// 存储元素的数组
private T[] eles;
// 记录当前顺序表元素的个数
private int N;
// 构造方法
public SequenceList() {
// 初始化数组
this.eles = (T[]) new Object[50];
// 初始化长度
this.N = 0;
}
// 将一个线性表置为空表
public void clear() {
this.N = 0;
}
// 判断当前线性表是否为空表
public boolean isEmpty() {
return N==0;
}
// 获取线性表长度
public int length() {
return N;
}
// 获取指定位置的元素
public T get(int i) {
if (isEmpty())
return null;
return eles[i];
}
// 向线性表中添加元素
public void insert(T t) {
if (N==eles.length) {
resize(2*eles.length);
}
eles[N++] = t;
}
// 在i处插入元素
public void insert(int i, T t) {
if (N==eles.length) {
resize(2*eles.length);
}
// 先把i索引及其后面的元素一次向后移动一位
for (int index=N;index>i;index--) {
eles[index] = eles[index-1];
}
// 再把t元素插入i处
eles[i] = t;
N++;
}
// 删除指定位置的元素并返回该元素
public T remove(int i) {
// 记录索引i的值
T cur = eles[i];
// 索引i后面的元素依次往前移动一位
for (int j = i; j < N-1; j++) {
eles[j] = eles[j+1];
}
N--;
// 元素个数N不满足小于数组元素个数的1/4
if (N<eles.length/4) {
resize(eles.length/2);
}
return cur;
}
// 查找指定元素第一次出现的位置
public int indexOf(T t) {
for (int i = 0; i < N; i++) {
if (eles[i]==t) {
return i;
}
}
return -1;
}
public void resize(int newSize) {
// 定义临时数组指向原数组
T[] temp = eles;
// 创建新数组
eles = (T[])new Object[newSize];
// 拷贝
for (int i = 0; i < N; i++) {
eles[i] = temp[i];
}
}
}
测试
代码:
public class SequenceListTest {
public static void main(String[] args) {
// 创建对象,指定元素类型为String
SequenceList<String> s1 = new SequenceList<>();
// 测试插入
s1.insert("姚明");
s1.insert("科比");
s1.insert("麦迪");
s1.insert(1,"詹姆斯");//指定位置插入
for(int i = 0;i < s1.length();i++) {
System.out.println(s1.get(i));
}
System.out.println("-----------------------------");
// 测试获取
String getres = s1.get(1);
System.out.println("获取索引1处的结果为:" + getres);
// 测试删除
System.out.println("删除前的长度为:" + s1.length());
String removeRes = s1.remove(0);
System.out.println("删除的元素为:"+removeRes+"\t"+"删除后的长度为:"+s1.length());
// 测试清空
s1.clear();
System.out.println("清空后顺序表中元素的个数为:"+s1.length());
}
}
结果: