1.什么是线性表?
- 线性表是最基本、最简单、也是最常用的一种数据结构。一个线性表是n个具有相同特性的数据元素的有限序列。
线性表的特征:数据元素之间具有一种“一对一”的逻辑关系。
线性表的分类:
线性表中数据存储的方式可以是顺序存储,也可以是链式存储,按照数据的存储方式不同,可以把线性表分为顺序表和链表。
2.什么是顺序表?
线性表的顺序存储又称为顺序表,实际上就是一个数组;它用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。第1个元素存储在线性表的起始位置,第 i 个元素的存储位置后面紧接着存储的是第 i+1个元素,称 i 为元素在线性表中的位序。
3.顺序表的静态分配
顺序表在静态分配时,使用“静态”的数组存放数据元素。静态的意思是:数组的大小和空间是事先预定好的,因此一旦空间占满,如果出现新的数据,就会产生溢出,最终导致程序崩溃。
缺点:开小了不够用,开大了会存在浪费。
(1)顺序表的元素插入
public class SequenceList<T>{
//存储元素的数组
private T[] eles;
//记录当前顺序表中的元素个数
private int N;
//构造方法
public SequenceList(int capacity){
//初始化数组
this.eles=(T[])new Object[capacity];
//初始化长度
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){
return eles[i];
}
//向线型表中添加元素t
public void insert(T t){
if (N==eles.length){
throw new RuntimeException("当前表已满");
}
eles[N++]=t;
}
}
(2)顺序表的元素指定位置插入
//在i元素处插入元素t
public void insert(int i,T t){
if (N==eles.length){
throw new RuntimeException("当前表已满");
}
if (i<0 || i>N){
throw new RuntimeException("插入的位置不合法");
}
//先把i索引处的元素及其后面的元素依次向后移动一位
for(int index=N;index>i;index--){
eles[index]=eles[index-1];
}
//再把t元素放到i索引处即可
eles[i]=t;
//元素个数+1
N++;
}
(3)顺序表的元素指定位置删除
//删除指定位置i处的元素,并返回该元素
public T remove(int i){
if (i<0 || i>N-1){
throw new RuntimeException("当前要删除的元素不存在");
}
//记录索引i处的值
T current = eles[i];
//索引i后面元素依次向前移动一位即可
for(int index=i;index<N-1;index++){
eles[index]=eles[index+1];
}
//元素个数-1
N--;
return current;
}
(4)顺序表的元素查找
//查找t元素第一次出现的位置
public int indexOf(T t){
if(t==null){
throw new RuntimeException("查找的元素不合法");
}
for(int i=0;i<N;i++){
if (eles[i].equals(t)){
return i;
}
}
return -1;
}
(5)顺序表的元素遍历
在java中,遍历集合的方式一般都是用的是foreach循环,如果想让我们的SequenceList也能支持foreach循环,则需要做如下操作:
public class SequenceList<T> implements Iterable<T>{
……上述代码省略
@Override
public Iterator<T> iterator() {
return new SIterator();
}
private class SIterator implements Iterator{
private int cusor;
public SIterator(){
this.cusor=0;
}
@Override
public boolean hasNext() {
return cusor<N;
}
@Override
public Object next() {
return eles[cusor++];
}
}
}
(6)代码测试
public class SequenceListTest {
public static void main(String[] args) {
//创建顺序表对象
SequenceList<String> sl = new SequenceList<String>(10);
//测试插入
sl.insert("小明");
sl.insert("小比");
sl.insert("小迪");
sl.insert(1,"小斯");
for (String s : sl) {
System.out.println(s);
}
System.out.println("------------------------------------------");
//测试获取
String getResult = sl.get(1);
System.out.println("获取索引1处的结果为:"+getResult);
//测试删除
String removeResult = sl.remove(1);
System.out.println("删除的元素是:"+removeResult);
for (String s : sl) {
System.out.println(s);
}
//测试清空
sl.clear();
System.out.println("清空后的线性表中的元素个数为:"+sl.length());
}
}
4.顺序表的动态分配
采用动态分配时,存储数组的空间是在程序执行过程中通过动态存储分配语句分配的,一旦数据空间占满,就另外开辟一块更大的存储空间,用以替换原来的存储空间,从而达到扩充数组空间的目的,而不需要为线性表一次性地划分所有空间。
考虑容器的容量伸缩性,其实就是改变存储数据元素的数组的大小,那我们需要考虑什么时候需要改变数组的大
小?
(1)添加元素时:
//向线型表中添加元素t
public void insert(T t){
if (N==eles.length){
resize(2*eles.length);
}
eles[N++]=t;
}
//在i元素处插入元素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;
//元素个数+1
N++;
}
//根据参数newSize,重置eles的大小
public void resize(int newSize){
//定义一个临时数组,指向原数组
T[] temp=eles;
//创建新数组
eles=(T[])new Object[newSize];
//把原数组的数据拷贝到新数组即可
for(int i=0;i<N;i++){
eles[i]=temp[i];
}
}
(2)移除元素时:
- 移除元素时,应该检查当前数组的大小是否太大,比如正在用100个容量的数组存储10个元素,这样就会造成内存空间的浪费,应该创建一个容量更小的数组存储元素。如果我们发现数据元素的数量不足数组容量的1/4,则创建一个是原数组容量的1/2的新数组存储元素。
//删除指定位置i处的元素,并返回该元素
public T remove(int i){
//记录索引i处的值
T current = eles[i];
//索引i后面元素依次向前移动一位即可
for(int index=i;index<N-1;index++){
eles[index]=eles[index+1];
}
//元素个数-1
N--;
if (N<eles.length/4){
resize(eles.length/2);
}
return current;
}
//根据参数newSize,重置eles的大小
public void resize(int newSize){
//定义一个临时数组,指向原数组
T[] temp=eles;
//创建新数组
eles=(T[])new Object[newSize];
//把原数组的数据拷贝到新数组即可
for(int i=0;i<N;i++){
eles[i]=temp[i];
}
}
5.顺序表的时间复杂度
- get(i):不难看出,不论数据元素量N有多大,只需要一次eles[i]就可以获取到对应的元素,所以时间复杂度为O(1);
- insert(int i,T t):每一次插入,都需要把i位置后面的元素移动一次,随着元素数量N的增大,移动的元素也越多,时间复杂为O(n);
- remove(int i):每一次删除,都需要把i位置后面的元素移动一次,随着数据量N的增大,移动的元素也越多,时间复
杂度为O(n);
由于顺序表的底层由数组实现,数组的长度是固定的,所以在操作的过程中涉及到了容器扩容操作。这样会导致顺序表在使用过程中的时间复杂度不是线性的,在某些需要扩容的结点处,耗时会突增,尤其是元素越多,这个问题越明显
6.ArrayList实现
java中ArrayList集合的底层也是一种顺序表,使用数组实现,同样提供了增删改查以及扩容等功能。
原文地址:https://www.jb51.cc/wenti/3282071.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。