链表
- ok,在之前我已经把顺序表的基础知识已经分享给大家了,那我们想想看,因为每个元素都有自己的索引,就和我们都有身份证号码是一样的道理,
这就使得顺序表有着高效的查询速度,在顺序表中查询元素的时间复杂度是O(1);那我们在每一次增删操作时,紧跟其后的所有元素都得进行移动,
有没有办法解决呢?是有的,链表。 - 举个例子,体育课老师按照身高将学生排好一列,老师说把你前后的同学都记准确。当快要下课时老师集合同学时,他们能够快速按照原来的
顺序排好队。我举这个栗子什么意思,别急接着往下看。
定义
- 链表是一种物理存储单元,非连续,非顺序,头结点的数据域为空,尾节点的指针域为空。元素的逻辑顺序是通过链表中的指针来链接实现,有一系列的结点(链表中的元素称为结点),
且节点可在运行时动态生成。
单向链表
- 由多个节点组成,每个节点都有一个数据域和一个指针域,数据域用来存储数据,指针域用来指向其后继节点,链表的头节点的数据域不存储数据,指针域
指向第一个真正存储数据的节点。 - 代码实现
package DataStructure.LinkedList;
public class LinkedList<T> implements Iterable<T> {
private Node head;
private int N;
public class Node {
public T item;
public Node next;
public Node(T item,Node next) {
this.item = item;
this.next = next;
}
}
public LinkedList() {
this.head = new Node(null,null);
this.N = 0;
}
public void clean() {
head.next = null;
N = 0;
}
public boolean isEmpty() {return N==0;}
public int length() {return N;}
public T get(int i) {
Node n = head.next;
for (int j = 0; j < i; j++) {
n = n.next;
}
return n.item;
}
public void insert(T t) {
Node n = head;
while(n.next != null) {
n = n.next;
}
Node node = new Node(t, null);
n.next = node;
N++;
}
public void insert(int i,T t) {
Node n = head;
for (int j = 0; j < i-1; j++) {
n = n.next;
}
Node current = n.next;
Node node = new Node(t, current);
n.next = node;
N++;
}
public T remove(int i) {
Node n = head;
for (int j = 0; j < i-1; j++) {
n = n.next;
}
Node current = n.next;
Node nextNode = current.next;
n.next = nextNode;
N--;
return current.item;
}
public int indexOf(T t) {
Node n = head;
for (int i = 0; n.next!=null ; i++) {
n = n.next;
if (n.item.equals(t)) {
return i;
}
}
return -1;
}
@Override
public Iterator<T> iterator() {
return new LIterator();
}
public class LIterator implements Iterator {
private Node n;
public LIterator() {
this.n = head;
}
@Override
public boolean hasNext() {
return n.next != null;
}
@Override
public Object next() {
n = n.next;
return n.item;
}
}
}
双向链表
- 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
- 代码实现:
package DataStructure.LinkedList.TwoWay;
import java.util.Iterator;
public class TwoWayLinkedList<T> implements Iterable<T> {
private Node first;
private Node last;
private int N;
private class Node{
private T item;
private Node pre;
private Node next;
public Node(T item, Node pre, Node next) {
this.item = item;
this.pre = pre;
this.next = next;
}
}
public TwoWayLinkedList() {
this.first = new Node(null,null,null);
this.last = new Node(null,null,null);
this.N = 0;
}
public void clean() {
this.first.next = null;
this.first.pre = null;
this.first.item = null;
this.last = null;
this.N = 0;
}
public boolean isEmpty() {return N == 0;}
public int length() {return N;}
public T get(int i) {
Node n = first.next;
for (int j = 0; j < i; j++) {
n = n.next;
}
return n.item;
}
public void insert(T t) {
if (isEmpty()) {
Node node = new Node(t, first, null);
last = node;
first.next = last;
}else {
Node oldLast = last;
Node node = new Node(t, oldLast, null);
oldLast.next = node;
last = node;
}
N++;
}
public void insert(int i,T t) {
if (isEmpty()) {
return;
}
Node pre = first;
for (int j = 0; j < i-1; j++) {
pre = pre.next;
}
Node current = pre.next;
Node node = new Node(t, pre, current);
pre.next = node;
current.pre = node;
N++;
}
public T remove(int i) {
Node pre = first;
for (int j = 0; j < i-1; j++) {
pre = pre.next;
}
Node current = pre.next;
Node nextNode = current.next;
pre.next = nextNode;
nextNode.pre = pre;
N--;
return current.item;
}
public int indexOf(T t) {
Node n = first;
for (int i = 0; n.next != null; i++) {
n = n.next;
if (n.next.equals(t)) {
return i;
}
}
return -1;
}
public T getFirst() {
if (isEmpty()) {
return null;
}
return first.next.item;
}
public T getLast() {
if (isEmpty()) {
return null;
}
return last.item;
}
@Override
public Iterator<T> iterator() {
return new TIterator();
}
private class TIterator implements Iterator {
private Node n;
public TIterator() {
this.n = first;
}
@Override
public boolean hasNext() {
return n.next != null;
}
@Override
public Object next() {
n = n.next;
return n.item;
}
}
}
链表的复杂度分析
- 在链表执行插入删除时,由于链表的物理地址不是连续的,即不需要预先指定存储空间大小,扩容,元素的交换等。
- 相比较顺序表,链表的查询效率明显较低,即增删快,查询慢。