线性表的定义:线性表是n个类型相同的数据元素的有限序列。线性表中元素的个数N定义为线性表的长度。当N为0的时候定义为空表,在非空的线性表中每个数据元素在线性表中都有唯一确定的序号。
线性结构是最简单最常用的结构:线性结构的特点是:在数据元素的有限集中,除第一个元素无直接前驱,最后一个元素无直接后续外,每个元素有且仅有一个直接前驱和一个直接后续。
所以线性表和数组有点类似,线性表与数组之间的区别如下:
1.概念上来讲:首先线性表是一种抽象数据类型,数组是一种具体的数据结构
2.逻辑结构:线性表是元素之间具有1对1的线性关系的数据元素的集合,而数组是一组数据元素到数组下标的一一映射。
3.物理性质:数组中相邻元素是连续的存储在内存的,线性表只是一个抽象的数据数学结构,并没有具体的物理形式。
线性表的实现方式包括俩种:
- 线性表的顺序存储结构[基于数组的实现]
定义线性表顺序存储结构的接口:
package 线性表.sunxu;
/**
* 定义线性表中的相关操作
* @author acer
* 将数据的类型定义为Object类型,Object类式所有其他类的父类,因此其他任何类的引用或者任何类类型的变量都可以赋给Object类型的变量
* 这样实现的抽象数据类型对任何一种数据元素都适用。
*/
public interface List {
//返回线性表的大小,即数据元素的个数
public int getSize();
//如果线性表为空,那么返回true,否则返回false
public boolean isEmpty();
//判断线性表中是否包含元素E
public boolean contains(Object e);
//返回元素e在线性表中的序号
public int indexOf(Object e);
//将数据元素e插入到线性表中i号位置
public void insert(int i,Object e) throws OutOfBoundaryException;
//将元素e插入到元素obj之前
public boolean insertBefore(Object obj,Object e);
//将元素e插入到元素obj之后
public boolean insertAfter(Object obj,Object e);
//删除线性表中第i个元素,并将他返回
public Object remove(int i) throws OutOfBoundaryException;
//删除线性表中第一个与元素e相同的元素
public boolean remove(Object e);
//替换线性表中序号为i的数据元素为e,返回原数据元素
public Object replace(int i,Object e) throws OutOfBoundaryException;
//返回线性表中序号为i的元素
public Object get(int i) throws OutOfBoundaryException;
}
定义异常:
package 线性表.sunxu;
/**
* 定义异常
* @author acer
*/
public class OutOfBoundaryException extends Exception {
private static final long serialVersionUID = 1L;
public OutOfBoundaryException(String err){
super(err);
}
}
定义比较策略:
package 线性表.sunxu; /** * 将所有的数据类型都使用Object类来代替,那么在对俩个数据进行比较的时候便出现了不变,所有数据种类的比较就都需要使用Object类型的变量来完成。 * 但是不同的数据类型的比较是不同的,为此我们引入strategy接口,实现了这个接口就可以实现不同的比较策略 * @author acer */ public interface Strategy { public boolean equals(Object obj1,Object obj2); public int compare(Object obj1,Object obj2); } 线性表的实现:
package 线性表.sunxu; /** * 线性表的顺序存储与实现 * @author acer * 1.使用一组地址连续的存储单元依次存储线性表的数据元素,假设数据表中的每个数据元素需要占用K个存储单元,并以元素所占的第一个存储单元的地址作为数据元素的存储地址。 * 那么很容易得出第i个与第i+1个数据元素存储地址之间的关系。所以只要确定了线性表的起始地址,线性表中的任何一个数据元素都可以随机存取。因此线性表的顺序存储结构是一种 * 随机的存储结构。 */ public class ListArray implements List{ private final int LEN = 8;//数组的默认大小 private Strategy strategy;//数据元素比较策略 private int size;//线性表中数据元素的个数 private Object[] elements;//数据元素数组 /** * 构造方法 */ public ListArray(){ this(new DefaultStrategy()); } public ListArray(Strategy strategy) { this.strategy = strategy; this.size = 0; elements = new Object[LEN]; } @Override public int getSize() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public boolean contains(Object e) { for(int i=0;i<size;i++) { if(strategy.equals(elements[i], e)){ return true; } } return false; } @Override public int indexOf(Object e) { for(int i=0;i<size;i++) { if(strategy.equals(elements[i], e)){ return i; } } return -1; } @Override public void insert(int i, Object e) throws OutOfBoundaryException { if(i<0 || i>size) throw new OutOfBoundaryException("超出线性表边界异常"); if(size >= elements.length) {//如果系统需要扩容 expandSpace(); } for(int j=size;j>i;j--){ elements[j] = elements[j-1]; } elements[i] = e; size ++; } //进行扩容 public void expandSpace(){ Object a [] = new Object[elements.length*2]; for(int i=0;i<size;i++) { a[i] = elements[i]; } elements = a;//旧数组失去引用,将要被回收 } @Override public boolean insertBefore(Object obj, Object e) { int i = this.indexOf(obj); if(i < 0) { return false; } try { this.insert(i, e); } catch (OutOfBoundaryException e1) { e1.printStackTrace(); } return true; } @Override public boolean insertAfter(Object obj, Object e) { int i = this.indexOf(obj); if(i < 0) { return false; } try { this.insert(i+1, e); } catch (OutOfBoundaryException e1) { e1.printStackTrace(); } return true; } @Override public Object remove(int i) throws OutOfBoundaryException { if(i<0 || i>size) throw new OutOfBoundaryException("超出范围"); for(int j = i ;j<size;j++){ elements[j] = elements[j+1]; } elements[--size] = null; this.size --; return elements[i]; } @Override public boolean remove(Object e) { int i = this.indexOf(e); try { this.remove(i); } catch (OutOfBoundaryException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return true; } @Override public Object replace(int i, Object e) throws OutOfBoundaryException { if(i<0 || i>size) throw new OutOfBoundaryException("超出范围"); Object o = elements[i]; elements[i] = e; return o; } @Override public Object get(int i) throws OutOfBoundaryException { if(i<0 || i>size) throw new OutOfBoundaryException("超出范围"); return elements[i]; } } class DefaultStrategy implements Strategy{ @Override public boolean equals(Object obj1, Object obj2) { return false; } @Override public int compare(Object obj1, Object obj2) { return obj1.toString().compareTo(obj2.toString()); } }
- 线性表的链式存储实现
用指针将存储线性表中数据元素的那些单元依次串联在一起。这种方法避免了在数组中连续用存储单元存储元素的缺点,但是需要在每个单元中设置指针来表示表中元素之间的逻辑关系。
单链表【single linked list】:除了尾节点外每个节点都只有一个指向后续节点的指针【java中使用对象】:优点:结构简单
双向链表:单链表有一个缺点,只能访问后续节点,无法访问前驱节点,要在单链表中找到某个节点的前驱节点,必须从链表的首节点开始找。
package 线性表.linkedlist;
/**
* 定义线性表的节点
* @author acer
*/
public interface Node {
public Object getData();//获取节点数据域
public void setData(Object o);//设置节点数据域
}
package 线性表.linkedlist;
/**
* 单链表的节点定义
* @author acer
* 单链表的节点是一个标准的javabean结构
*/
public class SLNode implements Node{
private Object data;//定义数据域
private SLNode next;//定义指针域
public SLNode(){
}
public SLNode(Object o,SLNode n) {
this.data = o;
this.next = n;
}
public SLNode getNext() {
return next;
}
public void setNext(SLNode next) {
this.next = next;
}
@Override
public Object getData() {
// TODO Auto-generated method stub
return data;
}
@Override
public void setData(Object o) {
this.data = o;
}
}
package 线性表.linkedlist;
import 线性表.sunxu.*;
public class ListSLinked{
private Strategy strategy;//定义数据元素的比较策略
private SLNode head;//单列表首节点的引用
private int size; //线性表中数据元素的个数
public ListSLinked() {
new ListSLinked(new StudentStrategy());
}
public ListSLinked(Strategy strategy) {
this.strategy = strategy;
head = new SLNode();
size = 0;
}
//添加部分工具方法
//获取数据元素e所在节点的前驱节点
private SLNode getPreNode(Object e) {
SLNode p = head;
while(p.getNext() != null) {
if(strategy.equals(p.getNext().getData(), e))
return p;
else
p = p.getNext();
}
return null;
}
//获取序号为i的元素所在节点的前驱节点
private SLNode getPreNode(int i) {
SLNode p = head;
for(int j=0;j<i;j++) {
p = p.getNext();
}
return p;
}
//获取序号为i的元素所在的节点
public SLNode getNode(int i) {
return this.getPreNode(i-1).getNext();
}
//返回线性表的大小,即线性表中数据元素的个数
public int getSize(){
return size;
}
//判断线性表是否为空
public boolean isEmpty(){
return size == 0;
}
//判断线性表中是否包含数据元素e
public boolean contains(Object e) {
SLNode p = head;
while(p != null){
if(strategy.equals(p.getData(),e))
return true;
p = p.getNext();
}
return false;
}
//返回数据元素e所在线性表中的序号,如果没有找到则返回-1
public int indexOf(Object e) {
SLNode p = head;
int index = 0;
while(p != null){
if(strategy.equals(p.getData(),e))
return index;
p = p.getNext();
index++;
}
return -1;
}
//将数据元素e插入到线性表中i号的位置
public boolean insert(int i,Object o) throws OutOfBoundaryException {
if(i<0 || i>size){
return false;
}
SLNode p = getPreNode(i);
SLNode node = new SLNode(o,p.getNext());
p.setNext(node);
size++;
return true;
}
//将数据元素e插入到元素obj之后
public boolean insertAfter(Object obj,Object e) throws OutOfBoundaryException{
int i = this.indexOf(obj);
boolean b = insert(i+1,e);
return b;
}
//将数据元素e插入到元素obj之前
public boolean insertBefore(Object obj,Object e) throws OutOfBoundaryException{
int i = this.indexOf(obj);
boolean b = insert(i,e);
return b;
}
//删除线性表中序号为i的元素,并返回之
public Object remove(int i) throws OutOfBoundaryException{
if(i<0 || i>size) {
throw new OutOfBoundaryException("超出边界了");
}
SLNode node = this.getNode(i);
SLNode n = node.getNext();
SLNode p = this.getPreNode(i);
p.setNext(n);
size --;
return node.getData();
}
//删除线性表中第一个与e相同的元素
public boolean remove(Object o) throws OutOfBoundaryException {
int i = this.indexOf(o);
Object e = this.remove(i);
if(e != null)
return true;
return false;
}
//替换线性表中序号为i的数据元素为e,返回原数据元素
public Object replace(int i,Object e) throws OutOfBoundaryException{
if(i<0 || i>size)
throw new OutOfBoundaryException("超出边界了");
else {
SLNode n = this.getNode(i);
Object o = n.getData();
n.setData(e);
return o;
}
}
//返回线性表中序号为i的元素
public Object get(int i) throws OutOfBoundaryException{
if(i<0 || i>size)
throw new OutOfBoundaryException("超出边界了");
return this.getNode(i).getData();
}
}
这就是线性表的链式存储结构实现。
这俩种实现方法的比较:线性表的操作主要包括:查找、插入、删除三类操作
1、基于时间的比较:如果线性表的主要操作为插入,删除操作,那么使用链式存储的线性表性能最好
2、基于空间的比较:顺序存储结构中,其存储空间是预先静态分配的,虽然在实现过程中可以动态扩展数组空间,如果线性表的长度范围变化较大,空间在使用过程中会存在大量的空闲空间使得空间的存储利用率不高。而线性表的链式存储结构完全相反,所以如果线性表的长度变化较大时,宜使用链式存储结构。