线性表
- 本线性表包含顺序表和链表,使用java实现该数据结构
- 都使用泛型使得线性表可以容纳任何类型的数据
- 表现了顺序表以及链表的特性,以及技巧性的实现
顺序表
实现方法:
public AList()
初始化一个顺序表,顺序表大小为0。public void addLast(Elem x)
在顺序表的末尾添加一个元素,如果超出线性表容量则扩大线性表。public Elem getLast()
获取顺序表的最后一个元素,如果顺序为空,返回null。public Elem get(int i)
获取顺序表的第i个元素,i表示数组下标,如果索引越界,返回null。public int size()
获取顺序表的大小public int removeLast()
删除顺序表的最后一个元素,当数组大部分值为空,则缩小数组容量。rivate void resize(int capacity)
按照capacity调整顺序表的当前容量。
亮点:
- 所有实现的时间复杂度都是O(1)级别的。
- 使用了恰当的成员变量,例如使用size缓存当前数组大小,避免通过遍历或者递归
- 使用RFACTOR系数控制扩展线性表的速度,以免当顺序表容量不足,频繁扩大线性表
public class AList<Elem> {
private Elem[] item;
private int size;
private int RFACTOR = 2;
/** Creates an empty list. */
public AList() {
item = (Elem[]) new Object[100];
size = 0;
}
private void resize(int capacity){
Elem[] a = (Elem[]) new Object[capacity];
System.arraycopy(item, 0, a, 0, size);
item = a;
}
/** Inserts X into the back of the list. */
public void addLast(Elem x) {
if (size >= item.length) {
resize(size * RFACTOR);
}
item[size] = x;
size++;
}
/** Returns the item from the back of the list. */
public Elem getLast() {
if (size == 0) {
System.out.println("current list is empty");
return null;
}
return item[size-1];
}
/** Gets the ith item in the list (0 is the front). */
public Elem get(int i) {
if (i < 0 || i >= size){
System.out.println("list beyond list bound");
return null;
}
return item[i];
}
/** Returns the number of items in the list. */
public int size() {
return size;
}
/** Deletes item from back of the list and
* returns deleted item. */
public int removeLast() {
if ((double) size /item.length <= 0.25) {
resize(item.length / RFACTOR);
}
item[size-1] = null;
size--;
return 0;
}
}
线性表
实现方法
public SLList()
创建一个空链表。public SLList(Elem x)
创建一个有一个元素的链表。public void addFirst(Elem x)
在链表头部添加一个元素。public Elem getFirst()
获取链表头部元素值。public void addLast(Elem x)
在链表尾部添加一个元素。public Elem getLast()
获取链表尾部元素值。public void removeLast()
删除链表尾部元素值。private int size(IntNode p)
获取从元素p开始一直到链表尾部的长度。public int size()
获取链表大小。public String toString()
以恰当的方式打印链表
亮点:
- 使用内部类表示链表数据结构,将数据结构封装好防止客户从外部篡改链表
- 使用first以及last缓存当前链表的首部和尾部,方便在O(1)时间内完成大多数操作
public class SLList<Elem> {
private class IntNode {
public Elem item;
public IntNode next;
public IntNode(Elem i, IntNode n) {
item = i;
next = n;
}
}
private IntNode first;
private int size; //if list too longer,add size as a cache,record the list size.
private IntNode last; //similar to size
/**
* if list is empty, first should is null
*/
public SLList() {
first = null;
last = null;
size = 0;
}
public SLList(Elem x) {
first = new IntNode(x, null);
last = first;
size = 1;
}
/**
* Adds an item to the start of the list.
*/
public void addFirst(Elem x) {
first = new IntNode(x, first);
size = size + 1;
if (size == 1) {
last = first;
}
}
/**
* Retrieves the front item from the list.
*/
public Elem getFirst() {
return first.item;
}
/**
* Adds an item to the end of the list.
*/
public void addLast(Elem x) {
if (last == null) {
first = new IntNode(x, null);
last = first;
size += 1;
return;
}
last.next = new IntNode(x, null);
last = last.next;
size += 1;
}
/**
* Retrieves the last item from the list.
*/
public Elem getLast() {
if (last == null) {
System.out.println("current list is null");
return null;
}
return last.item;
}
public void removeLast() {
IntNode p = first;
if (last == null) {
System.out.println("current list is null");
return;
}
if (first.next == null) {
first = null;
last = null;
size = 0;
return;
}
while (p.next.next != null) {
p = p.next;
}
p.next = null;
size = size - 1;
}
/**
* Returns the size of the list starting at IntNode p.
*/
private int size(IntNode p) {
if (p.next == null) {
return 1;
}
return 1 + size(p.next);
}
public int size() {
return size;
}
@Override
public String toString() {
String result = "[";
IntNode p = first;
while (p.next != null){
result = result + p.item + ",";
p = p.next;
}
result = result + p.item;
result = result + "]";
return result;
}
}
顺序表和链表对比
- 当顺序表大小不足时,扩展顺序表是一种耗费性能的操作,在此方面,链表强于顺序表。
- 在线性表头部进行插入删除时,顺序表需要O(n)的时间复杂度,但链表只需要O(1)的时间复杂度。
- 在随机获取线性表的元素时,链表需要O(n)的时间复杂度,而顺序表只需要O(1)的时间复杂度。
- 顺序表虽然可能有内存浪费的问题,但每个元素只需存储数据本身,而链表的每个节点还需要额外空间来存储指向下一个节点的指针。
- 顺序表因为数据连续存储,更利于现代计算机系统的缓存机制,可以提高访问速度。而链表由于其分散的存储,可能导致较差的缓存利用率,影响性能。