数据结构之数组与链表
聊数组和链表前,我们首先需要知道内存和如何存放数据的。内存就像大超市寄存处,寄存处有非常多的柜子。每个柜子都有标号(内存地址),柜子用来存放行李(存放数据)。取柜子里的行李时,需要知道柜子标号。
数组
有时候我们的行李有很多,一个柜子存放不下,那么就需要使用多个柜子来存。熟悉Java的都知道,Java里的数组能够存放多个变量值。Java数组类似于一些连续在一起的柜子。
但是数组有一个非常大的缺陷,数组的长度在声明变量时就定好了。以后不可以变长也不可以缩减。在使用期间,如果数组的长度不够用了,那就要重新使用新的数组,太麻烦了。有一种策略是,声明的时候保证数组的长度足够长。但是,这样也会有两个问题:
- 数组中有许多未使用的空间,这些空间没有使用,白白浪费了
- 还是有可能长度超标的,超标后,还是需要使用新的容器来存放已有数组中的数据
链表
链表不同于数组,数组必须申请连续的内存空间,链表每个元素的位置是可以随意的,不必是连续的内存空间。链表中的每个元素的内存空间不是连续的,那么链表是如何知道每个元素的内存地址的呢?链表的每个元素都存储了下一个元素的地址,从而使一系列随机的内存地址串在一起。链表和数组比起来,具有动态性,不会存在长度不够的问题。
数组与链表的算法复杂度
数组 | 链表 | |
---|---|---|
读取 | O(1) | O(n) |
插入 | O(n) | O(1) |
删除 | O(n) | O(1) |
使用Java实现单链表结构
class LinkList<T> {
/* list size */
private int size = 0;
/* the head of list */
private Node<T> head;
boolean add (T elem) {
if (head == null) {
head = new Node<>(elem);
} else {
Node<T> temp = head;
while (temp.next != null) {
temp = temp.next;
}
temp.next = new Node<>(elem);
}
size++;
return true;
}
boolean add (int index, T element) {
if (index > size | index < 0)
return false;
Node<T> temp = head;
Node<T> newNode = new Node<>(element);
if (index == 0) {
head = newNode;
head.next = temp;
} else if (size == index){
return add(element);
}else {
for (int i=1; i < index; i++) {
temp = temp.next;
}
Node<T> next = temp.next;
temp.next = newNode;
newNode.next = next;
}
size++;
return true;
}
boolean remove (int index) {
if (index >= size || index < 0)
return false;
if (index == 0) {
Node<T> next = head.next;
head = null;
head = next;
} else {
Node<T> prev = head;
for (int i = 0; i < index - 1; i++) {
prev = prev.next;
}
prev.next = prev.next.next;
}
return true;
}
T get (int index) {
if (index >= size || index < 0) {
return null;
}
Node<T> current = head;
for (int i=0; i < index; i++) {
current = current.next;
}
return current.elem;
}
void forEach () {
for (Node<T> temp = head; temp != null; temp = temp.next) {
System.out.println(temp.elem);
}
}
int size() {
return size;
}
private class Node<E extends T>
{
E elem;
Node<E> next;
Node(E elem) {
this.elem = elem;
}
}
}