一、双链表的理解
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表任意一个结点开始,都可以很方便的访问它的前驱结点和后继结点。

二、双链表的代码实现
首先我们先来初始化双链表,注意双链表有两个地址域一个存放前驱结点的地址一个存放后继结点的地址。
class ListNode{
public int val;
public ListNode next;
public ListNode pre;
public ListNode(int val){
this.val = val;
}
}
头插法
双链表的头插法与尾插法与单链表基本一致,在此不画图做过多赘述
public void addFirst(int data){
ListNode node = new ListNode(data);
if(head == null){
node = this.head;
node = this.last;
}
if(head != null){
node.next = this.head;
head.pre = node;
this.head = node;
}
}
尾插法
public void addLast(int data){
ListNode node = new ListNode(data);
if(head == null){
node = this.head;
node = this.last;
}else{
this.last.next = node;
node.pre = this.last;
this.last = node;
}
}
任意位置插入,第一个数据结点为0号下标

在这里我们需要判断几种情况,如果下标超过了数组长度或者下标小于0则位置不合法。如果下标在开始的位置则使用头插法,如果下标在链表末尾位置则使用尾插法,如果下标在中间位置,我们要先找到inedx 的位置,不需要找到其前驱结点的位置。
public ListNode searchIndex(int index){
ListNode cur = this.head;
while(index != 0){ //循环判断inedx的位置,返回cur时cur就是inedx的所在位置
cur = cur.next;
index--;
}
return cur;
}
public void addIndex(int index,int data){
ListNode node = new ListNode(data);
if(index < 0 || index > size()){ //判断index位置是否合法
System.out.println("index位置不合法");
}
if(index == 0){ //头插法
addFirst(data);
return;
}
if(index == size()){ //尾插法
addLast(data);
return;
}
ListNode cur = searchIndex(index); //中间插入
node.next = cur.pre.next;
cur.pre.next = node;
node.pre = cur.pre;
cur.pre = node;
}
查找是否包含关键字key是否在双链表当中
public boolean contains(int key){
ListNode cur = head;
while(cur != null){
if(cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
删除第一次出现关键字为key的节点
与双链表插入相同,我们也要判断下标是否合法,具体在哪个位置删除。

public void remove(int key){
ListNode cur = head;
while(cur != null){
if(cur.val == key){
if(cur == head){
head = head.next;
head.pre = null;
}else{
if(cur == last){
last = last.pre;
last.next = null;
}else{
cur.pre.next = cur.next;
cur.next.pre = cur.pre;
}
}
cur = cur.next;
}
}
}
删除所有结点值为key的结点
public void removeAllKey(int key){
ListNode cur = head;
while(cur != null){
if(cur.val == key){
if(cur == head){
head = head.next;
if(head != null){
head.pre = null;
}
}else {
last = null;
}
}else{
cur.pre.next = cur.next;
if(cur.next != null){
cur.next.pre = cur.pre;
}else{
last = last.pre;
}
}
}
cur = cur.next;
}
得到双链表的长度
public int size(){
int count = 0;
ListNode cur = this.head;
while(cur != null){
count++;
cur = cur.next;
}
return count;
}
打印双链表
public void display(){
ListNode cur = this.head;
while(cur != null){
System.out.println(cur.val+" ");
cur = cur.next;
}
System.out.println( );
}
清空双链表
清空双链表时可以直接将头结点和尾结点置空,也可以一个一个删除
public void clear(){
while(head != null){
ListNode next = this.head.next;
head.pre = null;
head.next = null;
head = next;
}
last = null;
}
三、双链表的优缺点
优点:
拥有着前驱和后继,可往前往后遍历,多样化。
在修改和删除时拥有着较好的性能,在查询时也有着不错的速度
可以不需要从头结点开始访问,提高了检索性能
缺点:
相较于单向链表,多了一个pre节点占据了更多的空间
因为除了一个data域存数据外,其他两个pre和next域不存放数据,空间利用率低
两个指针在维护时比较麻烦。