线性结构的特点:
在数据元素的非空有限集合中
1、存在唯一一个被称为“第一个”的数据元素,
2、存在唯一一个被称作“最后一个”的数据元素,
3、除第一个之外,集合中的每一个数据元素均只有一个前驱,
4、除最后一个之外,集合中的每一个数据元素均只有一个后继
线性表:是n个数据元素的有限序列。
线性表的顺序表示:
用一组地址连续的存储单元依次存储线性表的数据元素。
用数组实现具体参照jdk源码ArrayList
package java.util;
public class SeriesList<E> {
private Object[] elements;
//存储数据个数
private int size;
//修改次数
protected transient int modCount = 0;
public SeriesList(int length) {
elements = new Object[length];
}
private SeriesList() {
this(10);
}
/**
* 通过索引获取
* @param i
* @return
*/
public E get(int i) {
rangeCheck(i);
return (E) elements[i];
}
/**
* 添加
* @param e
*/
public boolean add(E e) {
ensureCapacity(size+1);
elements[size++]=e;
return true;
}
/**
* 删除
*/
public E remove(int i){
rangeCheck(i);
modCount++;
E oldValue = (E) elements[i];
int numMoved = size - i - 1;
if (numMoved > 0)
System.arraycopy(elements, i+1, elements, i,
numMoved);
elements[--size] = null; // Let gc do its work
return oldValue;
}
/**
* 清空
*/
public void clear() {
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elements[i] = null;
size = 0;
}
/**
* 确保数组的长度足够保存相关数据
* @param minCapacity
*/
private void ensureCapacity(int minCapacity) {
modCount++;
int oldLength = elements.length;
if(oldLength<minCapacity) {
int newLength = oldLength*3/2+1;
if(newLength<minCapacity) {
newLength = minCapacity;
}
elements = Arrays.copyOf(elements, newLength);
}
}
/**
* 检查角标越界
* @param i
*/
private void rangeCheck(int i) {
if( i>=size ) {
throw new IndexOutOfBoundsException("Index:"+i+",Size:"+size);
}
}
}
线性表的链式表示及实现
线性链表
用一组任意的存储单元来存储数据元素 , 不要求物理存储单元的连续性,由一系列结点组成,每个结点除了要存储数据外,还需存储指向后继结点或前驱结点的存储地址。
线性表的链式实现:
具体可以参照LinkedList双向链表
package java.util;
public class LinkedList<E> {
private transient int size = 0;
private transient Entry header = new Entry<E>(null, null, null);
private Entry<E> next;
public LinkedList () {
header.next=header.pre=header;
}
/**
* 添加
* @param e
*/
public void add(E e) {
addBefore(e,header);
}
/**
* 按索引查找
* @return
*/
public E get(int index) {
return entry(index).element;
}
/**
* 查找数据节点
* @return
*/
private Entry<E> entry(int i){
if(i<0||i>=size) {
throw new IndexOutOfBoundsException("Index: "+i+
", Size: "+size);
}
Entry<E> e = header;
if (i < (size >> 1)) {
for (int j = 0; j <= i; j++)
e = e.next;
} else {
for (int j = size; j > i; j--)
e = e.pre;
}
return e;
}
/**
* 删除
* @param index
* @return
*/
public E remove(int index) {
return remove(entry(index));
}
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.pre.next = e.next;
e.next.pre = e.pre;
e.next = e.pre = null;
e.element = null;
size--;
return result;
}
public Entry addBefore(E e, Entry<E> entry) {
Entry newEntry = new Entry<E>(e, entry.pre, entry);
newEntry.next.pre=newEntry;
newEntry.pre.next=newEntry;
size++;
return newEntry;
}
private static class Entry<E> {
E element;
Entry<E> pre;
Entry<E> next;
public Entry(E element,Entry<E> pre,Entry<E> next){
this.element = element;
this.pre = pre;
this.next = next;
}
}
}
顺序存储和链式存储对比
顺序存储结构
优点
实现比较简单
查找指定位置的元素效率很快,时间复杂度为常数阶O(1)
无需额外存储元素之间的逻辑关系(链式存储由于存储空间随机分配,需要存储元素之间的逻辑关系)
缺点
需要预先分配存储空间,如果数据元素数量变化较大,很难确定存储容量,并导致空间浪费
若频繁进行插入删除操作,则可能需要频繁移动大量数据元素
链式存储结构
优点
不需要提前分配存储空间,元素个数不受限制
对于插入删除操作,在已找到目标位置前提下,效率很高,仅需处理元素之间的引用关系,时间复杂度为O(1)
缺点
实现相对复杂
查找效率较低,最坏情况下需要遍历整张表
由于物理存储位置不固定,需要额外存储数据元素之间的逻辑关系
循环链表 特点:
表中最后一个结点的指针域指向头结点,整个链表形成一个环。从表中任一结点出发均可找到表中其他结点。
双向链表:
在双向链表的结点中有两个指针域,其一指向直接后继,另一指向直接前驱。
双向链表的的每一个结点,包含两个指针域,一个指向它的前驱结点,一个指向它的后继结点。