1 双向链表的简单介绍
前不久,写了一篇关于博文《单向链表的基本操作: java语言实现》,现在写一下双向链表的基本操作。与单向链表相比,双向链表多了一个前驱指针域。具体表示结构如下:
previous域--存放结点的直接前驱的地址(位置)的指针域
data域--存放结点值的数据域
next域--存放结点的直接后继的地址(位置)的指针域
双向链表的主要优点是对于任意给的结点,都可以很轻易的获取其前结点和后结点,其主要缺点是每个结点需要保存next和previous两个指针域,因此需要更多的空间开销,同时结点的插入与删除操作也将更加耗时,因为需要操作更多的指向操作。
2 双向链表的基本操作
2.1 创建结点类
//创建一个双向链表结点类,并用get,set方法获取其数据。
public class DLLNode {
private int data;//数据域
private DLLNode next;//后继指针域
private DLLNode previous;//前驱指针域
public DLLNode(int data) {
this.data = data;
this.next = null;
this.previous = null;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public DLLNode getNext() {
return next;
}
public void setNext(DLLNode next) {
this.next = next;
}
public DLLNode getPrevious() {
return previous;
}
public void setPrevious(DLLNode previous) {
this.previous = previous;
}
//显示该结点的数据
public void display() {
System.out.print( data + " ");
}
}
2.2 创建链表类
public class DoublyLinkedList {
private DLLNode head;//表头
private int length = 0;
public DoublyLinkedList() {
this.head = null;
}
//在链表头部添加结点
public void addHead(int data) {
DLLNode newNode = new DLLNode(data);
if(head == null) {
head = newNode;//如果链表为空,增加新结点
}
else {
newNode.setNext(head);
head.setPrevious(newNode);
head = newNode;
}
length++;
}
//在链表头部删除结点
public void deleteHead() {
if(head == null){
System.out.println("空表,删除的结点不存在");
}
else {
DLLNode curNode = head;
head = curNode.getNext();
head.setPrevious(null);
}
length--;
}
//在链表尾部添加结点
public void addTail(int data) {
DLLNode newNode = new DLLNode(data);
if (head == null) {
head = newNode;
} else {
DLLNode curNode = head;
int count = 1;
while (count < length) {
curNode = curNode.getNext();
count++;
}
newNode.setNext(null);
newNode.setPrevious(curNode);
curNode.setNext(newNode);
}
length++;
}
//在链表尾部删除结点
public void deleteTail() {
if(head == null){
System.out.println("空表,删除的结点不存在");
}else{
DLLNode preNode = head;
int count = 1;
while(count < length-1) {
preNode = preNode.getNext();
count++;
}
preNode.setNext(null);
}
length--;
}
//正向遍历链表
public void printOrderNode() {
if(head == null) {
System.out.println("空表");
}
DLLNode curNode = head;
while (curNode != null) {
curNode.display();
curNode = curNode.getNext();
}
System.out.println();
}
//反向遍历链表
public void printReverseNode() {
if(head == null) {
System.out.println("空表");
}
DLLNode curNode = head;
while (curNode.getNext() != null) {
curNode = curNode.getNext();
}
while (curNode != null) {
curNode.display();
curNode = curNode.getPrevious();
}
System.out.println();
}
//获取链表的长度
public int listLength() {
return length;
}
//在指定位置插入结点
public void insertList(int data, int index) {
DLLNode newNode = new DLLNode(data);
if(head == null){
head = newNode;//链表为空,插入
}
if(index > length+1 || index < 1) {
System.out.println("结点插入的位置不存在,可插入的位置为1到"+(length+1));
}
if(index == 1) {
newNode.setNext(head);
head.setPrevious(newNode);
head = newNode;//在链表开头插入
} else{ //在链表中间或尾部插入
DLLNode preNode = head;
int count = 1;
while(count < index-1) {
preNode = preNode.getNext();
count++;
}
DLLNode curNode = preNode.getNext();
newNode.setNext(curNode);
newNode.setPrevious(preNode);
preNode.setNext(newNode);
if(curNode != null) {
curNode.setPrevious(newNode);
}
}
length++;
}
//在指定位置删除结点
public void deleteList(int index) {
if(index > length || index < 1) {
System.out.println("结点删除的位置不存在,可删除的位置为1到"+length);
}
if(index == 1) {
DLLNode curNode = head;
head = curNode.getNext();
head.setPrevious(null);
length--;
} else{
DLLNode preNode = head;
int count = 1;
while(count < index-1) {
preNode = preNode.getNext();
count++;
}
DLLNode curNode = preNode.getNext();
DLLNode laterNode = curNode.getNext();
preNode.setNext(laterNode);
if(laterNode != null) { //若被删除结点的后继结点不是null结点,那么设置其前驱结点
laterNode.setPrevious(preNode);//指针指向被删除结点的前驱结点
}
length--;
}
}
//查找数据是否存在,与单链表一样
public boolean containData(int data) {
if(head == null){
System.out.println("空表");
return false;
}
DLLNode curNode = head;
while(curNode.getData()!= data){
if(curNode.getNext() == null) {
System.out.println("结点数据不存在");
return false;
}
curNode =curNode.getNext();
}
System.out.println("结点数据存在");
return true;
}
//获取指定位置的数据,与单链表一样
public void getIndexData(int index) {
if(head == null){
System.out.println("空表");
}
if(index > length || index < 1) {
System.out.println("结点位置不存在,可获取的位置为1到"+length);
}
DLLNode curNode = head;
int count =1;
while(count != index) {
curNode =curNode.getNext();
count++;
}
curNode.display();
System.out.println();
}
//修改指定位置的结点数据,与单链表一样
public void updateIndexData(int index, int data) {
if(head == null){
System.out.println("空表");
}
if(index > length || index < 1) {
System.out.println("结点位置不存在,可更新的位置为1到"+length);
}
DLLNode curNode = head;
int count =1;//while也可以用for循环方式解决
while(count != index) {
curNode =curNode.getNext();
count++;
}
curNode.setData(data);
}
}
3 建立测试类
测试类建立如下,运行结果这里不再赘述。
public class DoublyLinkedListTest {
public static void main(String [] args) {
DoublyLinkedList list = new DoublyLinkedList();
list.addHead(3);
list.addHead(2);
list.addHead(1);
//list.addTail(4);
//list.addTail(5);
//list.addTail(6);
//list.deleteTail();
//list.deleteHead();
//list.insertList(1,1);
//list.deleteList(2);
//list.containData(1);
//list.getIndexData(2);
list.updateIndexData(3,5);
list.printOrderNode();
list.printReverseNode();
}
}
4 个人总结
对于链表特点及增、删等操作过程及原理的掌握,有助于我们写程序实现。
5 参考资料
[1] 数据结构与算法经典问题解析