终于进入正题了,这一章介绍线性表,既然说到线性表,就要先谈一下什么叫线性,线性结构主要有以下几个特点:
- 存在唯一的一个被称作第一个的元素
- 存在唯一的一个被称作最后一个的数据元素
- 除第一个之外,集合中的每个数据元素均只有一个前驱
- 除最后一个之外,集合中每个数据元素均只有一个后续
很简单对吧,想象之前排队的例子就知道啦。
好,下面就开始代码实现咯。线性表在具体实现中又分为顺序表示和链式表示,而链式表示中又分为线性链表,循环链表和双向链表。下面我将介绍各种方式的Java实现。
基本操作
不管是如何实现的,一个线性表的基本操作都是一样的,所以我们先创建一个定义了线性表基本操作的接口
ListBase
/**
* 线性链表的所有操作接口
* @author Like
*
*/
public interface ListBase<T> {
/**
* 清空列表
*/
void clearList();
/**
* 判断列表是否为空
* @return 如果为空,返回true,否则返回false
*/
boolean isEmpty();
/**
* 获得列表中元素额数量
* @return
*/
int size();
/**
* 获得列表第index个元素
* @param index 列表中的位置
* @return
*/
T get(int index) throws Exception;
/**
* 如果t是列表中的元素,且不是第一个,则返回t的前一个元素,否则返回null
* @param t
* @return
*/
T preElem(T t) throws Exception;
/**
* 如果t是列表中的元素,且不是最后一个,则返回t的下一个元素,否者返回null
* @param t
*/
T nextElem(T t) throws Exception;
/**
* 将t插入到列表第index元素之前
* @param t
*/
void add(int index, T t) throws Exception;
/**
* 删除列表的第index个元素
* @param index
*/
void remove(int index) throws Exception;
/**
* 打印列表中的所有数据
*/
void printAll();
}
顺序实现
以下是通过数组实现的顺序列表。
public class LinearList<T> implements ListBase<T> {
private T[] mts;
// 数组创建时的默认长度
private int mDefaultLength = 10;
// 当数组空间不足时,扩充的数组长度
private int mDefaultExpandLength = 5;
// 记录当前插入数据个数
private int mSize = 0;
// 定义两个异常
private Exception mSTZeroException = new Exception("数值必须大于0");
private Exception mBTLengthException = new Exception("超出列表长度");
private Exception mNotInListException = new Exception("元素不在列表中");
private Exception mFirstException = new Exception("已经是第一个元素");
private Exception mLastException = new Exception("已经是最后一个元素");
@SuppressWarnings("unchecked")
public LinearList() {
mts = (T[]) new Object[mDefaultLength];
}
@SuppressWarnings("unchecked")
@Override
public void clearList() {
mts = (T[]) new Object[mDefaultLength];
mSize = 0;
}
@Override
public boolean isEmpty() {
return mSize == 0;
}
@Override
public int size() {
return mSize;
}
@Override
public T get(int index) throws Exception {
if(index > mSize - 1)
throw mBTLengthException;
if(index < 0)
throw mSTZeroException;
return mts[index];
}
@Override
public T preElem(T t) throws Exception {
int index = getElementIndex(t);
if(index == -1)
throw mNotInListException;
if(index == 0)
throw mFirstException;
return mts[index-1];
}
@Override
public T nextElem(T t) throws Exception {
int index = getElementIndex(t);
if(index == -1)
throw mNotInListException;
if(index == mts.length)
throw mLastException;
return mts[index+1];
}
@Override
public void add(int index, T t) throws Exception {
mSize++;
// 对index范围进行判断
if(index < 0)
throw mSTZeroException;
if(index > mSize - 1)
throw mBTLengthException;
// 如果超出数组长度,对数组进行扩容
if(mSize == mts.length) {
arrayExpand(mDefaultExpandLength);
}
// 因为要将数据插入到index之前,所有从index开始所有数据都要往后移一位
for(int i=mSize; i>index; i--) {
mts[i] = mts[i-1];
}
mts[index] = t;
}
@Override
public void remove(int index) throws Exception {
mSize--;
if(index < 0)
throw mSTZeroException;
if(index > mSize - 1)
throw mBTLengthException;
// 将index之后的数据全部左移一位,实现删除
for(int i=index; i<mSize; i++) {
mts[i] = mts[i+1];
}
}
@Override
public void printAll() {
for(int i=0; i<mSize; i++) {
System.out.print(mts[i] + " ");
}
}
// 通过Arrays的copyOf来扩展数组的空间
private void arrayExpand(int expandLength) {
mts = Arrays.copyOf(mts, expandLength + mts.length);
}
// 通过给定元素找寻其在数组中的位置,如果不在数组中返回-1
private int getElementIndex(T t) {
for(int i=0; i<mSize; i++) {
if(mts[i].equals(t)) {
return i;
}
}
return -1;
}
public static void main(String[] args) throws Exception {
ListBase<String> list = new LinearList<>();
for(int i=0; i<20; i++) {
list.add(i, "" + i);
}
list.printAll();
}
}
就此,顺序列表已经实现成功,使用起来就和ArrayList一样。
public static void main(String[] args) throws Exception {
ListBase<String> list = new LinearList<>();
for(int i=0; i<20; i++) {
list.add(i, "" + i);
}
list.printAll();
}
总结
通过上面的实现可以明显的感觉到,顺序的线性表十分
善于进行查询的操作,只要知道位置可以快速定位到需要的找的元素。而
对于插入和删除操作,则十分繁琐,需要将插入或删除的元素之后所有元素进行移位操作。