手把手带你用Java实现双向链表

双向链表是对于单向链表的增强,建议先查看单向链表,掌握单向链表后再学习双向链表事半功倍

一、双向链表介绍

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

说人话,每个节点都有两个变量,一个指向前一个节点,一个指向后一个节点。

结构图
1

二、链表的应用示例

使用带head头的单向链表实现输入学生信息,查询时根据学生id顺序显示

  1. 添加节点:根据学生id将学生信息插入到指定位置(若id存在则提醒添加失败)
    在这里插入图片描述

    添加(创建)
    1.创建一个head头节点,作为单链表的头部
    2.找到新添加节点的位置,可以通过辅助变量(指针)完成,用temp保存(新节点的前一个节点)
    3.新节点.next = temp.next
    4.temp.next.pre = 新节点
    5.temp.next = 新节点
    6.新节点.pre = temp
    ==注意:第三步与第五步顺序不能反,若先将当前节点的next指向新节点,则原链表截断后的元素则丢失。==
    
  2. 修改节点

    1.找到将要修改的节点
    2.将信息给原节点
    
  3. 删除节点

    1.找到需要删除的节点。存为temp
    2.temp.pre.next = temp.next
    3.temp.next.pre = temp.pre;
    
  4. 查看节点

     通过遍历进行查询
    

三、代码实现

全部代码:


import java.util.Stack;

/**
 * 双向链表
 * @author 张家宝
 * @date 2020/4/9
 */
public class DoublyLinkedListDemo {
    public static void main(String[] args) {
        //乱序添加
        StudentNodeDoubly node1 = new StudentNodeDoubly(1,"唐僧");
        StudentNodeDoubly node3 = new StudentNodeDoubly(3,"猪八戒");
        StudentNodeDoubly node2 = new StudentNodeDoubly(2,"孙悟空");
        StudentNodeDoubly node4 = new StudentNodeDoubly(4,"沙僧");

        //创建链表
        DoublyLinkedList doublyLinkedList = new DoublyLinkedList();
        System.out.println("创建后查询:");
        doublyLinkedList.show();

        //将节点添加到链表中
        doublyLinkedList.add(node1);
        doublyLinkedList.add(node3);
        doublyLinkedList.add(node2);
        doublyLinkedList.add(node4);
        System.out.println("添加后查询:");
        doublyLinkedList.show();

        //修改节点
        StudentNodeDoubly node5 = new StudentNodeDoubly(4,"沙和尚");
        doublyLinkedList.update(node5);
        System.out.println("修改后查询:");
        doublyLinkedList.show();

        //删除节点
        doublyLinkedList.delete(2);
        System.out.println("删除后查询:");
        doublyLinkedList.show();

        //统计当前链表有效节点数
        System.out.println("当前链表有效节点数为:"+doublyLinkedList.size());

        //查找倒数第n个节点
        System.out.println("倒数第1个节点为:"+doublyLinkedList.findLastIndexNode(1));

//        //反转当前链表
//        System.out.println("反转后查询:");
//        doublyLinkedList.reverseList();
//        doublyLinkedList.show();

        //反转打印当前链表
        System.out.println("反转打印:");
        doublyLinkedList.reverseShow();

        //合并链表
        //创建第二个链表
        DoublyLinkedList doublyLinkedList2 = new DoublyLinkedList();
        StudentNodeDoubly node11 = new StudentNodeDoubly(6,"白骨精");
        StudentNodeDoubly node12 = new StudentNodeDoubly(7,"牛魔王");
        StudentNodeDoubly node13 = new StudentNodeDoubly(2,"孙悟空");
        doublyLinkedList2.add(node11);
        doublyLinkedList2.add(node12);
        doublyLinkedList2.add(node13);

        //进行合并
        doublyLinkedList.addAll(doublyLinkedList2);

        System.out.println("合并后打印:");
        doublyLinkedList.show();

    }
}

/**
 * 双向链表
 */
class DoublyLinkedList{
    //初始化一个头节点,头节点不存放具体数据
    private StudentNodeDoubly head = new StudentNodeDoubly(0,"");

    /**
     *  添加节点到单向链表
     *  思路:
     *  1.找到新添加节点的位置
     *  2.新节点.next = temp.next
     * 	3.temp.next.pre = 新节点
     * 	4.temp.next = 新节点
     * 	5.新节点.pre = temp
     *  注意:第二步与第四步顺序不能反,若先将当前节点的next指向新节点,则原链表截断后的节点则丢失。
     * @param studentNode
     */
    public void add(StudentNodeDoubly studentNode){
        //因为头节点不能动,所有我们需要一个辅助指针(变量)来协助找到添加的位置
        //单链表结构,我们找到的temp是位于  添加位置  的前一个节点。
        StudentNodeDoubly temp = head;
        //标志着添加编号是否存在,默认为false;
        boolean flag = false;
        while(true){
            //说明temp已经是链表的最后一个
            if(temp.next == null){
                break;
            }
            //找到要添加的位置,在temp后边添加节点
            if(temp.next.id > studentNode.id){
                break;
            }
            //说明该id已存在,更改验证存在标识为true
            else if(temp.next.id == studentNode.id){
                flag = true;
                break;
            }
            //指针后移,遍历当前链表
            temp = temp.next;
        }
        //判断是否进行添加操作
        if(flag){
            System.out.printf("待添加的学生id %d 已被占用,不可添加\n",studentNode.id);
        }else{
            //将新节点的下一个节点指向到新节点的next中
            studentNode.next = temp.next;
            //需判断当前节点后是否存在节点,若存在节点则需将当前节点之后的节点的前指针指向新节点(细品)
            if (temp.next != null) {
                //将当前节点的next.pre指向新节点
                temp.next.pre = studentNode;
            }
            //将当前节点的next指向到新节点
            temp.next = studentNode;
            //将新节点的pre 指向当前节点
            studentNode.pre = temp;
//            System.out.println("添加成功!");
        }
    }

    /**
     * 修改节点,根据id进行修改,因此id不可更改
     * 思路:
     *  1.找到将要修改的节点
     *  2.将信息给原节点
     * @param studentNode
     */
    public void update(StudentNodeDoubly studentNode){
        //判断当前链表是否为空
        if(head.next == null){
            System.out.println("链表为空  ~~~~~  ");
            return;
        }
        //找到将要修改的节点
        StudentNodeDoubly temp = head.next;
        //标识是否找到该节点
        boolean flag = false;
        while(true){
            //说明temp已经是链表的最后一个
            if(temp == null){
                break;
            }
            //将节点进行比较
            if(temp.id == studentNode.id){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //判断是否可以进行修改操作
        if(flag){
            temp.name = studentNode.name;
        }else {
            System.out.printf("没有找到 id为 %d 的节点,不可修改\n",studentNode.id);
        }
    }

    /**
     * 删除节点  ,根据id删除节点
     * 思路:
     * 1.如果指针指向了要删除的节点则无法删除,所有需要找到要删除的节点的上一个节点
     * 2.temp.next = temp.next.next
     * @param id
     */
    public void delete(int id){
        //因为头节点不能动,所有我们需要一个辅助指针(变量)来协助找到添加的位置
        //单链表结构,我们找到的temp是位于 头节点之后的第一个节点。
        StudentNodeDoubly temp = head.next;
        //标识是否找到待删除的节点
        boolean flag = false;
        while (true){
            //说明temp已经是链表的最后一个
            if(temp == null){
                break;
            }
            //将节点进行比较
            if(temp.id == id){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //判断是否可以进行删除操作
        if(flag){
            //当前节点的前一个节点的下一个节点指向当前节点的下一个节点
            temp.pre.next = temp.next;
            //判断当前节点后是否存在节点,若存在节点则需将 当前节点之后的节点的前指针指向当前节点的前一个节点(细品)
            if(temp.next!=null){
                temp.next.pre = temp.pre;
            }
        }else {
            System.out.printf("没有找到 id为 %d 的节点,无法删除\n",id);
        }
    }

    /**
     * 显示链表中所有节点
     */
    public void show(){

        //判断当前链表是否为空
        if(head.next == null){
            System.out.println("链表为空  ~~~~~  ");
            return;
        }
        System.out.println("-----------------显示链表开始-------------------");
        //因为头节点不能动,所有我们需要一个辅助指针(变量)来协助找到添加的位置
        //单链表结构,我们找到的temp是位于  添加位置  的前一个节点。
        StudentNodeDoubly temp = head;
        while (true){
            //说明temp已经是链表的最后一个
            if(temp == null){
                break;
            }
            //将节点进行打印
            System.out.println(temp);
            temp = temp.next;
        }
        System.out.println("-----------------显示链表结束-------------------");
    }

    /*----------- 拓展 ------------*/

    /**
     * 获取当前链表有效节点数
     * 思路:遍历链表中的所有节点
     * @return
     */
    public int size(){
        //空链表
        if(head.next == null){
            return 0;
        }
        int size = 0;
        //定义一个辅助指针(变量)
        StudentNodeDoubly temp = head.next;
        while (temp != null){
            size++;
            //遍历当前链表下的节点
            temp = temp.next;
        }
        return size;
    }

    /**
     * 找到单链表中倒数第index个节点
     * 思路:
     *  1.判断节点是否存在
     *  2.从第一个节点开始遍历,遍历(size - index)个节点
     * @param index
     * @return
     */
    public StudentNodeDoubly findLastIndexNode(int index){
        //空链表
        if(head.next == null){
            return null;
        }
        //判断节点数是否存在
        if(index<=0 || index > size()){
            return null;
        }
        //遍历第size - index的位置,该位置就是我们要找的节点
        StudentNodeDoubly temp = head.next;
        for (int i = 0;i < size() - index;i++){
            temp = temp.next;
        }
        return temp;
    }

    /**
     * 反转当前链表
     */
    public void reverseList(){
        //空链表或只有一个节点直接返回
        if(head.next == null || head.next.next == null){
            return;
        }
        //定义一个辅助指针(变量)
        StudentNodeDoubly temp = head.next;
        //指向当前节点的下一个节点,避免节点丢失
        StudentNodeDoubly next = null;
        //创建临时链表头节点
        StudentNodeDoubly reverseHead = new StudentNodeDoubly(0,"");
        //遍历原链表中的每一个节点将其取出放在临时链表reverseHead的最前端
        while (temp != null){
            //暂时保存当前节点后的节点
            next = temp.next;
            //将temp的下一个链表指向新链表的最前端
            temp.next = reverseHead.next;
            //将temp连接到新链表上
            reverseHead.next = temp;
            //遍历当前链表下的节点(指针后移)
            temp = next;
        }
        head.next = reverseHead.next;
    }

    /**
     * 反向打印单链表
     * 使用Stack栈,利用Stack栈先进后出原则进行反向打印,反向打印不影响源数据结构
     */
    public void reverseShow(){
        //空链表
        if(head.next == null){
            return;
        }
        //创建一个栈,将各个节点压入栈
        Stack<StudentNodeDoubly> stack = new Stack<StudentNodeDoubly>();
        StudentNodeDoubly temp = head.next;
        while(temp != null){
            stack.add(temp);
            //指针后移
            temp = temp.next;
        }
        while (stack.size()>0){
            //打印节点信息
            System.out.println(stack.pop());
        }
    }

    /**
     * 合并/添加一个有序的单链表,合并后依然有序
     */
    public void addAll(DoublyLinkedList doublyLinkedList){
        //判断待添加链表有效节点数的数量
        if(doublyLinkedList.size()==0)
        {
            return;
        }
        //将待添加链表加入当前链表中
        while (doublyLinkedList.size()>0){
            StudentNodeDoubly temp = doublyLinkedList.findLastIndexNode(1);
            doublyLinkedList.delete(temp.id);
            this.add(temp);
        }
    }
}


/**
 * 定义一个StudentNode, 每个StudentNode对象就是一个链表中的节点
 */
class StudentNodeDoubly{
    public int id;
    public String name;
    public StudentNodeDoubly next;
    public StudentNodeDoubly pre;

    public StudentNodeDoubly(int id,String name){
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "StudentNode{id=" + id +", name='" + name + "'}";
    }
}
发布了27 篇原创文章 · 获赞 11 · 访问量 4395

猜你喜欢

转载自blog.csdn.net/qq_26020387/article/details/105403354