Java单链表头插法和尾插法以及增删改查方法

一、单链表的头插法:

头插法原理图:
在这里插入图片描述

代码演示头插法:

package com.fan.linkedlist;

public class SingleLInkedListDemo3 {
    public static void main(String[] args) {
        SingleLInkedList3 singlelist3 = new SingleLInkedList3();
        System.out.println(singlelist3.headInsert(1));
        System.out.println(singlelist3.headInsert(2));
        System.out.println(singlelist3.headInsert(3));
        System.out.println("显示链表:");
        singlelist3.show();
        //测试尾插法1:向单链表增加数据
        System.out.println("\n测试尾插法1:");
        SingleLInkedList3 singlelist4 = new SingleLInkedList3();
        System.out.println(singlelist4.tailInsert1(4));
        System.out.println(singlelist4.tailInsert1(5));
        System.out.println(singlelist4.tailInsert1(6));
        singlelist4.show();
        //测试尾插法2:向单链表增加数据
        System.out.println("\n测试尾插法2:");
        SingleLInkedList3 singlelist5 = new SingleLInkedList3();
        System.out.println(singlelist5.tailInsert2(7));
        System.out.println(singlelist5.tailInsert2(8));
        System.out.println(singlelist5.tailInsert2(9));
        singlelist5.show();
    }
}
//2.创建链表类,并初始化头结点/尾结点,设置其的指针域为空.
class SingleLInkedList3{//链表带泛型
    //一个单链表是由多个节点构成的。这里我们设置头节点和尾部节点
    //声明头节点,并设置其指针域为空即headNode --->  null
     Node3 headNode = new Node3();
     //声明尾部节点,用来演示尾插法而设置的变量
     Node3 tailNode = new Node3();


    public Node3 getHeadNode() {
        return headNode;
    }

    public void setHeadNode(Node3 headNode) {
        this.headNode = headNode;
    }

    public Node3 getTailNode() {
        return tailNode;
    }

    public void setTailNode(Node3 tailNode) {
        this.tailNode = tailNode;
    }

    //使用空的构造方法创建单链表,当一个空链表创建好了之后就要有头或者尾节点
    //此构造方法很重要
    public SingleLInkedList3() {
        //初始化头节点的next指针域为空,并设置其指针域为空即headNode --->  null
        headNode.setNext(null);//一个空链表的头节点是指向null的
        //初始化尾节点的next指针域也为空,采用头插法就没必要设置tailNode
        tailNode.setNext(null);
        //headNode永远存储第一个节点的地址,tailNode永远存储最后一个节点的地址
        headNode = tailNode ;//也为了show方法的遍历
    }
    //头插法,即头节点不动,其后的指针每次进行拆散,并将新节点插入到头节点紧邻后边
    /*头插法一般也用于单链表模拟栈,我们用头插法能实现先入后出*/
    /*向单链表增加数据*/
    public boolean headInsert(Object obj){
        Node3 newNode = new Node3(obj);//将数据封装成一个新节点
        newNode.setNext(headNode.getNext());//设置新节点的后驱节点为 头节点的下一个节点
        headNode.setNext(newNode);//设置新节点的前驱节点为 头节点,即头节点不动
        //如果头节点紧邻后是新节点,证明头插入成功
        return headNode.getNext() == newNode;
    }
    //尾插法  ====第一种实现:使用从头节点遍历找尾节点并插入===
    /*向单链表增加数据*/
    public boolean tailInsert1(Object obj){
        Node3 newNode = new Node3(obj);
        Node3 curr = headNode;//临时节点从头节点开始遍历,
        while(true){
            if(curr.getNext() == null){
                break;//到了链表结尾
            }
            curr = curr.getNext();//临时节点后移
        }//end-while
        //出循环后就证明到了链表结尾,直接添加新节点,curr此时指向了最后一个有效节点
       curr.setNext(newNode);
        newNode.setNext(null);
       return newNode.getNext() == null;
    }
    //尾插法====第二种实现,使用尾节点移动来实现
    public boolean tailInsert2(Object obj){

        Node3 newNode = new Node3(obj);
        //1.将尾指针的指针域指向 刚刚插入的新节点newNode
        tailNode.setNext(newNode);
        //刷新尾节点,现在newNode变成了最后一个元素了,也就是新的尾节点
        //2.tailNode永远存储最后一个节点
        tailNode = newNode;//尾节点指向新添加的节点,即尾节点后移,类似于i++
        //将最终的尾节点指针域赋值为空
        tailNode.setNext(null);

        return tailNode.getNext()==null;
    }
    //链表元素的显示
    public void show(){
        //定义一个临时变量curr来遍历链表,不能使用头节点,因为头节点不能动
        Node3 curr = headNode.getNext();//curr指向头节点下一个,或者说将头节点地址赋值给curr临时节点
        while(true){
            if(curr == null){//下一个有效元素位空,则到了链表的结尾
                break;
            }
            System.out.print(curr+"\t-->");//打印当前有效节点
            curr = curr.getNext();//指针后移,相当于i++
        }
    }

    //链表元素的显示

}
//1.创建节点类,也可以将此类设置成内部类
class Node3{
    //数据域,可以是多个数据如name,id,age等
    private Object object;//
    //指针域,单链表有数据域和next指针这两项
    private Node3 next;//指向下驱节点

    public Node3(Object object) {
        this.object = object;
    }

    public Node3() {
    }

    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    public Node3 getNext() {
        return next;
    }

    public void setNext(Node3 next) {
        this.next = next;
    }

    @Override
    public String toString() {
        return "Node3{" +
                "object=" + object +
                '}';
    }
}

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

二、单链表的尾插法:

尾插法: 尾插法相对于头插法有些许不同 因为要返回头 头不能动 所以需要一个tailNode来记录最后一个值 tailNode右移

尾插法原理图:
开始tailNode–>null是指向null的。

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

   //尾插法  ====第一种实现:使用从头节点遍历找尾节点并插入===
    /*向单链表增加数据*/
    public boolean tailInsert1(Object obj){
        Node3 newNode = new Node3(obj);
        Node3 curr = headNode;//临时节点从头节点开始遍历,
        while(true){
            if(curr.getNext() == null){
                break;//到了链表结尾
            }
            curr = curr.getNext();//临时节点后移
        }//end-while
        //出循环后就证明到了链表结尾,直接添加新节点,curr此时指向了最后一个有效节点
       curr.setNext(newNode);
        newNode.setNext(null);
       return newNode.getNext() == null;
    }
    //尾插法====第二种实现,使用尾节点移动来实现
    public boolean tailInsert2(Object obj){

        Node3 newNode = new Node3(obj);
        //1.将尾指针的指针域指向 刚刚插入的新节点newNode
        tailNode.setNext(newNode);
        //刷新尾节点,现在newNode变成了最后一个元素了,也就是新的尾节点
        //2.tailNode永远存储最后一个节点
        tailNode = newNode;//尾节点指向新添加的节点,即尾节点后移,类似于i++
        //将最终的尾节点指针域赋值为空
        tailNode.setNext(null);

        return tailNode.getNext()==null;
    }

测试代码:

public class SingleLInkedListDemo3 {
    public static void main(String[] args) {
        SingleLInkedList3 singlelist3 = new SingleLInkedList3();
        System.out.println(singlelist3.headInsert(1));
        System.out.println(singlelist3.headInsert(2));
        System.out.println(singlelist3.headInsert(3));
        System.out.println("显示链表:");
        singlelist3.show();
        //测试尾插法1:向单链表增加数据
        System.out.println("\n测试尾插法1:");
        SingleLInkedList3 singlelist4 = new SingleLInkedList3();
        System.out.println(singlelist4.tailInsert1(4));
        System.out.println(singlelist4.tailInsert1(5));
        System.out.println(singlelist4.tailInsert1(6));
        singlelist4.show();
        //测试尾插法2:向单链表增加数据
        System.out.println("\n测试尾插法2:");
        SingleLInkedList3 singlelist5 = new SingleLInkedList3();
        System.out.println(singlelist5.tailInsert2(7));
        System.out.println(singlelist5.tailInsert2(8));
        System.out.println(singlelist5.tailInsert2(9));
        singlelist5.show();
    }
}

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

总结头插法和尾插法:

在这里插入图片描述

三、单链表顺序添加:

需求,我们有一批购房的摇号者,我们让摇号者的编号小的越先获取购房资格,请乱序添加5个摇号者的编号,添加到链表中后是从小到大排好序的。

分析:单链表的按照次序添加,区别于头插法和尾插法:

  //按照编号顺序添加
    public void addByOrder(ConsumerNode newNode){
        ConsumerNode curr = headNode;//头节点不能动,所以设置临时节点
        boolean flag = false;//默认编号不存在
        while(true){
            //遍历到链表结尾的时候
            if(curr.getNext() == null){
                break;
            }
            //判断编号的插入位置
            if(newNode.getNo() < curr.getNext().getNo()){
                break;//头插
            }else if(newNode.getNo() == curr.getNext().getNo()){
                flag = true;//编号相同的特殊情况,单独处理
                break;
            }
            curr = curr.getNext();
        }
        if(!flag){//当编号不存在的时候我们才添加
            newNode.setNext(curr.getNext());
            curr.setNext(newNode);
        }else{
            System.out.println("编号相同,不能插入---");
        }
    }

四、按照值查找节点:

//根据数据域编号查找该节点的所有信息
    public void findByNo(Integer key){
        ConsumerNode curr =headNode;
        boolean flag =false;
        while(true){
            if(curr.getNext() == null){//到链表结尾,退出循环
                break;
            }
            if(curr.getNext().getNo() == key){
                flag = true;//找到查找的节点了,退出
                break;
            }
            curr = curr.getNext();//临时节点后移进行遍历
        }
        if(flag){
            System.out.println(curr.getNext());
        }else{
            System.out.println("没有找到要查找的节点信息----");
        }
    }

五、删除节点:

//删除某一个节点,按照编号
    public void delByNo(Integer no){
        ConsumerNode curr = headNode;
        boolean flag = false;//设置是否找到要删除的节点
        while(true){
            //需要判断是否到链表的结尾处
            if(curr.getNext() == null){
                break;
            }
            if(curr.getNext().getNo() == no){
                flag = true;

                break;
            }
            curr = curr.getNext();//没有找到的话,后移继续找
        }
        if(flag){
            curr.setNext(curr.getNext().getNext());
        }else{
            System.out.println("没有此节点要删除---");
        }
    }

六、修改节点:

    //修改某一个节点,根据节点编号,修改
    public void update(ConsumerNode newNode){
        //思路,就是替换编号 相同的节点,即插入新节点
        ConsumerNode curr = headNode;
        boolean flag = false;//设置是否找到要修改的节点
        while(true){
            //需要先判断是否到链表的结尾处
            if(curr.getNext() == null){
                break;//到链表的结尾
            }

            if(curr.getNext().getNo()== newNode.getNo()  ){
                flag = true;
                break;
            }
            curr = curr.getNext();
        }
        if(flag){
            newNode.setNext(curr.getNext().getNext());
            curr.setNext(newNode);
        }else{
            System.out.println("输入的编号没找到对应节点---");
        }

    }

七、求单链表的表长:

//获取链表的长度
    public int getSize(){
        int count = 0;
        ConsumerNode curr = headNode;

        while(true){
            if(curr.getNext() == null){
                break;
            }
            count++;
            curr = curr.getNext();
        }
        return count;
    }

最后的总代码:

package com.fan.linkedlist;

public class SingleLinkedlistDemo4 {
    public static void main(String[] args) {
        //创建一个空链表
        ConsumerLinkedlist consumerLinkedlist = new ConsumerLinkedlist();

        /*consumerLinkedlist.addFirst(new ConsumerNode(1,"小一"));
        consumerLinkedlist.addFirst(new ConsumerNode(2,"小二"));
        consumerLinkedlist.addFirst(new ConsumerNode(3,"张三"));*/

        /*consumerLinkedlist.addLast(new ConsumerNode(1,"小一"));
        consumerLinkedlist.addLast(new ConsumerNode(2,"小二"));
        consumerLinkedlist.addLast(new ConsumerNode(3,"张三"));
        consumerLinkedlist.addLast(new ConsumerNode(4,"李四"));*/

        consumerLinkedlist.addByOrder(new ConsumerNode(4,"小一"));
        consumerLinkedlist.addByOrder(new ConsumerNode(2,"小二"));
        consumerLinkedlist.addByOrder(new ConsumerNode(1,"张三"));
        consumerLinkedlist.addByOrder(new ConsumerNode(3,"李四"));
        consumerLinkedlist.addByOrder(new ConsumerNode(5,"李四"));
        System.out.println("显示添加后的顺序链表:");
        consumerLinkedlist.show();
        System.out.println("\n链表长度:"+ consumerLinkedlist.getSize());
        //consumerLinkedlist.delByNo(3);
        consumerLinkedlist.delByNo(11);
        System.out.println("删除后显示:");
        consumerLinkedlist.show();
        System.out.println("\n修改节点:===");
        consumerLinkedlist.update(new ConsumerNode(3,"我是大山"));//测试的此编号不存在
        consumerLinkedlist.show();
        System.out.println("\n查找节点========");
        //consumerLinkedlist.findByNo(8);
        consumerLinkedlist.findByNo(3);
    }
}
//链表类
class ConsumerLinkedlist{
    //一个单链表要有头节点或者尾节点
    private ConsumerNode headNode = new ConsumerNode();
    private ConsumerNode tailNode = new ConsumerNode();
    //构造方法
    public ConsumerLinkedlist(){
        headNode.setNext(null);
        tailNode.setNext(null);
        //头节点和尾节点开始时是重合的,都是指向null的
        //headNode永远存储第一个节点的地址,tailNode永远存储最后一个节点的地址
        headNode = tailNode ;//头尾重合,两个指针重合
    }
    //按照标号添加到链表中
    public void addFirst(ConsumerNode newNode){
        //头插法,不需要循环,头节点不动
        //注意,必须先处理新节点的后驱节点
        newNode.setNext(headNode.getNext());
        headNode.setNext(newNode);
    }
    //尾插法,还是头结点不动,尾节点移动
    public void addLast(ConsumerNode newNode){
        //注意,tailNode此时相当于一个临时的节点指针,相当一个指向节点的一个箭头
      /*  if(tailNode.getNext() == null){
            //直接添加新节点到尾节点的后边
            tailNode.setNext(newNode);
            //让尾节点成为最后一个节点
            tailNode = tailNode.getNext();
            //让尾节点的指针为null
            tailNode.setNext(null);
        }*/
        //直接添加新节点到尾节点的后边
        tailNode.setNext(newNode);
        tailNode = tailNode.getNext();//让尾节点成为最后一个节点
        tailNode.setNext(null);//让尾节点指向null
    }

    //按照编号顺序添加
    public void addByOrder(ConsumerNode newNode){
        ConsumerNode curr = headNode;//头节点不能动,所以设置临时节点
        boolean flag = false;//默认编号不存在
        while(true){
            //遍历到链表结尾的时候
            if(curr.getNext() == null){
                break;
            }
            //判断编号的插入位置
            if(newNode.getNo() < curr.getNext().getNo()){
                break;//头插
            }else if(newNode.getNo() == curr.getNext().getNo()){
                flag = true;//编号相同的特殊情况,单独处理
                break;
            }
            curr = curr.getNext();
        }
        if(!flag){//当编号不存在的时候我们才添加
            newNode.setNext(curr.getNext());
            curr.setNext(newNode);
        }else{
            System.out.println("编号相同,不能插入---");
        }
    }
    //删除某一个节点,按照编号
    public void delByNo(Integer no){
        ConsumerNode curr = headNode;
        boolean flag = false;//设置是否找到要删除的节点
        while(true){
            //需要判断是否到链表的结尾处
            if(curr.getNext() == null){
                break;
            }
            if(curr.getNext().getNo() == no){
                flag = true;

                break;
            }
            curr = curr.getNext();//没有找到的话,后移继续找
        }
        if(flag){
            curr.setNext(curr.getNext().getNext());
        }else{
            System.out.println("没有此节点要删除---");
        }
    }
    //修改某一个节点,根据节点编号,修改
    public void update(ConsumerNode newNode){
        //思路,就是替换编号 相同的节点,即插入新节点
        ConsumerNode curr = headNode;
        boolean flag = false;//设置是否找到要修改的节点
        while(true){
            //需要先判断是否到链表的结尾处
            if(curr.getNext() == null){
                break;//到链表的结尾
            }

            if(curr.getNext().getNo()== newNode.getNo()  ){
                flag = true;
                break;
            }
            curr = curr.getNext();
        }
        if(flag){
            newNode.setNext(curr.getNext().getNext());
            curr.setNext(newNode);
        }else{
            System.out.println("输入的编号没找到对应节点---");
        }

    }
    //根据数据域编号查找该节点的所有信息
    public void findByNo(Integer key){
        ConsumerNode curr =headNode;
        boolean flag =false;
        while(true){
            if(curr.getNext() == null){//到链表结尾,退出循环
                break;
            }
            if(curr.getNext().getNo() == key){
                flag = true;//找到查找的节点了,退出
                break;
            }
            curr = curr.getNext();//临时节点后移进行遍历
        }
        if(flag){
            System.out.println(curr.getNext());
        }else{
            System.out.println("没有找到要查找的节点信息----");
        }
    }
    //获取链表的长度
    public int getSize(){
        int count = 0;
        ConsumerNode curr = headNode;

        while(true){
            if(curr.getNext() == null){
                break;
            }
            count++;
            curr = curr.getNext();
        }
        return count;
    }
    //单链表的遍历
    public void show(){
        //判断链表是否为空,只有一个头节点的话直接结束
        if(headNode.getNext() == null){
            System.out.println("链表为空");
            return;
        }
        ConsumerNode curr = headNode.getNext();//从有效节点开始遍历
        while(true){
            if(curr == null){//到链表末尾时退出
               break;
            }
            System.out.print(curr+"-->");
            curr = curr.getNext();
        }
    }
}
//消费者节点,消费者将来需要被叫号排到链表中,可以用lambook
class ConsumerNode {
    //数据域
    private Integer no;
    private String name;
    //指针域,单链表只有一个next指针域,用于指向下一个节点
    private ConsumerNode next;
    //生成构造和set,get等


    public ConsumerNode(Integer no, String name) {
        this.no = no;
        this.name = name;
    }

    public ConsumerNode() {
    }

    public Integer getNo() {
        return no;
    }

    public void setNo(Integer no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public ConsumerNode getNext() {
        return next;
    }

    public void setNext(ConsumerNode next) {
        this.next = next;
    }

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

单链表常见面试题:

在这里插入图片描述

单链表的反转:

在这里插入图片描述

    //单链表的反转,类似于头插法,返回一个新链表的头节点,不影响原链表结构
    public void resver(){
        ConsumerNode curr = headNode.getNext();//用于遍历的指针节点
        ConsumerNode temp = null;//后面用于存储curr的后驱节点的
        ConsumerNode newlinkedHead = new ConsumerNode();
        //循环拆解原链表,并拼接到新链表的头节点上
        if(curr.getNext() == null || curr.getNext().getNext() == null){
            return ;//只有一个节点或者空链表
        }
        while(true){
            if(curr == null){
                break;//到链表结尾
            }
            if(curr != null){
                //先将curr的下一个节点保存起来
                temp = curr.getNext();
                //设置curr的下一个节点不再是原先链表的下一个节点了,而是新链表头节点的下一个节点
                curr.setNext(newlinkedHead.getNext());
                //新节点的下一个节点就是curr
                newlinkedHead.setNext(curr);
                //让curr后移到原先链表的后一个节点
                curr = temp;//这个相当于原先的 curr = curr.getNext();
            }
        }
        //将新链表的所有节点穿到旧链表的头节点上,这样输出旧链表就已经反转了
        headNode.setNext(newlinkedHead.getNext());
    }

测试:

public class SingleLinkedlistDemo4 {
    public static void main(String[] args) {
        //创建一个空链表
        ConsumerLinkedlist consumerLinkedlist = new ConsumerLinkedlist();

        //添加顺序1->2->3->4->5
        consumerLinkedlist.addLast(new ConsumerNode(1,"小一"));
        consumerLinkedlist.addLast(new ConsumerNode(2,"小二"));
        consumerLinkedlist.addLast(new ConsumerNode(3,"张三"));
        consumerLinkedlist.addLast(new ConsumerNode(4,"李四"));
        consumerLinkedlist.addLast(new ConsumerNode(5,"李四"));
     
        System.out.println("\n反转链表:--");
        consumerLinkedlist.resver();
        consumerLinkedlist.show();
    }
}

在这里插入图片描述

对于从尾部到头部打印单链表,可以先反转,后调用show方法即可;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值