双向链表的增删改查,其中难点在于按顺序增加,如何保持节点中指向上一个节点的pre也能被修改成功
提示:以下是本篇文章正文内容,下面案例可供参考
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点
使用带 head 头的双向链表实现 –水浒英雄排行榜
代码如下(示例):
/**
* 节点类 data域 next域 pre域
*/
public class Node {
String name;//名字
String nikeName;//昵称
Integer no;//排序
Node next;//下一个节点的内存地址
Node pre;//上一个节点的内存地址
public Node(String name, String nikeName, Integer no) {
this.name = name;
this.nikeName = nikeName;
this.no = no;
}
//重写了toString方便遍历后输出节点的详细信息
@Override
public String toString() {
return "Node{" +
"name='" + name + '\'' +
", nikeName='" + nikeName + '\'' +
", no=" + no +
'}';
}
}
代码如下(示例):
包含头结点 以及无参数构造方法,在创建双向链表对象的时候自动创建data为空的头节点
public class DoubleLinkedList {
Node head;//双向链表中的头结点
public DoubleLinkedList() {
//双向链表无参构造,构造头结点
this.head = new Node(null, null, 0);
}
}
//获取链表头
public Node getHead(){
return head;
}
通过遍历获取链表的最后一个节点
//获取双向链表的尾节点
public Node getLastNode() {
Node lastNode = null;
if (head.next == null) {
System.out.println("链表为空");
}
Node temp = head.next;
//遍历链表
while (true) {
if (temp == null) {
break;
}
lastNode = temp;
temp = temp.next;
}
return lastNode;
}
这两个方法作用是为了后文两种遍历方法(从头遍历和从尾遍历)做铺垫
找到链表的最后节点,在他后面加上新节点
/无脑添加方法add
public void add(Node newNode) {
/**
* 思路
* 找到链表的最后有效节点让其next属性等于newNode
* 让newNode的pre属性等于最后的有效节点
*/
Node temp = head;//创建辅助节点
while (true) {
if (temp.next == null) {
break;
}
temp = temp.next;
}
temp.next = newNode;
newNode.pre = temp;
}
//遍历链表
public void show() {
if (head.next == null) {
System.out.println("链表为空");
return;
}
// Node temp = head;//创建辅助节点
// while (true) {
// if (temp.next == null) {
// break;
// }
// temp = temp.next;
// System.out.println(temp);
// }//两种循环方式均可以
Node temp = head.next;
while (true) {
if (temp == null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
//遍历链表根据pre
public void showByPre() {
Node lastNode = getLastNode();//获取链表尾部节点
while (true) {
if (lastNode == null | lastNode.no == 0) {
break;
}
System.out.println(lastNode);
lastNode = lastNode.pre;
}
}
以上的遍历方法是为了更好地检验我们顺序添加的方法addByNo是否在添加完成节点后的next和pre都赋值正确
public void upDate(Node node) {
if (head.next == null) {
System.out.println("链表为空,没有能被修改的数据");
return;
}
// Node temp=head.next;
// boolean flag=false;//标记
// //通过遍历找到被修改的节点
// while (true){
// if (temp==null){
// break;
// }
// if (temp.no==node.no){
// flag=true;
// break;
// }
// temp=temp.next;
// }
Node temp = head;//创建辅助节点
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no == node.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.next.name = node.name;
temp.next.nikeName = node.nikeName;
} else {
System.out.println("没找到对应点no");
}
}
两种遍历方式分别找到被修改节点和被修改节点的前一个节点
public void delete(int no) {
if (head.next == null) {
System.out.println("链表为空");
return;
}
/**
* 双向链表的删除分为三步 举例 连续节点1 2 3
* 第一步,如果要删除中间节点2,找到节点2
* 第二步 让2.next.pre=2.pre 让节点3的pre指向1
* 第二步 让2.pre.next=2.next; 让1的next指向3
*/
Node temp = head.next;//创建辅助节点
boolean flag = false;//常见标记如果flag为false 就是没找到
while (true) {
if (temp == null) {
break;
}
if (temp.no == no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
} else {
System.out.println("链表中没有这个no");
}
}
通过遍历找到被删除节点,将其前后两个节点的next和pre修改,让两者跳过被删除节点
//根据编号添加
public void addByNo(Node node) {
/**
* 思路
* 遍历数组找到node.no的后一个位置temp.next,再将node加到temp其后面,亦可以说成将node添加到temp.next前面都一样
*/
Node temp = head;//创建辅助节点
boolean flag = false;//创建标记
while (true) {
if (temp.next == null) {
break;//说明找到最后,直接就加在链表尾部了
}
if (temp.next.no > node.no) {
//找到添加位置
break;
}
if (temp.next.no == node.no) {
flag = true;//说明找到node.no已经存在不能重复
break;
}
temp = temp.next;
}
if (flag) {
System.out.println("链表中已存在节点");
} else {
node.next = temp.next;
node.pre = temp;
temp.next = node;
if (node.next != null) {
node.next.pre = node;//此处为了避免temp指向
//head时,链表中没有数据时候会出现的空指针异常
}
}
}
为了保证我们添加后的next和pre都要指向正确的位置所以必须做出判断
情况1当我们的双向链表没有有效节点时,我们添加节点 ,
这时我们只需要改动三个属性,新节点后并没有其他节点,所以后面节点的pre不需要修改,强行执行代码会出现空指针异常
情况2当 链表中有节点,同时我们的新节点要添加在head节点之后,这时必须执行第四行代码,让新节点后一个节点的pre正常赋值
public class Test {
public static void main(String[] args) {
DoubleLinkedList doubleLinkedList=new DoubleLinkedList();
Node n1=new Node("1","1",1);
Node n2=new Node("2","2",2);
Node n3=new Node("3","3",3);
Node n4=new Node("4","4",4);
doubleLinkedList.addByNo(n2);
doubleLinkedList.addByNo(n4);
doubleLinkedList.addByNo(n3);
doubleLinkedList.addByNo(n1);
doubleLinkedList.show();
System.out.println("通过pre遍历后");
doubleLinkedList.showByPre();
}
}
这里可以看出通过next属性或者pre属性 都可以正常遍历链表
如有问题或建议,欢迎留言