链表-单链表

1、链表(Linked List)介绍
链表是有序的列表,但是它在内存中是存储如下
在这里插入图片描述
小结上图:

  1. 链表是以节点的方式来存储, 是链式存储
  2. 每个节点包含 data 域, next 域:指向下一个节点.
  3. 如图:发现 链表的各个节点不一定是连续存储.
  4. 链表分 带头节点的链表和 没有头节点的链表,根据实际的需求来确定

-单链表(带头结点) 逻辑结构示意图如下
在这里插入图片描述
2、单链表的应用实例

使用带 head 头的单向链表实现

  • 第一种方法在添加英雄时,直接添加到链表的尾部 思路分析示意图:

在这里插入图片描述

  • 第二种方式在添加英雄时, 根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示) 思路的分析示意图:

在这里插入图片描述

  • 修改节点功能

思路(1) 先找到该节点,通过遍历

  • 删除节点
    思路分析的示意图:
    在这里插入图片描述

  • 求单链表中有效节点的个数

  • 查找单链表中的倒数第 k 个结点

  • 单链表的反转

思路分析图解
在这里插入图片描述
在这里插入图片描述

  • 从尾到头打印单链表
    在这里插入图片描述

  • 实现代码:

SingleLinkedListNode.java

package com.yg.datastructures.linkedlist;

/**
 * @Description: 单链表的模型
 * @Author yg
 * @Date 2021-03-24 9:42
 */
public class SingleLinkedListNode {
    private int id;
    private String name;
    private int age;
    private String phone;
    private SingleLinkedListNode next;

    public SingleLinkedListNode(int id, String name, int age, String phone) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.phone = phone;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public SingleLinkedListNode getNext() {
        return next;
    }

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

    @Override
    public String toString() {
        return "SingleLinkedListNode{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                '}';
    }
}

SingleLinkedList .java

package com.yg.datastructures.linkedlist;

import java.util.Stack;

/**
 * @Description: 单链表操作
 * @Author yg
 * @Date 2021-03-24 9:42
 */
public class SingleLinkedList {

    SingleLinkedListNode head = new SingleLinkedListNode(0, "", 0, "");

    /**
     * 获取头节点,空的头节点
     *
     * @return
     */
    public SingleLinkedListNode getHead() {
        return head;
    }

    /**
     * 获取链表 的长度
     *
     * @return
     */
    public int getLength() {
        if (getHead().getNext() == null) {
            return 0;
        }
        int sum = 0;
        SingleLinkedListNode temp = getHead().getNext();
        while (true) {
            if (temp != null) {
                sum++;
            } else {
                break;
            }
            temp = temp.getNext();
        }
        return sum;
    }

    /**
     * 添加节点
     *
     * @param singleLinkedListNode
     */
    public void addNode(SingleLinkedListNode singleLinkedListNode) {
        SingleLinkedListNode temp = getHead();
        while (true) {
            if (temp.getId() == singleLinkedListNode.getId()) {
                throw new RuntimeException("添加的节点id已经存在:" + singleLinkedListNode);
            }
            if (temp.getNext() == null) {
                //找到了最后的节点,将添加的节点添加到尾部
                break;
            }
            temp = temp.getNext();
        }

        temp.setNext(singleLinkedListNode);
    }

    /**
     * 按照id顺序添加节点
     *
     * @param singleLinkedListNode
     */
    public void addOrderById(SingleLinkedListNode singleLinkedListNode) {
        SingleLinkedListNode temp = getHead();
        boolean flag = false;
        while (true) {
            if (temp.getNext() == null) {
                break;
            }
            //找到要添加的前一个id   singleLinkedListNode.getId()<= 当前对比的id
            //需要找到添加的位置的前一个节点,
            if (singleLinkedListNode.getId() < temp.getNext().getId()) {
                break;
            } else if (singleLinkedListNode.getId() == temp.getNext().getId()) {
                flag = true;
                break;
            }
            temp = temp.getNext();
        }
        //temp 就是那个节点
        if (flag) {
            System.out.printf("准备插入的英雄的编号 %d 已经存在了, 不能加入\n", singleLinkedListNode.getId());
        } else {
            //这里添加注意顺序,如果颠倒则会链表无限循环了
            singleLinkedListNode.setNext(temp.getNext());
            temp.setNext(singleLinkedListNode);
        }
    }


    /**
     * 更新节点
     *
     * @param singleLinkedListNode
     * @return
     */
    public SingleLinkedListNode updateNode(SingleLinkedListNode singleLinkedListNode) {
        if (head.getNext() == null) {
            throw new RuntimeException("链表为空,无有效数据更新!");
        }
        //从第一关元素遍历,不能给更新头节点
        SingleLinkedListNode temp = head.getNext();
        //标志位判断是否找到更新节点
        boolean flag = false;
        while (true) {
            if (temp.getId() == singleLinkedListNode.getId()) {
                //说明找到了,
                flag = true;
                break;
            }
            if (temp.getNext() == null) {
                //说明已经到最后了,也没有找到。
                System.out.println("没有找到要更新的节点:" + singleLinkedListNode);
                break;
            }
            temp = temp.getNext();
        }
        if (flag) {
            //进来表示找到了
            temp.setAge(singleLinkedListNode.getAge());
            temp.setName(singleLinkedListNode.getName());
            temp.setPhone(singleLinkedListNode.getPhone());
            return temp;
        }
        return null;
    }

    /**
     * 删除节点
     *
     * @param delNo
     */
    public void delNode(int delNo) {
        if (isNull()) {
            //删除从第一个节点开始遍历
            SingleLinkedListNode temp = getHead().getNext();
            boolean flag = false;
            while (true) {
                if (temp.getNext() == null) {
                    //表示已经找到了最后一个节点,还没有找到删除的元素
                    break;
                }
                if (temp.getNext().getId() == delNo) {
                    //找到要删除的前一个节点
                    flag = true;
                    break;
                }
                temp = temp.getNext();
            }
            if (flag) {
                //进来表示找到了,此时的temp 是指向要删除的前一个节点的
                if (temp.getNext().getNext() != null) {
                    //表示删除的后一个节点不为空,即删除的不是最后一个节点
                    temp.setNext(temp.getNext().getNext());
                } else {
                    //如果是最后一个节点
                    temp.setNext(null);
                }
            }
        }
    }

    /**
     * 打印链表
     */
    public void showList() {
        if (isNull()) {
            SingleLinkedListNode temp = getHead().getNext();
            System.out.println("----------准备打印列表------------");
            while (true) {
                if (temp == null) {
                    //表示已经到头了
                    break;
                }
                System.out.println(temp);
                temp = temp.getNext();
            }
            System.out.println("----------打印列表结束------------");
        }
    }

    /**
     * 查找单链表中的倒数第 k 个结点
     * //思路
     * //1. 编写一个方法,接收 head 节点,同时接收一个 index
     * //2. index 表示是倒数第 index 个节点
     * //3. 先把链表从头到尾遍历,得到链表的总的长度 getLength
     * //4. 得到 size 后,我们从链表的第一个开始遍历 (size-index)个,就可以得到
     * //5. 如果找到了,则返回该节点,否则返回 nulll
     *
     * @param singleLinkedListNode 链表
     * @param index                倒数个数
     * @return
     */
    public SingleLinkedListNode findLastIndexNode(SingleLinkedListNode singleLinkedListNode, int index) {
        int length = getLength();
        SingleLinkedListNode temp = singleLinkedListNode;

        if (isNull()) {
            //有效性判断
            if (index <= 0 || index > length) {
                return null;
            }
            for (int i = 0; i < length - index + 1; i++) {
                temp = temp.getNext();
            }
            return temp;
        }
        return null;
    }


    /**
     * 判断是否为空
     *
     * @return
     */
    public boolean isNull() {
        if (head.getNext() == null) {
            System.out.println("链表为空,无法操作!");
            return false;
        }
        return true;
    }


    /**
     * 将单链表反转
     *
     * @param head 链表的头
     * @return
     */
    public SingleLinkedListNode reverseLinkedList(SingleLinkedListNode head) {
        if (head.getNext() == null || head.getNext().getNext() == null) {
            //链表为空或者只有一个元素,无需反转
            return head;
        }
        //定义一个辅助的指针(变量),帮助我们遍历原来的链表
        //遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表 newNode 的最前端
        //头插法
        SingleLinkedListNode newNode = new SingleLinkedListNode(0, "", 0, "");
        SingleLinkedListNode temp = head.getNext();
        SingleLinkedListNode next = null;
        while (true) {
            if (temp != null) {
                //临时保存下一个节点
                next = temp.getNext();
                //设置当前节点的下一个节点为新链表的第一个节点
                temp.setNext(newNode.getNext());
                //设置新链表的第一个节点为当前插入的节点。
                newNode.setNext(temp);
                //temp 右移动
                temp = next;
            }else{
                break;
            }

        }

        //将 head.next 指向 newNode.next , 实现单链表的反转
        head.setNext(newNode.getNext());
        return head;
    }

    /**
     * 单链表的逆序打印代码
     * 利用栈 实现
     * @param head
     */
    public void reversePrint(SingleLinkedListNode head){
        Stack<SingleLinkedListNode> stack = new Stack<SingleLinkedListNode>();
        SingleLinkedListNode temp = head.getNext();

        while(true){
            if(temp == null){
                break;
            }
            stack.push(temp);
            temp = temp.getNext();
        }
        while(stack.size()>0){
            System.out.println("出栈顺序:"+stack.pop());
        }
    }
}

SingleLinkedListDemo.java

package com.yg.datastructures.linkedlist;

/**
 * @Description: 测试类
 * @Author yg
 * @Date 2021-03-24 9:40
 */
public class SingleLinkedListDemo {
    public static void main(String[] args) {
        SingleLinkedListNode node1 = new SingleLinkedListNode(1, "李白", 18, "18609287231");
        SingleLinkedListNode node2 = new SingleLinkedListNode(2, "杜甫", 20, "18709873321");
        SingleLinkedListNode node3 = new SingleLinkedListNode(3, "白居易", 22, "1818873321");
        SingleLinkedListNode node4 = new SingleLinkedListNode(4, "李清照", 25, "17638779333");
        SingleLinkedListNode node5 = new SingleLinkedListNode(5, "王安石", 33, "1887788772");

        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.addNode(node1);
        singleLinkedList.addNode(node2);
        singleLinkedList.addNode(node4);
        singleLinkedList.showList();

        singleLinkedList.delNode(2);
        System.out.println("-------------删除后链表--------------");
        singleLinkedList.showList();

        System.out.println("-------------添加5后链表--------------");
        singleLinkedList.addNode(node5);
        singleLinkedList.showList();

        System.out.println("-------------顺序添加2,3后链表--------------");
        singleLinkedList.addOrderById(node2);
        singleLinkedList.addOrderById(node3);
        singleLinkedList.showList();
        System.out.println("-------------修改后链表--------------");
        singleLinkedList.updateNode(new SingleLinkedListNode(1, "茅以升", 33, "1879983332"));
        singleLinkedList.showList();
        System.out.println("链表长度length = " + singleLinkedList.getLength());


        System.out.println("链表的倒数第3个节点=" + singleLinkedList.findLastIndexNode(singleLinkedList.getHead(), 3));

        SingleLinkedListNode reverseLinkedListNode = singleLinkedList.reverseLinkedList(singleLinkedList.getHead());
        System.out.println("---------------反转后的链表为--------------");
        singleLinkedList.showList();
        System.out.println("----------------逆序打印链表---------------");
        singleLinkedList.reversePrint(singleLinkedList.getHead());
    }
}

运行结果:

D:\jdk1.8.0_251\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:1360,suspend=y,server=n -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2020.3\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "D:\jdk1.8.0_251\jre\lib\charsets.jar;D:\jdk1.8.0_251\jre\lib\deploy.jar;D:\jdk1.8.0_251\jre\lib\ext\access-bridge-64.jar;D:\jdk1.8.0_251\jre\lib\ext\cldrdata.jar;D:\jdk1.8.0_251\jre\lib\ext\dnsns.jar;D:\jdk1.8.0_251\jre\lib\ext\jaccess.jar;D:\jdk1.8.0_251\jre\lib\ext\jfxrt.jar;D:\jdk1.8.0_251\jre\lib\ext\localedata.jar;D:\jdk1.8.0_251\jre\lib\ext\nashorn.jar;D:\jdk1.8.0_251\jre\lib\ext\sunec.jar;D:\jdk1.8.0_251\jre\lib\ext\sunjce_provider.jar;D:\jdk1.8.0_251\jre\lib\ext\sunmscapi.jar;D:\jdk1.8.0_251\jre\lib\ext\sunpkcs11.jar;D:\jdk1.8.0_251\jre\lib\ext\zipfs.jar;D:\jdk1.8.0_251\jre\lib\javaws.jar;D:\jdk1.8.0_251\jre\lib\jce.jar;D:\jdk1.8.0_251\jre\lib\jfr.jar;D:\jdk1.8.0_251\jre\lib\jfxswt.jar;D:\jdk1.8.0_251\jre\lib\jsse.jar;D:\jdk1.8.0_251\jre\lib\management-agent.jar;D:\jdk1.8.0_251\jre\lib\plugin.jar;D:\jdk1.8.0_251\jre\lib\resources.jar;D:\jdk1.8.0_251\jre\lib\rt.jar;D:\IdeaProjects\datastructures\target\classes;D:\RepMaven\org\springframework\boot\spring-boot-starter-web\2.4.4\spring-boot-starter-web-2.4.4.jar;D:\RepMaven\org\springframework\boot\spring-boot-starter\2.4.4\spring-boot-starter-2.4.4.jar;D:\RepMaven\org\springframework\boot\spring-boot\2.4.4\spring-boot-2.4.4.jar;D:\RepMaven\org\springframework\boot\spring-boot-autoconfigure\2.4.4\spring-boot-autoconfigure-2.4.4.jar;D:\RepMaven\org\springframework\boot\spring-boot-starter-logging\2.4.4\spring-boot-starter-logging-2.4.4.jar;D:\RepMaven\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\RepMaven\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\RepMaven\org\apache\logging\log4j\log4j-to-slf4j\2.13.3\log4j-to-slf4j-2.13.3.jar;D:\RepMaven\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;D:\RepMaven\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;D:\RepMaven\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\RepMaven\org\yaml\snakeyaml\1.27\snakeyaml-1.27.jar;D:\RepMaven\org\springframework\boot\spring-boot-starter-json\2.4.4\spring-boot-starter-json-2.4.4.jar;D:\RepMaven\com\fasterxml\jackson\core\jackson-databind\2.11.4\jackson-databind-2.11.4.jar;D:\RepMaven\com\fasterxml\jackson\core\jackson-annotations\2.11.4\jackson-annotations-2.11.4.jar;D:\RepMaven\com\fasterxml\jackson\core\jackson-core\2.11.4\jackson-core-2.11.4.jar;D:\RepMaven\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.11.4\jackson-datatype-jdk8-2.11.4.jar;D:\RepMaven\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.11.4\jackson-datatype-jsr310-2.11.4.jar;D:\RepMaven\com\fasterxml\jackson\module\jackson-module-parameter-names\2.11.4\jackson-module-parameter-names-2.11.4.jar;D:\RepMaven\org\springframework\boot\spring-boot-starter-tomcat\2.4.4\spring-boot-starter-tomcat-2.4.4.jar;D:\RepMaven\org\apache\tomcat\embed\tomcat-embed-core\9.0.44\tomcat-embed-core-9.0.44.jar;D:\RepMaven\org\glassfish\jakarta.el\3.0.3\jakarta.el-3.0.3.jar;D:\RepMaven\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.44\tomcat-embed-websocket-9.0.44.jar;D:\RepMaven\org\springframework\spring-web\5.3.5\spring-web-5.3.5.jar;D:\RepMaven\org\springframework\spring-beans\5.3.5\spring-beans-5.3.5.jar;D:\RepMaven\org\springframework\spring-webmvc\5.3.5\spring-webmvc-5.3.5.jar;D:\RepMaven\org\springframework\spring-aop\5.3.5\spring-aop-5.3.5.jar;D:\RepMaven\org\springframework\spring-context\5.3.5\spring-context-5.3.5.jar;D:\RepMaven\org\springframework\spring-expression\5.3.5\spring-expression-5.3.5.jar;D:\RepMaven\mysql\mysql-connector-java\8.0.23\mysql-connector-java-8.0.23.jar;D:\RepMaven\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;D:\RepMaven\org\springframework\spring-core\5.3.5\spring-core-5.3.5.jar;D:\RepMaven\org\springframework\spring-jcl\5.3.5\spring-jcl-5.3.5.jar;D:\JetBrains\IntelliJ IDEA 2020.3\lib\idea_rt.jar" com.yg.datastructures.linkedlist.SingleLinkedListDemo
Connected to the target VM, address: '127.0.0.1:1360', transport: 'socket'
----------准备打印列表------------
SingleLinkedListNode{id=1, name='李白', age=18, phone='18609287231'}
SingleLinkedListNode{id=2, name='杜甫', age=20, phone='18709873321'}
SingleLinkedListNode{id=4, name='李清照', age=25, phone='17638779333'}
----------打印列表结束------------
-------------删除后链表--------------
----------准备打印列表------------
SingleLinkedListNode{id=1, name='李白', age=18, phone='18609287231'}
SingleLinkedListNode{id=4, name='李清照', age=25, phone='17638779333'}
----------打印列表结束------------
-------------添加5后链表--------------
----------准备打印列表------------
SingleLinkedListNode{id=1, name='李白', age=18, phone='18609287231'}
SingleLinkedListNode{id=4, name='李清照', age=25, phone='17638779333'}
SingleLinkedListNode{id=5, name='王安石', age=33, phone='1887788772'}
----------打印列表结束------------
-------------顺序添加23后链表--------------
----------准备打印列表------------
SingleLinkedListNode{id=1, name='李白', age=18, phone='18609287231'}
SingleLinkedListNode{id=2, name='杜甫', age=20, phone='18709873321'}
SingleLinkedListNode{id=3, name='白居易', age=22, phone='1818873321'}
SingleLinkedListNode{id=4, name='李清照', age=25, phone='17638779333'}
SingleLinkedListNode{id=5, name='王安石', age=33, phone='1887788772'}
----------打印列表结束------------
-------------修改后链表--------------
----------准备打印列表------------
SingleLinkedListNode{id=1, name='茅以升', age=33, phone='1879983332'}
SingleLinkedListNode{id=2, name='杜甫', age=20, phone='18709873321'}
SingleLinkedListNode{id=3, name='白居易', age=22, phone='1818873321'}
SingleLinkedListNode{id=4, name='李清照', age=25, phone='17638779333'}
SingleLinkedListNode{id=5, name='王安石', age=33, phone='1887788772'}
----------打印列表结束------------
链表长度length = 5
链表的倒数第3个节点=SingleLinkedListNode{id=3, name='白居易', age=22, phone='1818873321'}
---------------反转后的链表为--------------
----------准备打印列表------------
SingleLinkedListNode{id=5, name='王安石', age=33, phone='1887788772'}
SingleLinkedListNode{id=4, name='李清照', age=25, phone='17638779333'}
SingleLinkedListNode{id=3, name='白居易', age=22, phone='1818873321'}
SingleLinkedListNode{id=2, name='杜甫', age=20, phone='18709873321'}
SingleLinkedListNode{id=1, name='茅以升', age=33, phone='1879983332'}
----------打印列表结束------------
----------------逆序打印链表---------------
出栈顺序:SingleLinkedListNode{id=1, name='茅以升', age=33, phone='1879983332'}
出栈顺序:SingleLinkedListNode{id=2, name='杜甫', age=20, phone='18709873321'}
出栈顺序:SingleLinkedListNode{id=3, name='白居易', age=22, phone='1818873321'}
出栈顺序:SingleLinkedListNode{id=4, name='李清照', age=25, phone='17638779333'}
出栈顺序:SingleLinkedListNode{id=5, name='王安石', age=33, phone='1887788772'}
Disconnected from the target VM, address: '127.0.0.1:1360', transport: 'socket'

Process finished with exit code 0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值