数据结构与算法-双向链表

本文讲述了双向链表在查找、添加和删除节点方面的优势,以及与单向链表的区别,强调了双向链表的双向查找和自我删除功能。
摘要由CSDN通过智能技术生成

1.简介

在使用带头节点的单向链表查找时查找的方向只能是一个方向,而双向链表可以向前或向后查找。例如单向链表只能查到一个节点的下一个节点,但是双向链表不仅可以查到下一个节点,还可以查到上一个节点。

在删除节点时,单向链表不能自我删除,需要借助辅助节点temp,因为单向链表就能找到下一个节点,把这个节点删了就不能直接找到下一个节点了。而双向链表可以自我删除,反正可以找到前一个节点,也可以找到后一个节点,这个节点自己就可以实现删除自我,让自己上一个节点和下一个节点连起来。

比如单向链表长这样:一个next可以找到下一个节点

双向链表可以长这样:一个pre指向前一个节点,一个next指向后一个节点

它可以从前往后查找,也可以从后往前查找

2.思路

①遍历:在遍历时遍历方式和单链表一样,只是它可以向前查找,也可以向后查找

②添加:在添加节点时,默认添加到双向链表最后的情况,先找到双向链表最后的节点,找到以后先使temp.next=newHeroNode,再使newHeroNode.pre=temp。就是原来最后一个节点的下一个节点是新节点,新节点的前一个是原来最后一个节点。

③修改:和单向链表一样

④删除:比如删除编号为5的节点,先找到要删除的这个节点temp,然后使temp.pre.next=temp.next,再使temp.next.pre=temp.pre。这样遍历的时候就跳过要删除的节点了。

要注意:在删除最后一个节点时如果要找temp.next.pre就会找不到。所以如果temp.next==null时就不执行temp.next.pre=temp.pre这行代码。

这两行代码可以记成:等号前面是更长的,有next有pre,等号后面是前面的头和尾,没有中间的next或者pre。

temp.next.pre=temp.pre

temp.pre.next=temp.next

⑤考虑编号顺序添加节点:找到要添加位置的前一个节点temp,找到以后新节点.next=temp.next,判断temp.next是否为空,如果不为空就temp.next.pre=新节点。然后temp.next=新节点,新节点.pre=temp。

3.代码

package com.xjj.linkedlist;

public class DoubleLinkedListDemo {
    public static void main(String[] args) {
        //测试
        System.out.println("双向链表");
        //创建节点
        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.list();
        doubleLinkedList.add(hero1);
        doubleLinkedList.add(hero2);
        doubleLinkedList.add(hero3);
        doubleLinkedList.add(hero4);
        doubleLinkedList.list();
        //修改双向链表
        System.out.println("修改双向链表");
        HeroNode2 newHeroNode = new HeroNode2(2, "卢", "玉麒麟--");
        doubleLinkedList.update(newHeroNode);
        doubleLinkedList.list();
        //删除
        System.out.println("删除节点1");
        doubleLinkedList.delete(1);//删除第一个节点
        doubleLinkedList.list();
        System.out.println("删除节点4");
        doubleLinkedList.delete(4);//删除第四个节点
        doubleLinkedList.list();
        //按编号顺序添加
        System.out.println("按编号顺序添加");
        DoubleLinkedList doubleLinkedList1 = new DoubleLinkedList();
        doubleLinkedList1.addByOrder(hero2);
        doubleLinkedList1.addByOrder(hero4);
        doubleLinkedList1.addByOrder(hero3);
        doubleLinkedList1.addByOrder(hero1);
        doubleLinkedList1.list();
    }
}

class HeroNode2 {
    public int no;
    public String name;
    public String nickname;
    public HeroNode2 next;//指向下一个节点
    public HeroNode2 pre;//指向上一个节点

    //构造器
    public HeroNode2(int hNo, String hName, String hNickname) {
        this.no = hNo;
        this.name = hName;
        this.nickname = hNickname;
    }

    //重写toString来显示方法
    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
    }
}

class DoubleLinkedList {
    private HeroNode2 head = new HeroNode2(0, "", "");

    //返回头节点
    public HeroNode2 getHead() {
        return head;
    }

    //添加节点。不考虑编号顺序时,先找到当前链表的最后节点,再将这个节点的next指向新节点
    public void add(HeroNode2 heroNode) {
        //辅助遍历temp
        HeroNode2 temp = head;
        //遍历链表找到最后
        while (true) {
            if (temp.next == null) {//找到最后了就退出
                break;
            }
            //没到最后就后移到下一个节点
            temp = temp.next;
        }
        //退出while循环时temp就指向链表最后
        //我们要添加新节点,就将这个最后节点的next指向节点,将节点的前一个指向temp
        temp.next = heroNode;
        heroNode.pre = temp;
    }

    //添加节点,考虑编号顺序。也就是插入到链表非末尾位置
    public void addByOrder(HeroNode2 heroNode) {
        //辅助变量位于添加位置的前一个节点
        HeroNode2 temp = head;
        boolean flag = false;//标识添加的编号是否存在。如果存在就不能添加了
        while (true) {
            if (temp.next == null) {
                //已经在链表最后,要添加的节点可能已经在链表最后
                break;
            }
            if (temp.next.no > heroNode.no) {
                //temp下一个节点的编号大于要插入的节点编号,就是找到了,要在这个temp后面插入
                break;
            } else if (temp.next.no == heroNode.no) {
                //编号已存在
                flag = true;//编号存在
            }
            temp = temp.next;//后移
        }
        //判断编号是否已存在
        if (flag) {
            System.out.printf("准备插入的编号 %d 已经存在,无法添加\n", heroNode.no);
        } else {
            //新节点插入链表中temp的后面
            heroNode.next = temp.next;//新节点下一个指向插入节点
            if (temp.next != null) {
                temp.next.pre = heroNode;
            }
            temp.next = heroNode;//插入节点后一个的前一个指向新节点
            heroNode.pre = temp;//新节点前一个指向插入节点前一个
        }
    }

    //根据编号修改节点信息,即编号不能修改
    public void update(HeroNode2 newHeroNode) {
        //判断链表是否为空
        if (head.next == null) {
            //如果链表为空就不遍历了
            System.out.println("链表为空");
            return;
        }
        //找到需要修改的节点
        //辅助变量
        HeroNode2 temp = head.next;
        boolean flag = false;//判断是否找到该节点
        while (true) {
            if (temp == null) {
                break;//已经遍历完链表
            }
            if (temp.no == newHeroNode.no) {
                //找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //判断是否找到节点
        if (flag) {
            temp.name = newHeroNode.name;
            temp.nickname = newHeroNode.nickname;
        } else {
            System.out.printf("没有找到编号为 %d 的节点\n", newHeroNode.no);
        }
    }

    //删除节点
    //直接找到要删除的节点
    public void delete(int no) {
        //判断链表是否为空
        if (head.next == null) {
            //如果链表为空就不遍历了
            System.out.println("链表为空");
            return;
        }
        //找到需要修改的节点
        //辅助变量
        HeroNode2 temp = head.next;//直接指向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;//前一个的下一个直接变成下一个
            //temp.next.pre=temp.pre;//下一个的前一个变成前一个。但是有风险
            //如果删除最后一个节点,temp就没有next了。所以是删最后一个节点就不用执行下面的代码
            if (temp.next != null) {
                temp.next.pre = temp.pre;
            }
        } else {
            System.out.printf("没有找到编号为 %d 的节点\n", no);
        }
    }

    //显示链表,看有没有成功
    public void list() {
        //判断链表是否为空,如果为空就不遍历了
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //不为空就遍历
        //使用辅助变量来遍历
        HeroNode2 temp = head.next;//这里指向的是下一个
        while (true) {
            //看是否到链表最后
            if (temp == null) {
                break;
            }
            //不为空就输出节点信息
            System.out.println(temp);
            //后移
            temp = temp.next;
        }
    }
}

4.运行结果

5.提炼一下

①直接在链表末尾添加节点就是找到末尾节点temp,然后使temp.next=新节点,再使新节点.pre=temp

②在链表中间按编号顺序添加就是找到添加位置的前一个节点temp,找到以后新节点.next=temp.next,判断temp.next是否为空,如果不为空就temp.next.pre=新节点。然后temp.next=新节点,新节点.pre=temp。

②删除节点就遍历找到该节点temp后temp.next.pre=temp.pre,temp.pre.next=temp.next

③遍历以及修改节点与单链表的一样


加油加油^_^

  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值