线性表的基本特征:java
线性表按物理存储结构的不一样可分为顺序表(顺序存储)和链表(链式存储):算法
顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构。线性表采用顺序存储的方式存储就称之为顺序表。编程
插入删除操做如图:数组
抽象数据类型(ADT)是指一个数学模型及定义在该模型上的一组操做。于Java语言中的抽象类和接口设计理念是想通的。安全
abstract class SequenceListAbst{ //顺序表 private static final int DEFAULT_CAPACITY=10; private int size; private Object[] elements; //Object数组 public SequenceListAbst(){ size=0; elements=new Object[DEFAULT_CAPACITY]; } //顺序表大小 public abstract int size(); //判断是不是空 public abstract boolean isEmpty(); //清空顺序表 public abstract void clear(); //在index处添加元素 public abstract void add(int index, Object element); //删除指定索引的元素 public abstract boolean delete(int index); //获取指定索引的元素 public abstract Object get(int index); //遍历链表 public abstract void iterator(); }
具体的代码省略...数据结构
顺序表效率分析:dom
顺序表的优缺点:编程语言
顺序表适合元素个数变化不大,且更可能是读取数据的场合。ide
扩展:性能
Java中AarrayList是系统实现的顺序表,它是一个动态数组。添加时会有扩容,删除时会有缩容。
扩容:经过无参构造的话,初始数组容量根据JDK版本不一样策略不一样,每次经过copeOf的方式扩容后容量为原来的1.5倍。
缩容:ArrayList不会自动缩小容积,有一个方法 trimToSize 能够缩小容积。
其余描述:
ArrayList是基于数组实现的,是一个动态数组,其容量能自动增加。 ArrayList不是线程安全的,只能用在单线程环境下。 实现了Serializable接口,所以它支持序列化,可以经过序列化传输; 实现了RandomAccess接口,支持快速随机访问,实际上就是经过下标序号进行快速访问; 实现了Cloneable接口,能被克隆。
链表(LinkedList)一般由一连串节点组成,每一个节点包含任意的实例数据(data fields)和一或两个用来指向上一个/或下一个节点的位置的连接("links")
链表是一种常见的基础数据结构,是一种线性表,可是并不会按线性的顺序存储数据,而是在每个节点里存放指向下一个节点的指针(Pointer),Java中称之为引用。
使用链表结构能够克服数组链表须要预先知道数据大小的缺点,链表结构能够充分利用计算机内存空间,实现灵活的内存动态管理。可是链表失去了数组随机读取的优势,同时链表因为增长告终点的指针域,空间开销比较大。
(1)单链表是链表中结构最简单的。一个单链表的节点(Node)分为两个部分,第一个部分(data)保存或者显示关于节点的信息,另外一个部分存储下一个节点的地址。最后一个节点存储地址的部分指向空值。
单链表有带头结点和不带头结点两种结构,其结构以下
因为带头结点的链表更容易操做,这里仅实现带头结点的单链表
带头结点的链表插入与删除示意图:
抽象数据类型(ADT)是指一个数学模型及定义在该模型上的一组操做。于Java语言中的抽象类和接口设计理念是想通的。
abstract class SingleLinkedListAbst{ //单链表 protected int size; //链表节点的个数 protected Node head; //头节点 //链表的每一个节点类 protected class Node{ //内部类 protected Object data; //每一个节点的数据 protected Node next; //每一个节点指向下一个节点的引用 public Node(Object data){ this.data=data; } } //单链表的大小 public abstract int size(); //判断链表是否为空 public abstract boolean isEmpty(); //在链表index处添加元素 public abstract void add(int index, Object element); //删除指定索引的元素 public abstract boolean delete(int index); //判断元素是否存在 public abstract boolean exist(Object obj); //查找元素,根据索引index返回节点Node public abstract Node get(int index); //遍历链表 public abstract void print(); }
具体的代码:
class SingleLinkedList extends SingleLinkedListAbst{ //实现单链表 public SingleLinkedList(){ //构造方法初始化一个头结点 head=new Node("head"); head.next=null; size=0; } @Override public int size() { return size; } @Override public boolean isEmpty() { return size==0; } @Override public void add(int index, Object element) { if(index<0 || index>size){ throw new IndexOutOfBoundsException("参数输入错误:"+index); } //找到索引index结点以前的结点 Node before=head; int temp=index; while(temp-->0){ before=before.next; } //构造新的待插入结点 Node newNode=new Node(element); //若索引index处的结点不存在,就在最后插入 if(index==(size+1)){ before=newNode; newNode.next=null; size++; return; } //插入结点(断开旧引用,构造新的引用) Node after=before.next; before.next=newNode; newNode.next=after; size++; } @Override public boolean delete(int index) { // TODO Auto-generated method stub return false; } @Override public boolean exist(Object obj) { // TODO Auto-generated method stub return false; } @Override public Node get(int index) { // TODO Auto-generated method stub return null; } @Override public void print() { Node currentNode=head.next; if(currentNode==null){ return; } int temp=size; while(temp--!=0){ //循环 System.out.println((String)currentNode.data); currentNode=currentNode.next; } } }
此处因为时间缘由做者只写了一部分,如是想提高能力的读者必然是所有写完。
单链表效率分析:
在单链表上插入和删除数据时,首先须要找出插入或删除元素的位置。对于单链表其查找操做的时间复杂度为 O(n),因此
链表插入和删除操做的时间复杂度均为 O(n)
链表读取操做的时间复杂度为 O(n)
单链表优缺点:
(2)单向循环链表
将单链表中终端结点的指针指向头结点,使整个单链表造成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。
对于循环链表,为了使空链表与非空链表处理一致,一般设一个头结点。如图:
循环链表和单链表的主要差别在于链表结束的判断条件不一样,单链表为current.next
是否为空,而循环链表为current.next
不等于头结点。对于循环链表的增删改查操做与单链表基本相同,仅仅须要将链表结束的条件变成current.next != head
便可。
(3)双向链表
双向链表是在单链表的每一个结点中,再设置一个指向其前驱结点的指针域。使得两个指针域一个指向其前驱结点,一个指向其后继结点。
对于双向链表,其空和非空结构以下图:
双向链表是单链表扩展出来的结构,它能够反向遍历、查找元素,它的不少操做和单链表相同,好比求长度size()、查找元素get()。这些操做只涉及一个方向的指针便可。插入和删除操做时,须要更改两个指针变量。
插入操做:注意操做顺序
双向链表相对于单链表来讲占用了更多的空间,但因为其良好的对称性,使得可以方便的访问某个结点的先后结点,提升了算法的时间性能。是用空间换时间的一个典型应用。
不少代码的实现没有写完,如是想提高能力的读者必然是所有写完(附:对于数据结构初学者(已熟练驾驭编程语言自己),链表的编写必然是熬三天三夜才能搞清楚脉络的)。
舒适提示:看的懂和能写出来绝对是两回事!