1.链表
循环链表:与单向链表的区别在于尾节点的地址域(指针域)不为null,它指向了首节点的引用。循环链表是另一种形式的链式存贮结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。
优点:可以从链表的任意节点出发,都能够通过后移操作,而扫描整个循环链表。
双端链表:增加了一个对尾节点的指针。链表中保存着对最后一个链节点引用的链表。
双向链表:
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
优点:对于链表中的任意一个节点,可以从两个方向来进行操作。
缺点:每个节点增加了一个前驱节点的指针,所以说,在内存上,要更多的消耗存储空间。
next:指向后继节点的地址
prior:指向前驱节点的地址
删除双向链表的节点:B节点
b.前驱节点.Next=B.Next指针;(A.next = B.next)
b.后继节点.prior=B.Prior指针;(B.next.prior = B.prior)
插入E节点;
E.Prior=B节点;
E.Next=B.Next
B.Next.Prior(C节点)=E节点
B.Next=E节点
2.用java代码模拟双向链表的查询,删除,遍历(正向遍历和反向遍历),增加节点(在指定节点前增加增加新的节点)
package com.cym.datastructure.linked_list;
/**
* 双向链表
*/
public class DoubleLInkedList {
//定义头节点
Node headNode;
//定义链表的大小
int sise;
//定义尾节点
Node endNode;
/**
* 内部类,定义链表的节点,元素
*/
class Node {
//定义节点存储的数据
Object data;
//定义下一个节点
Node next;
//定义前一个节点
Node prior;
public Node(Object data) {
this.data = data;
}
}
/**
* 定义一个获得空节点节点的方法
*/
public Node getNode(Object object) {
return new Node(object);
}
/**
* 链表的判空
*/
public boolean isEmpty() {
return sise == 0;
}
/**
* 链表节点的插入(初始化)
*/
public void addNode(Object object) {
Node newNode = new Node(object);
//判断链表是否为空,如果为空插入的节点作为头节点就可以了
if (isEmpty()) {
headNode = newNode;
//同时尾节点也是第一个插入的节点
endNode = newNode;
} else {
//如果链表中已经存在了节点,就把新加入的节点指向原有的的头节点(就是把头节点的地址存在新加入节点的newNode.next中)
newNode.next = headNode;
//把后继节点指向前驱结点(双向链表A<--->B)
headNode.prior = newNode;
//新加入的节点就作为该链表的新的头节点(因为链表的加入是从头节点开始插入的)
headNode = newNode;
}
sise++;
}
/**
* 遍历该链表(正向遍历)
*/
public void traverseSingleLinkedListsByHeadNode() {
traverseSingleLinkedList(headNode);
}
/**
* 遍历该链表(犯贱向遍历)
*/
public void traverseSingleLinkedListsByEndNode() {
traverseSingleLinkedList(endNode);
}
/**
* 遍历双向链表的方法
* @param traverseNode 指定是从头节点开始还是尾节点遍历
*/
public void traverseSingleLinkedList(Node traverseNode) {
//定义当前节点并把节点等于头节点
Node currentNode = traverseNode;
if (!isEmpty()) {
if (sise == 1) {
System.out.println("[" + currentNode.data + "]");
return;
} else {
//定义临时大小变量
int tempSize = sise;
System.out.print("[" + "\t");
while (tempSize > 0) {
if (currentNode == headNode) {//头节点的时候
System.out.print(currentNode.data);
if (traverseNode == headNode) {
System.out.print("<--->");
}
} else if (currentNode.next == null) {//尾节点的时候
System.out.print(currentNode.data);
if (traverseNode == endNode) {
System.out.print("<--->");
}
} else {//中间节点的时候
System.out.print(currentNode.data + "<--->");
}
//正向遍历的时候指向当前节点的后继节点
if (traverseNode == headNode) {
currentNode = currentNode.next;
}
//反向遍历的时候时候指向当前节点的前驱节点
if (traverseNode == endNode) {
currentNode = currentNode.prior;
}
tempSize--;
}
System.out.print("\t" + "]");
}
} else {
System.out.println("[]");
}
}
/**
* 查询指定的内容(这里是正向遍历查询,当然也可以反向,参考上述的反向遍历)
*
* @param object 待查询的内容
* @return
*/
public Object findNode(Object object) {
//判断链表是不为null
if (!isEmpty()) {
//指定当前节点为头节点
Node currentNode = headNode;
//定义链表的临时大小变量
int tempSize = sise;
while (tempSize > 0) {
//判断查询的值是否和当前节点的值一样
if (object.equals(currentNode.data)) {
//返回当前节点的地址的码
return currentNode;
}
//当前节点指向下一个节点
currentNode = currentNode.next;
tempSize--;
}
}
return null;
}
/**
* 在指定的节点之前插入(当然也可以在指定节点之后插入,不过在这我就不实现了,都是类型的)
*
* @param newNode
* @param object
*/
public void addNodeBySelect(Node newNode, Object object) {
//获得指定的节点
Node selectNode = (Node) findNode(object);
//如果是头节点直接调用初始化链表的方法
if (selectNode == headNode) {
this.addNode(newNode.data);
return;
}
//让新增的节点指向后继节点
newNode.next = selectNode;
//添加反向链
newNode.prior = selectNode.prior;
//让新插入的节点的前驱节点指向新加入的节点
selectNode.prior.next = newNode;
//添加反向链
selectNode.prior = newNode;
sise++;
}
/**
* 删除头节点
*/
public void deleteHeadNode() {
if(!isEmpty()){
//让头节点后面的一个节点成为新的头节点
headNode = headNode.next;
//把新的头节点的前驱结点的指针置为null
headNode.prior = null;
//链表大小减1
sise--;
}else {
System.out.println("链表是空的");
}
}
/**
* 删除指定的节点(正向遍历删除)
* @param object
*/
public boolean deleteNodeByData(Object object){
if (isEmpty()){return false;}
//定义当前节点为头节点
Node currentNode = headNode;
//定义前驱结点为头节点
Node precursorNode = headNode;
//判断要删除的值是否和当前节点的存放的值一样,如果不同则继续循环下去
while (!object.equals(currentNode.data)){
if(currentNode == null){
return false;
}else {
//是前驱结点等于当前节点
precursorNode = currentNode;
//当前节点指向下一个节点
currentNode = currentNode.next;
}
}
//判断如果删除的节点是头节点直接调用删除头节点的方法
if(currentNode == headNode){
this.deleteHeadNode();
}else if (currentNode.next == null){//尾节点的时候
//直接的把新的尾节点的后置节点置为null 如 a->b>->c,删除c把b的后置next=null
precursorNode.next = null;
//新的尾节点赋给全局变量尾节点的引用
endNode = precursorNode;
}else {//如果是中间节点节点
/*
* 让前驱节点的下一个节点指向当前节点的下一个节点 如 a-->b-->c-->d 删除的时候b节点
* 前驱节点b和当前节点是c,然后b的下一个节点原来是c的但被删除了,所以指向当前节点c的下一个节点d
*/
precursorNode.next = currentNode.next;
//新的后继节点(c)指向前驱节点(a)
currentNode.next.prior = precursorNode;
}
sise--;
return true;
}
/**
* 测试类主方法
*
* @param args
*/
public static void main(String[] args) {
DoubleLInkedList doubleLInkedList = new DoubleLInkedList();
doubleLInkedList.addNode("A");
doubleLInkedList.addNode("B");
doubleLInkedList.addNode("C");
doubleLInkedList.addNode("D");
doubleLInkedList.traverseSingleLinkedListsByHeadNode();
System.out.println();
doubleLInkedList.traverseSingleLinkedListsByEndNode();
System.out.println();
Object obj = doubleLInkedList.findNode("B");
System.out.println(obj);
doubleLInkedList.addNodeBySelect(doubleLInkedList.getNode("E"), "D");
doubleLInkedList.traverseSingleLinkedListsByHeadNode();
System.out.println();
doubleLInkedList.traverseSingleLinkedListsByEndNode();
System.out.println();
doubleLInkedList.deleteNodeByData("A");
doubleLInkedList.traverseSingleLinkedListsByHeadNode();
System.out.println();
doubleLInkedList.traverseSingleLinkedListsByEndNode();
}
}
输出:
[ D<--->C<--->B<--->A ]
[ A<--->B<--->C<--->D ]
com.cym.datastructure.linked_list.DoubleLInkedList$Node@154617c
[ E<--->D<--->C<--->B<--->A ]
[ A<--->B<--->C<--->D<--->E ]
[ E<--->D<--->C<--->B ]
[ B<--->C<--->D<--->E ]