线性表
线性表是最基本,最简单,最常用的一种数据结构,一个线性表是n个具有形同特征的数据元素的有限序列。
前躯元素和后继元素
若A元素在B元素的前面,则将A称为B的前驱元素,B称为A的后继元素。
主要特征
数据之间是“一对一”的逻辑关系
第一个元素没有前驱,称为头结点;最后一个元素没有后继,称为尾结点;除了第一个元素和最后一个元素外,其他数据元素有且仅有一个前驱和一个后继。
分类
(1)顺序存储结构:顺序表
(2)链式存储结构:链表
顺序表
定义
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组连续的存储单元,一次存储线性表中的各个元素,使得线性表中逻辑结构上相邻的数据元素存储在相邻的的物理存储单元中,即通过数据元素在物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
代码实现
主要方法类
//这里实现了Iterable接口,是要重写里面的迭代器Iterator,用于迭代
public class SequenceList<T> implements Iterable<T>{
//存储元素的数组
private T[] elements;
//记录当前顺序表中的元素个数(表中实际元素个数)
private int N;
//构造方法
//空参构造:如果没指定长度,则默认数组长度为10
public SequenceList(){
this(10);
}
public SequenceList(int capacity){
//初始化数组,中间强转Object类型为T泛型
this.elements = (T [])new Object[capacity];
//如果是int类型,可以直接将Object改为int,是String类型就将Object改为String
//初始化长度,N是元素个数
this.N = 0;
}
//-----------------------------------------------------------------------
//判断顺序表是否满了
public boolean isFull(){
//如果实际元素个数等于数组长度,则顺序表满了
if(N == elements.length){
return true;
}else {
return false;
}
}
//-----------------------------------------------------------------------
//清除顺序表中所有元素
public void clear(){
this.N = 0;
}
//-----------------------------------------------------------------------
//判断当前顺序表是否为空
public boolean isEmpty(){
return N == 0;
//为空返true,非空返false
}
//-----------------------------------------------------------------------
//获取顺序表的长度
public int length(){
return N;
}
//-----------------------------------------------------------------------
//获取指定位置处元素
public T get(int i){
return elements[i];
}
//-----------------------------------------------------------------------
//向顺序表中添加元素t,默认添加到顺序表末尾
public void add(T t){
//判断是否要扩容
if(N == elements.length){
resize(2*elements.length);
}
elements[N++] = t;
//N-1原本是最后一个元素的索引,经N++后最后一个索引即为N,添加了一个元素位置
}
//-----------------------------------------------------------------------
//向顺序表中指位置插入元素
public void insert(int i, T t){
//判断是否要扩容
if(N == elements.length){
resize(2*elements.length);
}
//先把i索引处的元素及其后面的元素依次向后移动一次
for(int index = N-1; index>=i; index--){
//index>=i是因为i位置的元素也要后移
elements[index+1] = elements[index];
}
elements[i] = t;
//实际元素个数也要增加一个
N++;
}
//-----------------------------------------------------------------------
//删除指定位置处的元素,并返回该元素
public T remove(int i){
//记录索引i处的值
T current = elements[i];
//索引i后面元素依次向前移动一位
for(int index = i; index<N-1; index++){
elements[index] = elements[index+1];
}
//同时实际元素个数要减少一个
N--;
//判断删除元素后,顺序表容量是否要收缩
if(N<elements.length/4){
resize(elements.length/2);
}
return current;
}
//-----------------------------------------------------------------------
//查找元素第一次出现的位置
public int indexOf(T t){
for(int i = 0; i<N; i++){
if(elements[i].equals(t)){
return i;
}
}
//返回-1表示没有找到
return -1;
}
//-----------------------------------------------------------------------
//查找元素,返回下标,返回值是一个集合
public ArrayList search (T key, int n){
ArrayList<Integer> list = new ArrayList<>();
//首先判断表是否为空
if(isEmpty()){
return null;
}
int position = -1;
for(int i = 0,m=0; i<this.N; i++){
if(elements[i] == key){
list.add(i);
}
}
return list;
}
//-----------------------------------------------------------------------
//根据参数重置elements大小(这个方法用于控制顺序表容量)
public void resize(int newSize){
//定义一个临时数组,指向原数组
T[] temp = elements;
//创建新数组
elements = (T[]) new Object[newSize];
//把原数组拷贝到新数组
for(int i = 0; i<N; i++){
elements[i] = temp[i];
}
}
//------------------------------------------------------------------------
//顺序表的遍历
//这里因为sequenceList类实现了接口Iterable,所以可以重写迭代器
@Override
public Iterator<T> iterator() {
//返回一个迭代器对象
return new SIterator();
}
//通过内部类实现Iterator接口,重写hasNext方法和Next方法
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 elements[cusor++];
}
}
}
测试类
public class SequenceListTest {
public static void main(String[] args) {
//创建顺序表对象
SequenceList<String> sl = new SequenceList<>(10);
//添加元素
sl.add("皮卡丘");
sl.add("冰墩墩");
sl.add("边牧");
sl.add("金毛");
//测试获取
for(int i = 0; i<sl.length(); i++) {
String getResult = sl.get(i);
System.out.println("获取索引" + i + "出的值为:" + getResult);
}
System.out.println("-------这是一条华丽的分割线-------");
//测试删除
System.out.println("删除索引2处的元素:"+ sl.remove(2));
for(int i = 0; i<sl.length(); i++) {
String getResult = sl.get(i);
System.out.println("获取索引" + i + "出的值为:" + getResult);
}
System.out.println("-------这是一条华丽的分割线-------");
//测试插入
sl.insert(1,"边牧");
sl.insert(2,"冰墩墩");
for(int i = 0; i<sl.length(); i++) {
String getResult = sl.get(i);
System.out.println("获取索引" + i + "出的值为:" + getResult);
}
System.out.println("-------这是一条华丽的分割线-------");
System.out.println(sl.search("冰墩墩",sl.length()));
System.out.println("-------这是一条华丽的分割线-------");
//测试顺序表迭代
System.out.println("顺序表迭代-------");
for(String s : sl){
System.out.println(s);
}
System.out.println("-------这是一条华丽的分割线-------");
//测试清空
sl.clear();
System.out.println( sl.isEmpty());
}
}
结果如下
获取索引0出的值为:皮卡丘
获取索引1出的值为:冰墩墩
获取索引2出的值为:边牧
获取索引3出的值为:金毛
-------这是一条华丽的分割线-------
删除索引2处的元素:边牧
获取索引0出的值为:皮卡丘
获取索引1出的值为:冰墩墩
获取索引2出的值为:金毛
-------这是一条华丽的分割线-------
获取索引0出的值为:皮卡丘
获取索引1出的值为:边牧
获取索引2出的值为:冰墩墩
获取索引3出的值为:冰墩墩
获取索引4出的值为:金毛
-------这是一条华丽的分割线-------
[2, 3]
-------这是一条华丽的分割线-------
顺序表迭代-------
皮卡丘
边牧
冰墩墩
冰墩墩
金毛
-------这是一条华丽的分割线-------
true
小贴士:
其实在数组扩容的那里也可以使用java.lang.System类中提供的arraycopy方法实现复制数组中的元素,如下:
接收一个数组对象,把这个数组对象的长度扩大到原来的2倍并返回
public int[] test(int[] a){
int[] b = new int[a.length*2];
System.arraycopy(a,0,b,0,a.length);
return b;
}