数据结构–双向链表
前言
在上一篇文章中介绍了单向链表的结构以及实现,本篇文章再来介绍另一种链表——双向链表。
一、双向链表的结构
双向链表是链表的一种,与单向链表均由若干个节点组成,每个节点包含数据域和指针域,不同的是,双向链表每个节点有两个指针域和一个数据域,其中一个指针指向前驱元素,另一个指针指向后继元素,如图:
添加元素操作说明:
在添加元素时,除了要完成使新节点前一个节点的next指向新节点,新节点的next指向后一个节点外,因为是双向的,还要使新节点的pre指向前一个节点,新节点后一个节点的pre指向新节点(此处有点绕)。
删除元素操作说明:
删除元素时,是被删除元素的前一个节点的next指向被删除元素后一个节点;被删除元素后一个节点的pre指向被删除元素的前一个节点。
二、代码实现
链表实现以及相关方法完整代码:
package sequence;
/**
* 双向链表
* @param <T>
*/
import java.util.Iterator;
public class TwoWayLinkList<T> implements Iterable<T>{
//首节点
private Node head;
//尾节点
private Node last;
//链表长度
private int N;
private class Node {
//数据域
public T item;
//指向上一个节点
public Node pre;
//指向下一个节点
public Node next;
public Node(T item, Node pre, Node next) {
this.item = item;
this.pre = pre;
this.next = next;
}
}
public TwoWayLinkList() {
//初始化头节点和尾节点
this.head = new Node(null, null, null);
this.last = null;
//初始化元素个数
this.N = 0;
}
//空置线性表
public void clear() {
this.head.next = null;
this.last = null;
this.N = 0;
}
//判断线性表是否为空
public boolean isEmpty() {
return N == 0;
}
//获取线性表中元素的个数
public int length() {
return N;
}
//读取并返回线性表中第i个元素
public T get(int i) {
Node n = head.next;
for(int index = 0; index < i; index++){
n = n.next;
}
return n.item;
}
//向线性表中添加一个元素
public void insert(T t) {
//当线性表为空时:
if (isEmpty()) {
//创建一个新节点
Node newNode = new Node(t, head, null);
//使新节点成为尾节点:
last = newNode;
//使头节点指向尾节点;
head.next = last;
} else {
Node oldLast = last;
//当链表不为空时:
//创建一个新节点:
Node newNode = new Node(t, oldLast, null);
//使当前尾节点指向新节点:
oldLast.next = newNode;
//使新节点成为尾节点:
last = newNode;
}
N++;
}
//向线性表中第i个元素之前添加一个值为t的元素
public void insert(int i, T t) {
Node pre = head;
//找到i位置的前一个节点:
for (int index = 0; index < i; index++) {
pre = pre.next;
}
//找到i位置的节点:
Node currNode = pre.next;
//创建新节点:
Node newNode = new Node(t, pre, currNode);
//使i位置的节点的pre指向新节点:
currNode.pre = newNode;
//使i位置的前一个节点的next指向新节点:
pre.next = newNode;
N++;
}
//删除并返回线性表中第i个数据元素
public T remove(int i) {
Node pre = head;
//找到i位置的前一个节点:
for(int index = 0; index < i; index++){
pre = pre.next;
}
//找到i位置的节点:
Node curNode = pre.next;
//找到i位置的下一个节点:
Node nextNode = curNode.next;
//使i位置节点的前一个节点的next指向i位置后的节点;
pre.next = nextNode;
//使i位置后的节点的pre指向i位置前的节点:
nextNode.pre = pre;
N--;
return curNode.item;
}
//返回线性表中首次出现指定元素的下标,若不存在,则返回-1
public int indexOf(T t) {
Node n = head;
for(int index = 0; n.next != null; index++){
n = n.next;
if(t.equals(n.item)){
return index;
}
}
return -1;
}
//获取第一个元素
public T getFirst() {
if (isEmpty()) {
return null;
}
return head.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 = head;
}
@Override
public boolean hasNext() {
return n.next != null;
}
@Override
public Object next() {
n = n.next;
return n.item;
}
}
}
总结
Java集合中的LinkedList的实现就是一个双向链表,可以查看底层的相关代码去验证。