java双向链表的增删改查,顺序增加图文详解

it2025-02-26  24

文章目录

前言一、双向链表是什么?二、案例1.节点类Node实现2.双向链表类DoubleLinkedLinst3.获取链表头,尾节点方法4.无脑增加方法add5.两个遍历方法(从头遍历和从尾遍历)6.修改节点upDate方法7删除方法delete()8.[重点菜]按顺序添加测试 总结


前言

双向链表的增删改查,其中难点在于按顺序增加,如何保持节点中指向上一个节点的pre也能被修改成功


提示:以下是本篇文章正文内容,下面案例可供参考

一、双向链表是什么?

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点

二、案例

使用带 head 头的双向链表实现 –水浒英雄排行榜

1.节点类Node实现

代码如下(示例):

/** * 节点类 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 + '}'; } }

2.双向链表类DoubleLinkedLinst

代码如下(示例): 包含头结点 以及无参数构造方法,在创建双向链表对象的时候自动创建data为空的头节点

public class DoubleLinkedList { Node head;//双向链表中的头结点 public DoubleLinkedList() { //双向链表无参构造,构造头结点 this.head = new Node(null, null, 0); } }

3.获取链表头,尾节点方法

获取头 //获取链表头 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; }

这两个方法作用是为了后文两种遍历方法(从头遍历和从尾遍历)做铺垫

4.无脑增加方法add

找到链表的最后节点,在他后面加上新节点

/无脑添加方法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; }

5.两个遍历方法(从头遍历和从尾遍历)

从头遍历show() //遍历链表 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都赋值正确

6.修改节点upDate方法

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"); } }

两种遍历方式分别找到被修改节点和被修改节点的前一个节点

7删除方法delete()

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修改,让两者跳过被删除节点

8.[重点菜]按顺序添加

//根据编号添加 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属性 都可以正常遍历链表


总结

如有问题或建议,欢迎留言

最新回复(0)