1.线性表
线性表是具有n个相同特性的数据元素的有限序列,线性表在逻辑上是线性结构,在物理存储上通常以数组和链式结构的形式存储。
2.顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。
用数组实现ArrayList(顺序表)
随机访问RandomAccess时间复杂度为O(1)
public class MyArrayList implements IArrayList {
private int[] array;
private int size;
private int capacity;
public MyArrayList(int capacity) {
this.capacity = capacity;
array = new int[capacity];
this.size = 0;
}
/**
* @Description: 顺序链表中的前插法
* @Param:
* @return:
*/
@Override
public void pushFront(int item) {
if (this.size < capacity) {
for (int i = size; i > 0; i--) {
this.array[i] = this.array[i - 1];
}
this.array[0] = item;
this.size++;
}
}
/**
* @Description: 顺序链表的尾插法
* @Param:
* @return:
*/
@Override
public void pushBack(int item) {
if (this.size < capacity) {
array[this.size] = item;
this.size++;
}
}
/**
* @Description: 顺序链表中,在指定下标中添加元素,需从后向前遍历
* @Param:
* @return:
*/
@Override
public void add(int item, int index) {
if (this.size < this.capacity) {
for (int i = this.size; i > index; i--) {
this.array[i] = this.array[i - 1];
}
this.array[index] = item;
this.size++;
}
}
/**
* @Description: 顺序链表的头删法,将后一个直接覆盖前一个
* @Param:
* @return:
*/
@Override
public void popFront() {
if (this.size > 0) {
for (int i = 1; i < this.size; i++) {
this.array[i - 1] = this.array[i];
}
array[this.size - 1] = 0;
this.size--;
}
}
/**
* @Description: 顺序链表的尾删
* @Param:
* @return:
*/
@Override
public void popBack() {
if (this.size > 0) {
array[this.size - 1] = 0;
this.size--;
}
}
/**
* @Description: 顺序链表中,删除指定下标的元素,直接将后一个覆盖前一个,size-1
* @Param:
* @return:
*/
@Override
public void remove(int index) {
//数据
if (this.size > index) {
for (int i = index; i < size; i++) {
array[i] = array[i + 1];
}
array[this.size--] = 0;
}
}
ArrayList常用API
顺序表的局限性:
顺序表中间或头部的插入删除操作,时间复杂度为O(n)
扩容时需要申请新空间,拷贝数据,释放旧空间,有不小的内存消耗;
增容一般呈而二倍的增长,会有一定的空间浪费。
3.链表
链表是一种物理存储上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的引用;连接次序实现的。
通过头插和尾插的时间复杂度都是O(1)
LinkedList常用API:
自己实现链表结构:
public interface ILinkedList {
void pushFront(int node);
void popFront();
void pushBack(int node);
void popBack();
}
public class MyLinkedList {
class Node {
int value;
Node next;
public Node(int value) {
this.value = value;
}
}
private Node head;
public MyLinkedList() {
this.head = null;
}
//头插
void pushFront(Node node) {
if (this.head == null) {
this.head = node;
} else {
//头插-->当前的next指向node
node.next = this.head;
//node取代原head成为head
this.head=node;
}
}
//尾插
void pushBack(Node node) {
//如果头节点为空,node作为最后一个
if (this.head == null) {
this.head = node;
} else {
//获取最后一个结点
Node cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
//最后一个结点的next是node
cur.next = node;
}
}
//头删
void popFront() {
Node cur = this.head;
if(cur==null){
this.head=null;
}else {
//如果当前有下一个结点,获取下一个结点
while (cur.next != null) {
//将头节点的下一个作为头节点
cur = cur.next;
}
}
}
//尾删
void popBack() {
//找到倒数第二个
Node cur = this.head;
//头节点的下一个为空,头节点就是最后一个
if (cur.next == null) {
this.head = null;
} else {
while (cur.next.next != null) {
cur = cur.next.next;
}
//将倒数第二个的下一个置空
cur.next = null;
}
}
}
总结:
顺序表
- 随机访问,用O(1)时间复杂度取第N个节点
- 单数个数据存储,效率更高
- 不容易产生碎片,可能缓存更友好(最常使用原则)
链表
- 插入删除效率更友好
- 整体上说,链表存储效率更高