提示:以下是本篇文章正文内容,Java系列学习将会持续更新
一、双向链表
双向遍历,既可以从前向后遍历,也可以从后往前遍历。(工作中常用)
优点:可以找到前驱和后继,可进可退;
缺点:增加删除节点复杂,需要多分配一个指针存储空间。
二、代码实现
/**
* 节点类,一个节点存放一个元素的值、前驱、后继
*/
public class Node {
//保存上一个车厢的地址
Node prev;
//存放具体数据
int val;
//保存下一个车厢的地址
Node next;
public Node(int val) {
this.val = val;
}
public Node(Node prev, int val, Node next){
this.prev = prev;
this.val = val;
this.next = next;
}
}
/**
* 双向链表
*/
public class DoubleLinkedList {
//链表中元素个数
private int size;
//头节点
private Node head;
//尾节点
private Node tail;
/**** 添加 *********************/
//头插法
public void addFirst(int val){
//新建的node的下一个节点指向原本的头节点head
Node node = new Node(null, val, head);
//当链表为空时
if(head == null){
//链表的尾节点也是node
tail = node;
}else{
//原本的头节点head的上一个节点指向node
head.prev = node;
}
//链表的头节点是node
head = node;
size ++;
}
//尾插法
public void addLast(int val){
Node node = new Node(tail, val, null);
if(head == null){
head = node;
}else{
tail.next = node;
}
tail = node;
size ++;
}
//中间插入
public void addIndex(int index,int val){
if(index < 0 || index > size){
System.err.println("add index illegal!");
return;
}
if(index == 0){
addFirst(val);
return;
}
if(index == size){
addLast(val);
return;
}
//找待插位置的前驱
Node nodePrev = nodeOf(index-1);
//A B C
//B的前驱 = A, B的后继 = C
Node node = new Node(nodePrev, val, nodePrev.next);
//A的后继 = B
nodePrev.next = node;
//C的前驱 = B
node.next.prev = node;
size ++;
}
/**** 删除 *********************/
//删除索引处的元素
public void removeIndex(int index){
if(rangeCheck(index)){
removeNode(nodeOf(index));
}else{
System.err.println("remove index illegal!");
}
}
//删除链表中的第一个value值
public void removeValueOnce(int val){
for(Node x = head; x != null; x = x.next){
if(x.val == val){
removeNode(x);
return;
}
}
}
//删除链表中的全部value值
public void removeValueAll(int val){
for(Node x = head; x != null;){
if(x.val == val){
//需要暂存x.next的地址
Node tmp = x.next;
//删完x节点后,x.next指向null
removeNode(x);
x = tmp;
}else{
x = x.next;
}
}
}
/**** 查找 *********************/
//返回索引处value的值
public int get(int index){
if(rangeCheck(index)){
return nodeOf(index).val;
}else{
System.err.println("get index illegal!");
return -1;
}
}
//判断链表中是否有value这个数据
public boolean contains(int val){
for(Node s = head; s != null; s = s.next){
if(s.val == val)
return true;
}
return false;
}
/**** 修改 *********************/
//将链表中索引为index的元素修改,并返回修改前的值
public int set(int index,int newVal){
if(rangeCheck(index)){
int oldVal = nodeOf(index).val;
nodeOf(index).val = newVal;
return oldVal;
}else{
System.err.println("set index illegal!");
return -1;
}
}
/**** 私有方法 *****************/
//返回下标在index处的节点
private Node nodeOf(int index){
Node ret = null;
if(index < size/2){
//从头开始正向遍历
ret = head;
for(int i=0; i<index; i++){
ret = ret.next;
}
}else{
//从尾开始逆向遍历
ret = tail;
for(int i=size-1; i>index; i--){
ret = ret.prev;
}
}
return ret;
}
//判断下标是否越界
private boolean rangeCheck(int index){
if(index < 0 || index >= size){
return false;
}else{
return true;
}
}
//将传入的node节点删掉
private void removeNode(Node node){
//分治思想
Node nodePrev = node.prev;
Node nodeNext = node.next;
//先处理node的左半边(node的前驱指向,nodePrev的后继指向)
if(nodePrev == null){
this.head = nodeNext;
}else{
nodePrev.next = nodeNext;
node.prev = null;
}
//再处理node的右半边(node的后继指向,nodeNext的前驱指向)
if(nodeNext == null){
this.tail = nodePrev;
}else{
nodeNext.prev = nodePrev;
node.next = null;
}
//两条if语句的处理,最终node的前驱和后继都指向null,释放了空间
size --;
}
/** 打印 **/
public String toString(){
String ret = "";
Node tmp = head;
while(tmp != null){
ret += tmp.val + " -> ";
tmp = tmp.next;
}
ret += "NULL";
return ret;
}
/** 返回链表的元素个数 **/
public int size(){
return this.size;
}
}
总结:
提示:这里对文章进行总结:
以上就是今天的学习内容,本文是Java数据结构的双向链表。之后的学习内容将持续更新!!!