模拟双向链表详解

模拟双向链表详解

说明

  1. 双向链表通过两个指针依次将各个节点连接起来
  2. 使用next指针指向后一个节点,通过pre指针指向前一个节点
  3. 可以很好的解决删除节点时找到自身节点删除的问题,单向链表在删除某个节点时需要找到这个节点的前一个节点,而双向链表则不用
  4. 每一个节点实质为内存中的一个节点实例对象,将每个实例对象的引用(地址)互相连接起来,看起来像一条链子一样,称为链表
  5. 一个节点除了存储本身的数据域外,还要存储其他节点的地址引用

节点

//节点类
//则每一个Hero实例就是一个节点
class HeroNode2 {

    //编号
    public int no;
    //姓名
    public String name;
    //昵称
    public String nickedName;
    //指针域,存储的实则是一个地址,指向下一个节点
    public HeroNode2 next;
    //指针域,定义指向前一个节点的指针,存储的也是前一个节点的地址
    public HeroNode2 pre;

    public HeroNode2() {
    }

    public HeroNode2(int no, String name, String nickedName) {
        this.no = no;
        this.name = name;
        this.nickedName = nickedName;
    }

    //显示节点信息
    @Override
    public String toString() {
        return "Hero{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickedName='" + nickedName + '}';
    }
}

双链表类模拟

//模拟双向链表
class DoubleLinkedList {
    //创建一个头节点,不存放任何数据
    public HeroNode2 head = new HeroNode2();

    //按照英雄编号添加节点
    //向链表中添加元素,按照英雄的编号大小添加
    //思路:
    //1.难点在于寻找新节点要插入的位置
    //2.考虑将新节点插入到temp 和 temp.next之间
    public void addByOrder(HeroNode2 hero) {
        //因为头节点不能动,因此还是需要一个辅助变量temp指向head
        HeroNode2 temp = head;
        //定义一个表示位flag标识节点是否能添加成功,默认为false
        boolean flag = false;
        while (true) {
            //如果已经遍历到最后一个节点,则将新节点添加到temp的后面
            if (temp.next == null) {
                break;
            }
            //如果要添加的节点的标号大于temp,小于temp.next的编号,则找到temp
            if (temp.next.no > hero.no) {
                break;
                //如果要添加的节点的编号已经存在,则不能添加
            } else if (temp.next.no == hero.no) {
                flag = true;
                break;
            }
            //temp后移
            temp = temp.next;
        }
        if (flag) {
            //如果flag为真,则表示要加入的节点编号已经存在
            System.out.println("该节点已经存在~~");
        } else {
            //循环结束后则已经找到新节点要插入的位置
            if (temp.next != null) {
                temp.next.pre = hero;
            }
            hero.next = temp.next;
            temp.next = hero;
            hero.pre = temp;
        }
    }


    //遍历双向链表
    //显示链表
    public void list() {
        //先判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空~~");
            return;
        }
        //因为头节点不能动,因此还是需要一个辅助遍历temp(类似一个指针,每次指向下一个节点)
        //temp指向第一个节点
        HeroNode2 temp = head.next;
        //如果链表不为空,则遍历链表
        while (true) {
            //如果链表为空,则结束遍历
            if (temp == null) {
                break;
            }
            //输出链表内容
            System.out.println(temp);
            //temp后移指向下一个节点
            temp = temp.next;
        }
    }

    //向链表中添加数据,添加到链表的最后边
    //编写向双链表中添加节点的方法,不考虑插入元素的位置,直接插入链表的最后
    public void add(HeroNode2 hero) {
        //因为head头节点不能动,所以需要创建一个临时变量
        HeroNode2 temp = head;

        //先遍历链表找到链表的最后,然后将这个节点添加
        while (true) {
            if (temp.next == null) {
                break;
            }
            //如果不是最后一个节点,则temp指向的节点后移
            temp = temp.next;
        }
        //循环结束后找到链表的最后一个元素
        //将这个节点添加到链表
        temp.next = hero;
        hero.pre = temp;
    }

    //修改节点
    //编写修改链表中节点的方法
    public void update(HeroNode2 newHero) {
        //判断链表是否为空
        if (head.next == null) {
            System.out.println("链表是空的~~");
            return;
        }
        //因为头节点不能动,因此创建一个辅助变量temp
        HeroNode2 temp = head.next;
        //创建一个标志位flag判断是否找到要修改的节点
        boolean flag = false;
        while (true) {
            //如果已经遍历完链表,还没有找到,直接返回
            if (temp == null) {
                break;
            }
            //如果找到需要修改的节点
            if (temp.no == newHero.no) {
                flag = true;
                break;
            }
            //指针后移
            temp = temp.next;
        }
        //如果找到需要修改的节点
        if (flag) {
            temp.name = newHero.name;
            temp.nickedName = newHero.nickedName;
        } else {
            //如果没有找到
            System.out.println("要修改的节点不存在~~");
        }
    }

    //删除指定的节点
    //编写方法删除链表中的节点(根据节点的编号)
    public void delete(int no) {
        //判断链表是否为空
        if (head.next == null){
            System.out.println("链表为空,不能删除~~");
            return;
        }
        //因为头节点不能动,定义辅助变量temp
        HeroNode2 temp = head.next;
        //定义标志位判断是否找到要删除的节点
        boolean 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;
            //如果删除的不是最后一个节点
            //如果是最后一个节点,会出现空指针异常
            if (temp.next != null){
                temp.next.pre = temp.pre;
            }
        } else {
            //没找到要删除的节点
            System.out.println("要删除的节点不存在~~");
        }

    }

}

测试类

public static void main(String[] args) {
        //测试,创建四个节点
        HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
        HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
        HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
        HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");

        //创建一个双向链表
        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();

        //向双向链表中添加元素
//        doubleLinkedList.add(hero1);
//        doubleLinkedList.add(hero2);
//        doubleLinkedList.add(hero3);
//        doubleLinkedList.add(hero4);

        //按照英雄编号向链表中添加元素
        doubleLinkedList.addByOrder(hero1);
        doubleLinkedList.addByOrder(hero3);
        doubleLinkedList.addByOrder(hero4);
        doubleLinkedList.addByOrder(hero2);


        //显示双向链表元素
        doubleLinkedList.list();

        HeroNode2 newHero = new HeroNode2(4, "小林", "豹子头~~");

        //修改链表中的节点
        System.out.println("修改节点~~");
        doubleLinkedList.update(newHero);
        doubleLinkedList.list();

        //删除某个节点
        System.out.println("删除节点~~");
        doubleLinkedList.delete(4);
        doubleLinkedList.list();

    }
}

posted @ 2021-05-05 21:27  mx_info  阅读(87)  评论(0)    收藏  举报