先搞懂一些概念:
逻辑结构
在数学模型中,数据对象中,数据元素之间的相互关系有这几种。
- 集合结构
元素之间,不存在前后左右的关系。属于最简单的关系。 - 线性结构
1对1的结构。
每个元素存在唯一的前驱,和后继。从图中看,前驱就是当前元素所连接的上一个元素,后继就是当前元素所连接的下一个元素。 - 树形结构
1对多的结构。
举例:文件系统,每一个文件夹下都有很多子文件夹,子文件夹又有子文件,子子孙孙无穷匮也。树形结构里,每个元素有兄弟元素,父亲元素,和子元素。以后再解释。 - 图形结构
多对多的结构。
物理结构
顺序存储结构
一个挨着一个,
O-O-O-O-O-O
查询快,插入删除都很慢。因为插入一个元素,在当前插入的位置之后的所有元素都要后移,大家一起动一定很慢。而查询时,只需要指针下移就可以找到下一个元素,不必去寻址。
链式存储结构
通过地址,实现。下面的图片,0x000*是内存中的地址,每一个O代编一个元素。
可见,连式存储结构在内存中,不一定是连续的。
查询慢,插入删除都很快。因为查询时,要根据地址去寻找下一个元素,所以慢。而插入删除时,都是链的操作,断链加链即可。
链表还有很多实现,单链表,最普通的链表:
比如循环链表:即链表最后一个元素再指向第一个元素。
双向链表:
双向循环链表:
自己思考。
线性结构的特点:
由上面的介绍,我们可以大致推断出线性结构的特点:
在数据元素的非空有限集合中:
- 存在唯一的一个被称作“第一个”的数据元素
- 存在唯一的一个被称作“最后一个”的数据元素
- 除第一个外,集合中的每个数据元素均只有一个前驱
- 除最后一个外,集合中的每个数据元素均只有一个后继
链式存储和顺序存储都是线性结构。
那么线性表的定义就是:
一个线性表是n个数据元素的有限序列
如:(a1,a2,a3,……ai……an)
例1 英文字母表(A,B,C,……,Z)是一个线性表
例2 你刚开学就发来的补考通知
所以归纳出线性表特征,用专业的数学用语表示就是:
元素个数n(n≥0) 称为表长度,n=0空表,记为()或φ
1 < i < n 时
ai的直接前驱是ai-1,a1无直接前驱
ai的直接后继是ai+1,an无直接后继
元素同构(属于同一数据对象)
那么怎么定义一个线性表,线性表在代码中具体是什么样的?
实现线性表
ADT List{
数据对象;//即我们线性表存储的对象是啥,什么类型的。
/**
* 这里有些抽象,
* R1={ <ai-1 ,ai >|ai-1 ,ai∈D, i=2,...,n }
* { 设线性表为 (a1,a2, . . . ,ai,. . . ,an),
* 称 i 为 ai 在线性表中的位序。}
*我们可以直接理解为,我们要以什么方式存储我们的数据对象。比如说数组。
*/
数据关系;
/**
*如果我们以数组存储我们的数据对象,
*这里的操作就是初始化一个数组
*/
结构初始化操作;
结构销毁操作;//销毁我们的此线性表
引用型操作;//返回一个前驱,一个后继,一个线性表长度等等操作
加工型操作;//插入删除清空等
//还可以根据自己实际问题的需要,加一些自己需要的方法。
}//ADT List
都说了数据结构就是为算法服务的,线性表只是其中的一种结构而已,在算法中,某种模型可能被定义为线性表,此时根据需要分析,确定是定义为顺序的还是链式的,如果是链式,是单链的还是双链的还是循环的等等。定义完模型后,然后再去利用模型解决问题,会很方便。
一个Java实现的简单的线性表 (数组实现)
这个demo是顺序存储结构实现的线性表。非链表。
/**
* Created by WenCh on 2017/4/22.
*/
public class mArrayList<E> {
private int size;//元素个数
private Object[] array;//存放元素的数组
private static final int MIN_CAPACITY_INCREATMWNT = 12;//默认最小增长长度的temp
public mArrayList(int s) {
if (s < 0) {
throw new IllegalArgumentException("exp: can not < 0");
}
array = new Object[s];
}
public mArrayList() {
array = new Object[0];
}
public mArrayList(Collection<? extends E> collection) {
Object[] a = collection.toArray();
if (a.getClass() != Object[].class) {//Object[].class得到的是[]里面的类型
Object[] newArray = new Object[a.length];
System.arraycopy(a, 0, newArray, 0, a.length);
a = newArray;
}
array = a;
size = a.length;
}
/**
* 扩容
*
* @param currentCapacity 当前容量
* @return 扩完之后总容量
*/
private static int newCapacity(int currentCapacity) {
int increatment = (currentCapacity > MIN_CAPACITY_INCREATMWNT / 2) ? currentCapacity >> 1 : MIN_CAPACITY_INCREATMWNT;
return increatment + currentCapacity;
}
/**
* 增加
*
* @param onject 添加的新元素
* @return true:添加成功;false:执行不到。
*/
public boolean add(E onject) {
Object[] a = array;//操作引用a,防止多线程程序crash
int s = size;
if (s == a.length) {
Object[] newArray = new Object[newCapacity(s)];
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
a[s] = onject;
size++;
return true;
}
/**
* 得到元素个数
*
* @return size
*/
public int size() {
return size;
}
/**
* 是否为空list 即元素个数是否为0
*
* @return true:空;false:不为空。
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 查找元素首次出现的下标
*
* @param object 欲查找的元素
* @return 第一次出现的下标
*/
public int indexOf(Object object) {
Object[] a = array;
int s = size;
if (object.getClass() != Object[].class)//如果二笔瞎传参数,所以建议参数类型为E,让系统帮我们异常。不知可否。
throw new ClassCastException("can not cast yout class to" + Object[].class);
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i]))
return i;
}
} else {
for (int i = 0; i < s; i++) {
if (null == a[i])
return i;
}
}
return -1;
}
/**
* 查找元素最后出现的下标
*
* @param object 欲查找的元素
* @return 最后出现的下标
*/
public int lastIndexOf(Object object) {
Object[] a = array;
int s = size;
if (object.getClass() != Object[].class)//如果二笔瞎传参数,所以建议直接将参数类型为E,让系统帮我们异常。不知可否。
throw new ClassCastException("can not cast yout class to" + Object[].class);
if (object != null) {
for (int i = s - 1; i >= 0; i--) {
if (object.equals(a[i]))
return i;
}
} else {
for (int i = s - 1; i >= 0; i--) {
if (null == a[i])
return i;
}
}
return -1;
}
/* *//**
* 查找元素最后出现的下标
*
* @param object 欲查找的元素
* @return 最后出现的下标
*//*
public int lastIndexOf(E object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = s - 1; i >= 0; i--) {
if (object.equals(a[i]))
return i;
}
} else {
for (int i = s - 1; i >= 0; i--) {
if (null == a[i])
return i;
}
}
return -1;
}*/
/**
* 根据下标删除元素
*
* @param index 欲删除元素的下表
* @return 此元素
*/
public E remove(int index) {
Object[] a = array;
int s = size;
/*
这个判断一定要加上=,因为传过来的是下标,而不是元素个数。
没错,即便不加上=也不会报错,当参数传进来的数为size时,返回来的是null,因为永远没有size=lenght的情况。
但是,不要忘了我们传进来的参数的意义是下标不是个数,下标加1才是当前的个数。
所以当传进来的数为size时,翻译成语言就成了:
-------------------------------
删除第size+1个元素。
-------------------------------
很显然不对,一共只有size个元素,怎么可能有size+1个元素呢?所以程序不crash,是不对的,应该crash才对。
当传来的参数的数正好是size的时候,应该报异常,因为这时我们要删除的是第size+1个元素,而我们只有size个元素。
重复:下标加1才是当前的个数,我们传过来的参数的意义是下标。
而且根据规范,理应加上<0的判断。
没错,如果传过来负数,证明传数的那个人是2b。
但是我记得老师教过我们,编程就是要把用你程序的人当成2b。这样才严谨。
反正老师说的大概就是这个意思罢:
万一真有个2b这么做怎么办?我们要事先帮他想好他所做的2b的事情。
*/
if (index >= s || index < 0)
throw new IndexOutOfBoundsException("out of bound,the number of object in array is:" + s);
E e = (E) a[index];//此行代码一定要在下行代码之前,因为下行代码把当前这玩意删了
System.arraycopy(a, index + 1, a, index, --s - index);//把欲删除的下标后面的元素拷贝到当前下标及以后,当前元素则被覆盖(即删除)。
a[s] = null;//拷贝完,最后一个元素还在原来的位置有一个原来的像。所以最后一个元素再置为null
size = s;//s之前已经--了,所以此处不用--
return e;
}
/**
* 根据元素删除list中所有元素
*
* @param object 欲删除的元素
* @return 此元素
*/
public E remove(Object object) {
if (object.getClass() != Object[].class)
throw new ClassCastException("2b,can not cast your class to " + Object[].class);
int s = size;
int currentFirstIndex = indexOf(object);
if (currentFirstIndex == -1)//找不到元素
return null;
int currentLastIndex = lastIndexOf(object);
if (currentFirstIndex != currentLastIndex) {//说明元素出现>1次
Object[] a = array;
for (int i = currentFirstIndex; i <= currentLastIndex; i++) {
if (a[i].equals(object))
remove(i);
}
} else {//元素只出现1次
remove(currentFirstIndex);
}
return (E) object;
}
/**
* 根据下标获取元素
*
* @param index 欲获取元素的下标
* @return 欲获取的元素
*/
public E get(int index) {
Object[] a = array;
if (index >= size || index < 0)
throw new IndexOutOfBoundsException("out of bound,the number of objects in array is " + size);
E e = (E) a[index];
return e;
}
}
等复习队列的时候,再写一个Linked的。链式。