前面讲过线性表中顺序表和链表的实现和性质。但是在数据结构与算法中,双向链表无论在考察还是运用中都占有很大的比例,笔者旨在通过本文与读者一起学习分享双链表相关知识。
与单链表区别
逻辑上没有区别。他们均是完成线性表的内容。主要的区别是结构上的构造有所区别。
对于单链表:
对于双链表:
结构的设计
所以我们构造的这个双链表的的性质:
对于node节点:
class node<T> { T data; node<T> pre; node<T> next; public node() { } public node(T data) { this.data = data; }}
对于链表:
public class doubleList<T> { private node<T> head;// 头节点 private node<T> tail;// 尾节点 private int length; //各种方法 }
初始化
public doubleList() { head = null; tail = head; length = 0; }
增加
空表插入:
node<T> teamNode = new node(data);if (isEmpty()) { head = teamNode; tail = teamNode; }
头插入:
对于头插入来说。步骤很简单,只需考虑head节点的变化。
尾插入:
对于尾插入来说。只需考虑尾节点tail节点的变化。
编号插入:
对于编号插入来说。要考虑查找和插入两部,而插入既和head无关也和tail无关。
4 . pre后驱指向node。node前驱指向pre(此时完毕)
整个流程的动态图为:
删除
单节点删除:
无论头删还是尾删,遇到单节点删除的需要将链表从新初始化!
if (length == 1)// 只有一个元素{ head = null; tail = head; length--;}
头删除:
头删除需要注意的就是删除不为空时候头删除只和head节点有关
大致分为:
尾删除:
尾删除需要注意的就是删除不为空时候尾删除只和tail节点有关。记得在普通链表中,我们删除尾节点需要找到尾节点的前驱节点。需要遍历整个表。而双向链表可以直接从尾节点遍历到前面。
删除的时tail所在位置的点。也就是tail所在节点要断绝和双链表的关系。
普通删除:
普通删除需要重点掌握,因为前两个删除都是普通删除的一个特例而已。(普通删除要确保不是头删除和尾删除)
3 . team.next=team.next.next;此时team.next也跳过被删除节点。
4 . 完成删除
整个流程的动态图为:
代码:
package LinerList;/* * 不带头节点的 */public class doubleList<T> {class node<T> { T data; node<T> pre; node<T> next; public node() { } public node(T data) { this.data = data; }}private node<T> head;// 头节点private node<T> tail;// 尾节点private int length;public doubleList() { head = null; tail = head; length = 0;}boolean isEmpty() { return length == 0 ? true : false;}void addfirst(T data) { node<T> teamNode = new node(data); if (isEmpty()) { head = teamNode; tail = teamNode; } else { teamNode.next = head; head = teamNode; } length++;}void add(T data)// 尾节点插入{ node<T> teamNode = new node(data); if (isEmpty()) { head = teamNode; tail = teamNode; } else { tail.next = teamNode; teamNode.pre=tail; tail = teamNode; } length++;} int length() { return length; }T getElum(int index)//为了简单统一从头找{ node<T> team=head; for(int i=0;i<index;i++)//不带头节点 遍历次数-1 { team=team.next; } return team.data;}void add(int index, T data)// 编号插入{ if (index == 0) { addfirst(data); } else if (index == length) { add(data); } else {// 重头戏 node teampre = head;// 为插入的前qu for (int i = 0; i < index -1; i++)// 无头节点,index-1位置找到前驱节点 { teampre = teampre.next; } node<T> team = new node(data);// a c 中插入B 找打a team.next = teampre.next;// B.next=c teampre.next.pre = team;// c.pre=B team.pre = teampre;// 关联a B teampre.next = team; length++; }}void deletefirst()// 头部删除{ if (length == 1)// 只有一个元素 { head = null; tail = head; length--; } else { head = head.next; length--; }} void deletelast() { if(length==1) { head=null; tail=head; length--; } else { tail.pre.next=null; tail=tail.pre; length--; }} void delete(int index) { if(index==0)deletefirst(); else if (index==length-1) { deletelast(); } else {//删除 为了理解统一从头找那个节点 node<T>team=head; for(int i=0;i<index-1;i++) { team=team.next; } //team 此时为要删除的前节点 a c 插入B a为team team.next.next.pre=team;//c的前驱变成a team.next=team.next.next;//a的后驱变成c length--; } } void set(int index,T data) { node<T>team=head; for(int i=0;i<index-1;i++) { team=team.next; } team.data=data; }@Overridepublic String toString() { node<T> team = head; String vaString = ""; while (team != null) { vaString += team.data + " "; team = team.next; } return vaString;}}
测试:
package LinerList;public class test { public static void main(String[] args) throws Exception {// TODO Auto-generated method stub System.out.println("线性表测试:"); doubleList<Integer> list = new doubleList<Integer>(); list.add(66); list.addfirst(55); list.add(1, 101); list.add(-22); list.add(555); list.addfirst(9999); System.out.println(list.toString() + " lenth " + list.length());// 9999 55 101 66 -22 555 // System.out.println(list.getElum(0)+" "+list.getElum(2)+" "+list.getElum(4)); list.deletefirst(); System.out.println(list.toString() + " lenth " + list.length());// 55 101 66 -22 555 lenth 5 list.delete(1); System.out.println(list.toString() + " length " + list.length());// 55 66 -22 555 length 4 list.delete(1); System.out.println(list.toString() + " length " + list.length());// 55 -22 555 length 3 list.deletelast(); System.out.println(list.toString() + " lenth " + list.length());// 55 -22 lenth 2 list.deletelast(); System.out.println(list.toString() + " lenth " + list.length());// 55 lenth 1 list.deletelast(); System.out.println(list.toString() + " lenth " + list.length());// lenth 0 System.err.println("欢迎关注公众号:bigsai"); }}
结果图
插入、删除顺序问题:
其他操作问题:
另外,代码写的可能不是太好,链表也没考虑线程安全问题。算法效率可能不太优。如果有什么改进或者漏洞还请大佬指出!
最后(last but not least):