0.线性表的定义
线性表又称线性存储结构,是最简单的一种存储结构,专门用来存储逻辑关系为“一对一”的数据。
在一个数据集中,如果每个数据的左侧都有且仅有一个数据和它有关系,数据的右侧也有且仅有一个数据和它有关系,那么这些数据之间就是“一对一“的逻辑关系。
举个简单的例子:
如上图所示,在 {1,2,3,4,5} 数据集中,每个数据的左侧都有且仅有一个数据和它紧挨着(除 1 外),右侧也有且仅有一个数据和它紧挨着(除 5 外),这些数据之间就是“一对一“的关系。
使用线性表存储具有“一对一“逻辑关系的数据,不仅可以将所有数据存储到内存中,还可以将“一对一”的逻辑关系也存储到内存中。
线性表存储数据的方案可以这样来理解,先用一根线将所有数据按照先后次序“串”起来,如下图所示:
图 2 中,左侧是“串”起来的数据,右侧是空闲的物理空间。将这“一串儿”数据存放到物理空间中,有以下两种方法:
两种存储方式都可以将数据之间的关系存储起来,从线的一头开始捋,可以依次找到每个数据,且数据的前后位置没有发生改变。
像图 3 这样,用一根线将具有“一对一”逻辑关系的数据存储起来,这样的存储方式就称为线性表或者线性存储结构。
0.1线性表的顺序存储和链式存储
从图 3 不难看出,线性表存储数据的实现方案有两种,分别是:
- 像图 3a) 那样,不破坏数据的前后次序,将它们连续存储在内存空间中,这样的存储方案称为顺序存储结构(简称顺序表);
- 像图 3b) 那样,将所有数据分散存储在内存中,数据之间的逻辑关系全靠“一根线”维系,这样的存储方案称为链式存储结构(简称链表)。
也就是说,使用线性表存储数据,有两种真正可以落地的存储方案,分别是顺序表和链表。
0.2前驱和后继
在具有“一对一“逻辑关系的数据集中,每个个体习惯称为数据元素(简称元素)。例如,图 1 显示的这组数据集中,一共有 5 个元素,分别是 1、2、3、4 和 5。
此外,很多教程中喜欢用前驱和后继来描述元素之间的前后次序:
- 某一元素的左侧相邻元素称为该元素的“直接前驱”,此元素左侧的所有元素统称为该元素的“前驱元素”;
- 某一元素的右侧相邻元素称为该元素的“直接后继”,此元素右侧的所有元素统称为该元素的“后继元素”;
以图 1 数据中的元素 3 来说,它的直接前驱是 2 ,此元素的前驱元素有 2 个,分别是 1 和 2;同理,此元素的直接后继是 4 ,后继元素也有 2 个,分别是 4 和 5。
1.使用数组实现顺序表
算法:描述解决问题的步骤,在计算机上描述解决问题的步骤,需要使用流程控制
数组:一段连续的存储空间
线性表:线性表是数据结构的一种,数据结构是数据元素之间的关系,线性表中数据
元素的关系就是线性表的定义,数据元素的上层是数据对象,数据对象是数据元素相
同的数据元素的集合,线性表的抽象数据类型就是在线性表上加上一些操作例如:增删
改查等其它操作。理解线性表的概念,其实数组就是一个线性表
2.顺序表的API设计
- 线性表实现Iterable接口,重写iterator方法
- 创建内部类SIterator实现Iterator接口,重写hasNext()方法和next()方法
- 线性表长度等于数组长度扩容扩2倍
- 线性表长度小于数组长度的四分之一缩容二分之一
import java.util.Iterator;
public class SequenceList<T extends Comparable<T>> implements Iterable{
T[] ele;
int len;
SequenceList(){
ele = (T[]) new Comparable[16];
this.len = 0;
}
SequenceList(int capacity){
ele = (T[]) new Comparable[capacity];
this.len = 0;
}
//清空线性表
public void clear(){
ele = null;
this.len = 0;
}
//线性表是否为空
public boolean isEmpty(){
return this.len == 0;
}
//线性表的长度
public int size(){
return this.len;
}
//获取指定索引处的元素
public T get(int i){
return ele[i];
}
//在数组末尾插入元素
public void insert(T t){
if(len == ele.length){
resize(ele.length*2);
}
ele[len++] = t;
}
//在数组指定索引处,插入元素
public void insert(int index,T t){
if(len == ele.length){
resize(ele.length*2);
}
for(int i = len-1; i >= index; i--){
ele[i+1] = ele[i];
}
ele[index] = t;
this.len++;
}
//删除指定索引出的元素,并返回索引处的元素
public T remove(int index){
if(len<ele.length/4){
resize(ele.length/2);
}
T result = ele[index];
for(int i =index+1; i < len; i++){
ele[i-1] = ele[i];
}
this.len--;
return result;
}
//查找元素所在的索引
public int indexOf(T t){
for (int i = 0; i < len; i++){
if(t == ele[i]){
return i;
}
}
return -1;
}
//扩容、缩容
public void resize(int capacity){
Comparable[] temp = ele;
ele = (T[]) new Comparable[capacity];
for (int i = 0; i < temp.length; i++) {
ele[i] = (T) temp[i];
}
}
//迭代(遍历)
@Override
public Iterator iterator() {
return new SIterator();
}
private class SIterator implements Iterator{
int n;
SIterator(){
n = 0;
}
@Override
public boolean hasNext() {
return n<len;
}
@Override
public Object next() {
return ele[n++];
}
}
}
测试:
public class SequenceListTest {
public static void main(String[] args) {
SequenceList<String> list = new SequenceList<>();
list.insert("k");
list.insert("l");
list.insert("z");
list.insert("h");
System.out.println(list.size());
System.out.println("----------------------");
for (Object o : list) {
System.out.println(o);
}
System.out.println("-----------------------");
list.insert(3,"w");
for (Object o : list) {
System.out.println(o);
}
System.out.println("------------------------");
System.out.println(list.get(2));
System.out.println(list.remove(2));
System.out.println("-------------------------");
for (Object o : list) {
System.out.println(o);
}
}
}
4
----------------------
k
l
z
h
-----------------------
k
l
z
w
h
------------------------
z
z
-------------------------
k
l
w
h
时间复杂度:
不同的操作有不同的时间复杂度
get方法的时间复杂度为1
insert(int i, T t):时间复杂度为n
insert(T t):时间复杂度为1
remove(int i): 时间复杂度为n
indexOf(T t):时间复杂度为n