写在前面:
线性表的根据其存储结构可以划分为为:顺序表和链式表,而链式表又可以划分为单向链表,双向链表,以及循环链表。
1.顺序表
特点:数据元素的存储是连续的,在内存中划分的区域也是连续的。
优点:查询某个位置的数据快
缺点:添加,删除某个元素,需要移动其他元素的位置,且当线性表容量到达最大时,需要扩容
下面来看代码实现:
定义一个接口---IListDemo1
一个实现类---ListDemo1
接口代码如下:
package list;
public interface IListDemo1 {
/**
* 清除线性表
*/
public void clear();
/**
* 判断线性表是否为空
*
* @return
*/
public boolean isEmpty();
/**
* 返回线性表的大小
*
* @return
*/
public int length();
/**
* 获取位置为i的元素
*
* @param i
* @return
* @throws Exception
*/
public Object get(int i) throws Exception;
/**
* 在指定位置i插入元素e
*
* @param i
* @param e
* @throws Exception
*/
public void insert(int i, Object e) throws Exception;
/**
* 移除列表中指定位置的元素
*
* @param i
* @throws Exception
*/
public void remove(int i) throws Exception;
/**
* 返回列表中最后出现指定元素的索引,如果列表不包含此元素,则返回 -1。
*
* @param e
* @return
*/
public int lastIndexOf(Object e);
/**
* 用指定元素e替换位置i上的元素
*
* @param i
* @param e
*/
public void set(int i, Object e);
/**
* 向线性表中添加元素
*
* @param e
*/
public void add(Object e);
/**
* 当线性表不够大时,扩容2倍
*/
public void expand();
}
实现类代码如下:
package list;
import java.util.ArrayList;
import java.util.List;
/**
* 模拟线性表的顺序存储
*
* @author McGRADY
*
*/
public class ListDemo1 implements IListDemo1 {
private Object[] listElements; // 线性表存储空间
private int maxLen; // 线性表最大长度
private int currLen;// 线性表当前长度
public ListDemo1() {
this(10); // 如果没有为线性表赋初始大小值,则默认为10
}
public ListDemo1(int initalSize) {
if (initalSize < 0) {
throw new RuntimeException("数组大小错误: " + initalSize);
} else {
listElements = new Object[initalSize];
this.currLen = 0;
this.maxLen = initalSize;
}
}
@Override
public void clear() {
currLen = 0;
}
@Override
public boolean isEmpty() {
return currLen == 0;
}
@Override
public int length() {
return currLen;
}
@Override
public Object get(int i) throws Exception {
if (i < 0 || i > currLen) {
return null;
} else {
return listElements[i];
}
}
@Override
public void insert(int i, Object e) throws Exception {
if (i < 0 || i > currLen) {
throw new RuntimeException("插入位置不正确");
} else {
if (currLen + 1 > maxLen) {
expand();// 先扩容
}
currLen++;
for (int j = currLen - 1; j > i; j--) {
listElements[j] = listElements[j - 1];
}
listElements[i] = e;
}
}
@Override
public void remove(int i) throws Exception {
for (int j = i; j < currLen - 1; j++) {
listElements[j] = listElements[j + 1];
}
currLen--;
}
@Override
public int lastIndexOf(Object e) {
int index = -1; // 记录数据e的索引
for (int i = 0; i < currLen; i++) {
if (listElements[i] == e) {
index = i;
}
}
if (index >= 0) {
return index;
} else {
return -1;
}
}
@Override
public void set(int i, Object e) {
listElements[i] = e;
}
@Override
public void add(Object e) {
if (currLen > maxLen) {
throw new RuntimeException("线性表以及达到最大值");
} else {
listElements[currLen] = e;
currLen++;
}
}
@Override
public void expand() {
maxLen = 2 * maxLen;
Object[] newArray = new Object[maxLen];
for (int i = 0; i < currLen; i++) {
newArray[i] = listElements[i];
}
listElements = newArray;
}
}
2.单向链表
特点:只能从头遍历到尾 或者 从尾遍历到头 (类似于火车)
3.双向链表
特点:既能从头遍历到尾也能从尾遍历到头
优点:操作头尾数据方便,不需要扩容,不会越界,删除快
缺点: 查找较慢
package list;
public class LinkedListDemo {
private Node first; // 链表的第一个节点
private Node last; // 链表的最后一个节点
private int size = 0; // 节点的数量
class Node {
Node prev; // 上一个节点对象
Node next; // 下一个节点对象
Object ele; // 当前节点存储的数据
public Node(Object ele) {
this.ele = ele;
}
}
/**
* 将节点添加到末尾
*
* @param e
*/
public void addLast(Object e) {
Node node = new Node(e);
if (size == 0) {
this.first = node;
this.last = node;
} else {
this.last.next = node;
node.prev = this.last;
this.last = node;
}
size++;
}
/**
* 将节点添加到头部
*
* @param e
*/
public void addFirst(Object e) {
Node node = new Node(e);
if (size == 0) {
this.first = node;
this.last = node;
} else {
this.first.prev = node;
node.next = this.first;
this.first = node;
}
size++;
}
/**
* 移除数据为e的节点
* @param e
*/
public void remove(Object e) {
// 不考虑数据有重复
Node current = this.first;
for (int i = 0; i < size; i++) {
if (!current.ele.equals(e)) {
if (current.next == null) {
return;
}
current = current.next;
}
}
if (current == this.first) {
this.first = current.next;
this.first.prev = null;
} else if (current == this.last) {
this.last = current.prev;
this.last.next = null;
} else {
current.next.prev = current.prev;
current.prev.next = current.next;
}
size--;
}
/**
* 根据数据寻找节点
*
* @param e
* @return
*/
public Node get(Object e) {
// 不考虑数据有重复
Node current = this.first;
for (int i = 0; i < size; i++) {
if (!current.ele.equals(e)) {
if (current.next == null) {
return null;
}
current = current.next;
}
}
return current;
}
/**
* 根据索引寻找对应节点的数据
* @param index
* @return
*/
public Object getObject(int index) {
if (index > this.size - 1 || index < 0) {
throw new RuntimeException("索引越界");
}
Node current = this.first;
for (int i = 0; i < index; i++) {
current = current.next;
}
return current.ele;
}
@Override
public String toString() {
if (size == 0) {
return "[]";
}
Node current = this.first;
StringBuffer sb = new StringBuffer(size * 2 + 1);
sb.append("[");
while (current.next != null) {
sb.append(current.ele + ">");
current = current.next;
}
sb.append(current.ele + "]");
return sb.toString();
}
}
到这里,我们就分别实现了线性表的增加,删除,修改,查询等操作。