03-java数据结构之链表的学习(单链表、双链表等)

1、链表

1.1 链表的介绍

在这里插入图片描述

  • 1)链表是以节点的方式来存储
  • 2)每个节点包含data域,next域:指向下一个节点
  • 3)发现链表的各个节点不一定是连续存储
  • 4)链表分头节点的链表和没有头节点的链表

2、单链表

在这里插入图片描述
步骤一、创建节点类(包含ni,name,nickName,next)

// 定义HeroNode,每一个HeroNode对象就是一个节点
class HeroNode{
    public int no;
    public String name;
    public String nickName;
    public HeroNode next;  // 指向下一个节点

    //构造器
    public HeroNode(int no, String name, String nickName) {
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }


    //为了显示方便,重写toString

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName +
                '}';
    }

步骤二、创建管理节点的类

// 定义一个LinkedList来管理HeroNode
class SingleLinkedList{
    // 先初始化头节点,head节点不能动,也不能放其他具体信息
    HeroNode head=new HeroNode(0,"","");
    // 显示操作
    // 添加操作
    // 修改操作
    // 删除操作
}

2.1、单链表的显示

    //显示链表
    public void list(){
        // 首先判断链表是否为空
        if(head.next==null){
            System.out.println("链表为空~~~");
            return;
        }
        // 创建一个辅助变量来遍历链表(因为head头节点不能改变)
        HeroNode temp=head.next;
        while (true){
            if(temp==null){
                System.out.println("链表遍历完了~~~");
                break;
            }
            // 输出节点信息
            System.out.println(temp.toString());
            // 后移
            temp=temp.next;
        }
    }

2.2、单链表的添加操作

2.2.1、直接添加到链表的尾部

在这里插入图片描述

    // 添加节点到单链表
    // 思路:不考虑编号顺序问题的话首先遍历到链表的最后,让最后节点的next指向新的节点
    public void add(HeroNode heroNode){
        // 创建一个辅助变量来遍历链表(因为head头节点不能改变)
        HeroNode temp=head;
        while(true){
            // 找到链表的最后
            if(temp.next==null){
                break;
            }
            // 如果链表没有到最后,那么temp需要后移
            temp=temp.next;
        }
        // 当循环退出之后,证明链表到最后了
        // 将这个最后节点的next指向新节点
        temp.next=heroNode;
    }

进行测试:

public class SingleLinked {
    public static void main(String[] args) {
        // 进行测试
        HeroNode hero1 = new HeroNode(1, "秦禹", "秦老黑");
        HeroNode hero2 = new HeroNode(2, "可可", "心脏");
        HeroNode hero3 = new HeroNode(3, "张子良", "人族智囊");
        SingleLinkedList singleLinkedList=new SingleLinkedList();
        // 添加节点
        singleLinkedList.add(hero1);
        singleLinkedList.add(hero2);
        singleLinkedList.add(hero3);
        // 显示链表数据
        singleLinkedList.list();
    }
}

// 定义一个LinkedList来管理HeroNode
class SingleLinkedList{
    // 先初始化头节点,head节点不能动,也不能放其他具体信息
    HeroNode head=new HeroNode(0,"","");

    // 添加节点到单链表
    // 思路:不考虑编号顺序问题的话首先遍历到链表的最后,让最后节点的next指向新的节点
    public void add(HeroNode heroNode){
        // 创建一个辅助变量来遍历链表(因为head头节点不能改变)
        HeroNode temp=head;
        while(true){
            // 找到链表的最后
            if(temp.next==null){
                break;
            }
            // 如果链表没有到最后,那么temp需要后移
            temp=temp.next;
        }
        // 当循环退出之后,证明链表到最后了
        // 将这个最后节点的next指向新节点
        temp.next=heroNode;
    }

    //显示链表
    public void list(){
        // 首先判断链表是否为空
        if(head.next==null){
            System.out.println("链表为空~~~");
            return;
        }
        // 创建一个辅助变量来遍历链表(因为head头节点不能改变)
        HeroNode temp=head.next;
        while (true){
            if(temp==null){
                System.out.println("链表遍历完了~~~");
                break;
            }
            // 输出节点信息
            System.out.println(temp.toString());
            // 后移
            temp=temp.next;
        }
    }
}

// 定义HeroNode,每一个HeroNode对象就是一个节点
class HeroNode{
    public int no;
    public String name;
    public String nickName;
    public HeroNode next;  // 指向下一个节点

    //构造器
    public HeroNode(int no, String name, String nickName) {
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }


    //为了显示方便,重写toString

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName +
                '}';
    }
}

结果:
在这里插入图片描述

2.2.2、根据no插入到指定位置

在这里插入图片描述
实现代码:

    //按照no顺序添加节点
    public void addOrder(HeroNode heroNode){
        HeroNode temp=head;
        // 作为是否已经存在no相等的节点
        boolean flag=false;
        // 循环链表
        while(true){
            if(temp.next==null){
                break;
            }
            //查找新节点在哪
            if(temp.next.no>heroNode.no){ // 位置找到了在temp后面添加新节点
                break;
            }
            if(temp.next.no==heroNode.no){ //编号已经存在,不能添加
                flag=true;
                break;
            }
            // temp后移
            temp=temp.next;
        }
        if(flag){
            System.out.println("该编号已经存在"+ heroNode.no+",不能添加该节点");
        }else{
            // 先把temp指向的下一个节点让新节点指向
            heroNode.next=temp.next;
            // 后temp指向的下一个节点设置为新节点
            temp.next=heroNode;
        }
    }

执行结果:
在这里插入图片描述

2.3、单链表节点的修改

代码方法实现:

 //修改节点的信息,通过no来修改即no是唯一的
    public void update(HeroNode heroNode){
        // 首先判断链表是否为空
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        // 找到需要修改的节点
        HeroNode temp=head.next;
        boolean flag=false;
        while(true){
            if(temp==null){
                break;
            }
            if(heroNode.no==temp.no){
                flag=true;
                break;
            }
            temp=temp.next;
        }
        if(flag){
            temp.name=heroNode.name;
            temp.nickName=heroNode.nickName;
        }
    }

运行结果:
在这里插入图片描述

2.4、单链表节点的删除

思路:找到删除节点的前一个节点,让要删除的节点的前一个节点指向要删除节点的后一个节点
代码实现:

    //删除节点
    public void delete(int no){
        if(head.next==null){
            return;
        }
        HeroNode temp=head;
        boolean flag=false;
        while(true){
            if(temp.next==null){
                break;
            }
            // 找到要删除节点的前一个节点
            if(temp.next.no==no){
                flag=true;
                break;
            }
            temp=temp.next;
        }
        if(flag){
            // 让要删除的节点的前一个节点指向要删除节点的后一个节点
           temp.next=temp.next.next;
        }else{
            System.out.println("未找到相对应的no节点");
        }
    }

运行结果:
在这里插入图片描述

3、双向链表

双向链表是在单链表的每个节点中,再设置一个指向其前驱节点的指针域。
在这里插入图片描述
创建节点类:

// 节点
class HeroNode2{
    public int no;
    public String name;
    public String nickName;
    // 指向后驱节点的指针
    public HeroNode2 next;
    // 指向前驱节点的指针
    public HeroNode2 pre;

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

    @Override
    public String toString() {
        return "HeroNode2{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

创建管理双向节点的类:

//创建双链表
class DoubleLinkedList{
    // 初始化一个头结点
    private  HeroNode2 head=new HeroNode2(0,"","");
    // 添加操作
    // 修改操作
    // 删除操作
    // 显示操作
}

3.1、双向链表的添加操作

3.1.1、一般默认添加到链表的尾部

思路:首先设置一个辅助节点来进行链表循环,如果链表为空,直接退出循环,否则一直循环到链表的最后退出循环,之后把temp的next指向新添加的节点,新添加的节点的pre指向temp。

代码实现:

    // 在尾部添加元素
    public void add(HeroNode2 heroNode2){
        HeroNode2 temp=head;
       while(true){
           // 判断链表是否到最后
           if(temp.next==null){
               break;
           }
           // 没到最后,继续后移
           temp=temp.next;
       }
       // 形成一个双向链表
       temp.next=heroNode2;
       heroNode2.pre=temp;
    }

运行结果:
在这里插入图片描述

3.1.2、根据no插入到指定位置

在这里插入图片描述

代码实现:

    // 根据no顺序添加节点
    public void orderAdd(HeroNode2 heroNode2){
        HeroNode2 temp=head;
        while(true){
            if(temp.next==null){
                temp.next=heroNode2;
                heroNode2.pre=temp;
                break;
            }
            if(temp.next.no==heroNode2.no){
                System.out.printf("已经存在编号%d,不能添加",heroNode2.no);
                break;
            }
            // 找出第一个大于等于新增节点的no的节点temp的后驱节点,即在temp后面添加节点
            if(temp.next.no>heroNode2.no){
                // 1、先把temp赋值给新增节点的前驱
                heroNode2.pre=temp;
                // 2、把temp的后驱赋值给新增节点的后驱
                heroNode2.next=temp.next;
                // 3、把新增节点赋值给temp的后驱节点的前驱
                temp.next.pre=heroNode2;
                // 4、把新增节点赋值给temp的后驱
                temp.next=heroNode2;
                break;
            }
            temp=temp.next;
        }
    }

运行结果:
在这里插入图片描述

3.2、双向链表节点的修改

思路跟单链表差不多,自己理解理解。

代码实现:

    // 修改heroNode2节点信息
    public void update(HeroNode2 heroNode2){
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        HeroNode2 temp=head.next;
        while(true){
            if(temp==null){
                // 没有找到要修改的节点
                System.out.printf("没有找到编号为%d的节点,不能修改",heroNode2.no);
                break;
            }
            // 找到要修改的节点
            if(temp.no==heroNode2.no){
                temp.name=heroNode2.name;
                temp.nickName=heroNode2.nickName;
                break;
            }
            temp=temp.next;
        }
    }

运行结果:
在这里插入图片描述

3.3、双向链表的删除

思路:相对于单链表需要找到删除节点的前一个节点删除,双向链表只要找到删除节点就可以进行删除。找到删除节点为temp,将temp的前一个节点的next指向temp的后一个节点,如果temp是最后一个节点,就不需要进行将temp的后一个节点的pre指向给temp的前一个节点。
在这里插入图片描述

代码实现:


    // 根据no删除元素
    public void delete(int no){
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        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.printf("没有编号为%d的节点",no);
        }
    }

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值