java实现双向链表

Posted 知识追寻者[同公众号]

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java实现双向链表相关的知识,希望对你有一定的参考价值。

一 前言

之前知识知识追寻者写了一篇单链表的实现,感觉不是很满意,写的逻辑不够清晰,有些地方实现的不过好,不能连成一个整体,伪单链表;为此研究了一会双向链表的简单实现;本篇的实现方式是以方法的形式展现,读者可以将其整合为一个类;

二 双向链表简介

双向链表的定义是,一个节点有两个方向,分别储存当前节点的前驱节点,和后续节点;双向链表的删除只需要指定前驱节点,或者后续节点就可以进行删除操作;但是缺点也很明显每次添加节点时都需要2个指向,额外增加了内存空间消耗;

技术图片

三 双向链表的实现

3.1 定义链表节点

  1. 定义data存储数据,知识追寻者使用的时int类型,读者可以改为object类型;
  2. 定义前驱节点previous
  3. 定义后续节点next
/**
 * @Author lsc
 * <p> 双向链表节点 </p>
 */
public class DoubleNode {

    //数据
    private Integer data;
    //后续节点节点
    private DoubleNode next;
    //前驱节点
    private DoubleNode previous;
    // 省略set get
}    

3.2 插入头节点

思路如下

  1. 将新节点的前驱节点指向nul
  2. 新节点的后续节点指向表头
  3. 将表头的前驱节点指向新节点
public class DoubleList {

    private DoubleNode head;

    /* *
     * @Author lsc
     * <p> 表头插入节点
     *  思路: 1 将新节点的前驱节点指向null,
     *        2新节点的后续节点指向表头
     *        3 将表头的前驱节点指向新节点
     * </p>
     * @Param [data]
     * @Return void
     */
   public void addFirst(int data){
       // 创建新节点
       DoubleNode newNode = new DoubleNode();
       // 为新节点添加数据
       newNode.setData(data);
       // 如果表头为空直接将新节点作为头
       if (head==null){
           head = newNode;
       }else {
           // 将新节点的前驱节点指向null(声明的时候本来就是null)
           //新节点的后续节点指向表头
           newNode.setNext(head);
           // 将表头的前驱节点指向新节点
           head.setPrevious(newNode);
           // head重新赋值
           head = newNode;
       }
   }
     /* *
     * @Author lsc
     * <p>顺序打印链表
      思路:从链表的头遍历到链表的尾巴
     * </p>
     * @Param []
     * @Return void
     */
   public void displayNext(){
       // 将表头作为当前节点
       DoubleNode currentNode = head;
       // 遍历链表
       while (currentNode!=null){
           // 打印数据
           System.out.println(currentNode.getData());
           // 将下一个节点作为当前节点
           currentNode = currentNode.getNext();
       }
   }
}   

测试代码如下,往前插入数据,打印出来就是倒序

  public static void main(String[] args) {
        DoubleList doubleList = new DoubleList();
        for (int i = 0; i <5 ; i++) {
            doubleList.addFirst(i);
        }
        doubleList.displayNext();
    }

结果

4
3
2
1
0

3.3 插入尾节点

思路如下

  1. 表尾的后续节点指向新节点
  2. 新节点的前驱节点指向表尾
  3. 新节点的后续节点指向null
/* *
     * @Author lsc
     * <p> 表尾插入节点
     * 思路 : 1 表尾的后续节点指向新节点
     *        2 新节点的前驱节点指向表尾
     *        3 新节点的后续节点指向null
     * </p>
     * @Param [data]
     * @Return void
     */
   public void addLast(int data){
       // 创建新节点
       DoubleNode newNode = new DoubleNode();
       // 为新节点添加数据
       newNode.setData(data);
       // 如果表头为空直接将新节点作为头
       if (head==null){
           head = newNode;
       }else {
           DoubleNode currentNode = head;
           //寻找尾节点
           while (currentNode.getNext()!=null){
               currentNode = currentNode.getNext();
           }
           //表尾的后续节点指向新节点
           currentNode.setNext(newNode);
           //新节点的前驱节点指向表尾
           newNode.setPrevious(currentNode);
       }
   }

测试代码如下,往表为插入数据,也就是顺序打印

  public static void main(String[] args) {
        DoubleList doubleList = new DoubleList();
        for (int i = 0; i <5 ; i++) {
            //doubleList.addFirst(i);
            doubleList.addLast(i);
        }
        doubleList.displayNext();
    }

结果

0
1
2
3
4

3.4 获取链表长度

思路 :遍历链表,一个节点代表一个单位的长度

    /* *
     * @Author lsc
     * <p> 获得链表长度
     * 思路:遍历链表,一个节点代表一个单位的长度
     *  </p>
     * @Param []
     * @Return int
     */
   public int length(){
       int length = 0;
       // 当前节点
       DoubleNode currentNode = head;
       while (currentNode!=null){
           // 一个节点 length 长度就加1
            length++;
            // 将下一个节点作为当前节点
            currentNode = currentNode.getNext();
       }
       return length;
   }

3.5 指定位置插入节点

思路: 假设在BC直接插入新节点N

  1. ? B 节点的后续节点指向 N
  2. N 节点 的前驱节点指向 B
  3. N 节点的后续节点指向 C
  4. C 节点的前驱节点指向 N

重要的也就是要找到B节点的位置,转存C节点;

技术图片

/* *
    * @Author lsc
    * <p> 指定位置插入节点
    * 思路: 假设在AB直接插入新节点N
    *       1 A 节点的后续节点指向 N
    *       2 N 节点 的前驱节点指向 A
    *       3 N 节点的后续节点指向 B
    *       4 B 节点的前驱节点指向 N
    * 重点也是找到A节点的位置
    *  </p>
    * @Param [data]
    * @Return void
    */
   public void add(int data, int index){
       // 索引超出,非法
       if (index<0 || index>length()){
           System.out.println("非法索引");
           return;
       }
       // 如果索引为0,调用addFirst方法
       if (index==0){
           addFirst(data);
           return;
       }
       // 如果索引等于链表的长度,调用addLast方法
       if (index==length()){
           addLast(data);
           return;
       }
       // 创建新节点
       DoubleNode newNode = new DoubleNode();
       // 为新节点添加数据
       newNode.setData(data);
       // 当前节点
       DoubleNode currentNode = head;
       // 定义指针
       int point = 0;
       // 寻找插入新节点的上一个节点A
       while ((index-1)!= point){
           currentNode = currentNode.getNext();
           point++;
       }
       // 转存当前节点的后续节点
       DoubleNode nextNode = currentNode.getNext();
       // 当前节点的后续节点指向新节点
       currentNode.setNext(newNode);
       // 新接的前驱节点指向当前节点
       newNode.setPrevious(currentNode);
       // 新节点的后续节点指向转存的节点
       newNode.setNext(nextNode);
       // 转存节点的前驱节点指向新节点
       nextNode.setPrevious(newNode);

   }

测试代码

    public static void main(String[] args) {
        DoubleList doubleList = new DoubleList();
        for (int i = 0; i <5 ; i++) {
            doubleList.addLast(i);
        }
        doubleList.add(666,3);
        doubleList.displayNext();
    }

结果

0
1
2
666
3
4

3.6 删除表头

思路如下

  1. 创建一个临时节点,存储表头的后续节点
  2. 将临时节点的前驱节点指向null
  3. 将临时节点赋值给表头
 /* *
    * @Author lsc
    * <p> 删除表头
     思路: 1 创建一个临时节点,存储表头的后续节点
     *     2 将临时节点的前驱节点指向null
     *     3 将临时节点赋值给表头
    * </p>
    * @Param []
    * @Return void
    */
   public void removeFirst(){
       if (length()==0){
           return;
       }
       // 只有一个节点直接清空表头
       if (length()==1){
           head=null;
           return;
       }
       // 创建一个临时节点,存储表头的后续节点
       DoubleNode temNode = head.getNext();
       // 将临时节点的前驱节点指向null
       temNode.setPrevious(null);
       // 将临时节点赋值给表头
       head = temNode;
   }

测试代码

    public static void main(String[] args) {
        DoubleList doubleList = new DoubleList();
        for (int i = 0; i <5 ; i++) {
            doubleList.addLast(i);
        }
        doubleList.removeFirst();
        doubleList.displayNext();
    }

结果

1
2
3
4

3.7 删除表尾

思路

  1. 找到表尾的前驱节点
  2. 将表尾的前驱节点的后续节点置为null
 /* *
    * @Author lsc
    * <p> 删除表尾
        思路: 1 找到表尾的前驱节点
        *     2 将表尾的前驱节点的后续节点置为null
        *     3
    * </p>
    * @Param []
    * @Return void
    */
   public void removeLast(){
        if (length()==0){
            return;
        }
       // 只有一个节点直接清空表头
       if (length()==1){
           head=null;
           return;
       }
       DoubleNode previousNode = head;
       // 寻找尾节点的前驱节点
       while (previousNode.getNext().getNext()!=null){
           previousNode = previousNode.getNext();
       }
       previousNode.setNext(null);
   }

测试代码

    public static void main(String[] args) {
        DoubleList doubleList = new DoubleList();
        for (int i = 0; i <5 ; i++) {
            //doubleList.addFirst(i);
            doubleList.addLast(i);
        }
        doubleList.removeLast();
        doubleList.displayNext();
    }

结果

0
1
2
3

3.8 删除指定节点

思路: 假设有BCD节点要删除B节点

  1. 将 B 节点的后续节点指向 D节点
  2. 将D节点前驱节点指向B节点

重要的也就是要找到B节点位置,转存D节点;

技术图片

/* *
    * @Author lsc
    * <p>  指定位置删除节点
      思路: 假设有ABC节点要删除B节点
      *     1 将 A 节点的后续节点指向 C节点
      *     2 将C节点前驱节点指向A节点
    * </p>
    * @Param [index]
    * @Return void
    */
   public void remove(int index){
       if (index<0 || index>=length()){
           System.out.println("非法索引");
           return;
       }
       // 头节点
       if (index==0){
           removeFirst();
           return;
       }
       // 尾节点
       if (index==(length()-1)){
           removeLast();
           return;
       }
       // 欲想删除节点的前驱节点
       DoubleNode previousNode = head;
       // 定义指针
       int point = 0;
       // 寻找新节
       while ((index-1)!=point){
           previousNode = previousNode.getNext();
           point++;
       }
       // 欲想删除节点的后续节点
       DoubleNode nextNode = previousNode.getNext().getNext();
       // 将欲想删除节点的前驱节点的后续节点指向欲想删除节点的后续节点
       previousNode.setNext(nextNode);
       // 将欲想删除节点的后续节点的前驱节点指向欲想删除节点的前驱节点
       nextNode.setPrevious(previousNode);

   }

测试代码

    public static void main(String[] args) {
        DoubleList doubleList = new DoubleList();
        for (int i = 0; i <5 ; i++) {
            //doubleList.addFirst(i);
            doubleList.addLast(i);
        }
        doubleList.remove(3);
        doubleList.displayNext();
    }

结果

0
1
2
4

以上是关于java实现双向链表的主要内容,如果未能解决你的问题,请参考以下文章

链表的java实现(单向双向链表,单向链表的反转)

Java:双向链表反转实现

Java数据结构—— 双向链表及带傀儡节点双向链表的实现

Java实现双向链表的基本操作

十双向链表的java实现

java-双向链表逻辑