数据结构之单链表超详细

单链表介绍和内存布局

链表是有序的列表,它在内存中存储结构如下:
下图是带头结点的链表
在这里插入图片描述

简单的描述一下:
头指针head 指向地址150的data域a1,a1的next域指向110的地址的data域a2,,依次类推。

从上图我们可以得到如下信息:

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

单链表(带头结点)逻辑结构示意图如下:

在这里插入图片描述

单链表的应用实例

使用带 head头的单向链表实现–水浒英雄排行榜管理完成对英雄人物的增删改查操作。

单链表的创建和和遍历分析

在这里插入图片描述

单链表的创建和和遍历代码

package LinkList;

/**
 * 单链表
 */
public class SingleLinkListTest {

    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        HeroNode heroNode1 = new HeroNode(1,"宋江","及时雨");
        HeroNode heroNode2 = new HeroNode(2,"卢俊义","玉麒麟");
        HeroNode heroNode3 = new HeroNode(3,"鲁智深","花和尚");
        singleLinkList.insert(heroNode1);
        singleLinkList.insert(heroNode2);
        singleLinkList.insert(heroNode3);

        singleLinkList.show();
    }
}

/**
 * 定义SingleLinkList管理 管理节点
 */
class SingleLinkList{

    /**
     * 初始化一个头结点,不存放具体的数据。
     */
    private HeroNode head = new HeroNode(0,"","");

    /**
     * 添加结点
     *
     * 不考虑当前编号,找到当前链表的最后一个节点,最后一个节点为NULL。
     * 将最后这个节点的next 指向新的节点。
     */
    public void insert(HeroNode heroNode){
        //因为头结点不动,我们需要一个辅助遍历
        HeroNode temp = head;

        //遍历链表
        while (true){
            //当temp的next==null 则找到了最后一个节点。
            if (temp.next == null){
                break;
            }
            //若没有找到,就将temp后移下一个节点
            temp = temp.next;
        }
        //退出循环时,temp指向了链表的最后,就可以将这个节点的next,指向新的节点。
        temp.next = heroNode;
    }

    /**
     * 遍历链表
     */
    public void show(){
        //通过一辅助遍历 遍历整个链表。
        //判断链表是否为空
        if (isEmpty()){
            System.out.println("链表当前没有元素");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            // 是否到了最后一个节点
            if (temp == null){
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }

    /**
     * 链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return head.next == null;
    }

}


/**
 * 定义HeroNode 每个HeroNode 对象就是一个节点
 */
class HeroNode{
    /**
     * 编号
     */
    public int no;
    /**
     * 名字
     */
    public String name;
    /**
     * 昵称
     */
    public String nickName;
    /**
     * 指向下一个节点
     */
    public HeroNode next;

    /**
     * 构造器
     * @param no 编号
     * @param name 名字
     * @param nickName 昵称
     */
    public HeroNode(int no,String name,String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

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

}


输出结果
在这里插入图片描述
目前insert插入没有考虑编号,怎么根据no排序了?

单链表按顺序插入节点

添加结点的时候,根据排名将结点插入到指定的位置(如果这个位置有人,则添加失败,并提示。)
在这里插入图片描述

代码实现

package LinkList;

import java.awt.image.BufferStrategy;
import java.beans.beancontext.BeanContext;

/**
 * 单链表
 */
public class SingleLinkListTest {

    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        HeroNode heroNode1 = new HeroNode(1,"宋江","及时雨");
        HeroNode heroNode2 = new HeroNode(6,"卢俊义","玉麒麟");
        HeroNode heroNode3 = new HeroNode(5,"鲁智深","花和尚");
        HeroNode heroNode4 = new HeroNode(4,"杨志","青面兽");
        HeroNode heroNode5 = new HeroNode(2,"吴用","智多星");

       /* singleLinkList.insert(heroNode1);
        singleLinkList.insert(heroNode2);
        singleLinkList.insert(heroNode3);*/

        singleLinkList.insertByOrder(heroNode1);
        singleLinkList.insertByOrder(heroNode2);
        singleLinkList.insertByOrder(heroNode3);
        singleLinkList.insertByOrder(heroNode4);
        singleLinkList.insertByOrder(heroNode5);

        singleLinkList.show();
    }
}

/**
 * 定义SingleLinkList管理 管理节点
 */
class SingleLinkList{

    /**
     * 初始化一个头结点,不存放具体的数据。
     */
    private HeroNode head = new HeroNode(0,"","");

    /**
     * 添加结点
     *
     * 不考虑当前编号,找到当前链表的最后一个节点,最后一个节点为NULL。
     * 将最后这个节点的next 指向新的节点。
     */
    public void insert(HeroNode heroNode){
        //因为头结点不动,我们需要一个辅助遍历
        HeroNode temp = head;

        //遍历链表
        while (true){
            //当temp的next==null 则找到了最后一个节点。
            if (temp.next == null){
                break;
            }
            //若没有找到,就将temp后移下一个节点
            temp = temp.next;
        }
        //退出循环时,temp指向了链表的最后,就可以将这个节点的next,指向新的节点。
        temp.next = heroNode;
    }

    /**
     * 按照no大小按升序序插入节点
     * @param heroNode
     */
    public void insertByOrder(HeroNode heroNode){
        //因为头结点不动,需要借助一个辅助,来帮助找到合适添加位置
        //单链表,temp是位于添加位置的前一个结点,否则插入失败, 因为我们可以根据上一个结点找到下一个节点位置,但是不能根据下一个节点找到上一个结点位置。
        HeroNode temp = head;
        // 添加no是否存在,默认为false
        boolean flag = false;

        while (true){
            // 找到链表结尾
            if (temp.next == null){
                break;
            }
            // 找到插入位置,就在temp的后面
            if (temp.next.no > heroNode.no){
                break;
            }else if (temp.next.no == heroNode.no){ //要添加的no已经存在
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }

        if (flag){
            throw new RuntimeException("待插入的编号:"+ heroNode.no + ",已经存在,插入失败。");
        }else {
            //将结点插入到链表,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    /**
     * 遍历链表
     */
    public void show(){
        //通过一辅助遍历 遍历整个链表。
        //判断链表是否为空
        if (isEmpty()){
            System.out.println("链表当前没有元素");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            // 是否到了最后一个节点
            if (temp == null){
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }

    /**
     * 链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return head.next == null;
    }

}


/**
 * 定义HeroNode 每个HeroNode 对象就是一个节点
 */
class HeroNode{
    /**
     * 编号
     */
    public int no;
    /**
     * 名字
     */
    public String name;
    /**
     * 昵称
     */
    public String nickName;
    /**
     * 指向下一个节点
     */
    public HeroNode next;

    /**
     * 构造器
     * @param no 编号
     * @param name 名字
     * @param nickName 昵称
     */
    public HeroNode(int no,String name,String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

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

}

输出结果
在这里插入图片描述

单链表结点的修改

修改结点的数据信息根据编号no来修改。

package LinkList;

import java.awt.image.BufferStrategy;
import java.beans.beancontext.BeanContext;

/**
 * 单链表
 */
public class SingleLinkListTest {

    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        HeroNode heroNode1 = new HeroNode(1,"宋江","及时雨");
        HeroNode heroNode2 = new HeroNode(6,"卢俊义","玉麒麟");
        HeroNode heroNode3 = new HeroNode(5,"鲁智深","花和尚");
        HeroNode heroNode4 = new HeroNode(4,"杨志","青面兽");
        HeroNode heroNode5 = new HeroNode(2,"吴用","智多星");

       /* singleLinkList.insert(heroNode1);
        singleLinkList.insert(heroNode2);
        singleLinkList.insert(heroNode3);*/

        //测试按照编号升序插入
        singleLinkList.insertByOrder(heroNode1);
        singleLinkList.insertByOrder(heroNode2);
        singleLinkList.insertByOrder(heroNode3);
        singleLinkList.insertByOrder(heroNode4);
        singleLinkList.insertByOrder(heroNode5);
        //测试修改
        HeroNode heroNode6 = new HeroNode(2,"吴用","大笨蛋");
        singleLinkList.update(heroNode6);
        
        singleLinkList.show();
    }
}

/**
 * 定义SingleLinkList管理 管理节点
 */
class SingleLinkList{

    /**
     * 初始化一个头结点,不存放具体的数据。
     */
    private HeroNode head = new HeroNode(0,"","");

    /**
     * 添加结点
     *
     * 不考虑当前编号,找到当前链表的最后一个节点,最后一个节点为NULL。
     * 将最后这个节点的next 指向新的节点。
     */
    public void insert(HeroNode heroNode){
        //因为头结点不动,我们需要一个辅助遍历
        HeroNode temp = head;

        //遍历链表
        while (true){
            //当temp的next==null 则找到了最后一个节点。
            if (temp.next == null){
                break;
            }
            //若没有找到,就将temp后移下一个节点
            temp = temp.next;
        }
        //退出循环时,temp指向了链表的最后,就可以将这个节点的next,指向新的节点。
        temp.next = heroNode;
    }

    /**
     * 按照no大小按升序序插入节点
     * @param heroNode
     */
    public void insertByOrder(HeroNode heroNode){
        //因为头结点不动,需要借助一个辅助,来帮助找到合适添加位置
        //单链表,temp是位于添加位置的前一个结点,否则插入失败, 因为我们可以根据上一个结点找到下一个节点位置,但是不能根据下一个节点找到上一个结点位置。
        HeroNode temp = head;
        // 添加no是否存在,默认为false
        boolean flag = false;

        while (true){
            // 找到链表结尾
            if (temp.next == null){
                break;
            }
            // 找到插入位置,就在temp的后面
            if (temp.next.no > heroNode.no){
                break;
            }else if (temp.next.no == heroNode.no){ //要添加的no已经存在
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }

        if (flag){
            throw new RuntimeException("待插入的编号:"+ heroNode.no + ",已经存在,插入失败。");
        }else {
            //将结点插入到链表,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }


    /**
     * 根据结点的编号no来修改结点data信息
     * @param newHeroNode
     */
    public void update(HeroNode newHeroNode){


        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        //找到需要修改修改的结点
        HeroNode temp = head.next;
        // 表示是否找到该结点
        boolean  flag = false;
        while (true){
            if (temp == null){ //找到了链表最后一个节点,遍历完该链表了。
                break;
            }
            // 找到该结点
            if (temp.no == newHeroNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //flag为true找到该结点,修改结点
        if (flag){
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
        }else {
            throw  new RuntimeException("当前链表没有结点no = "+ newHeroNode.no);
        }
    }

    /**
     * 遍历链表
     */
    public void show(){
        //通过一辅助遍历 遍历整个链表。
        //判断链表是否为空
        if (isEmpty()){
            System.out.println("链表当前没有元素");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            // 是否到了最后一个节点
            if (temp == null){
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }

    /**
     * 链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return head.next == null;
    }

}


/**
 * 定义HeroNode 每个HeroNode 对象就是一个节点
 */
class HeroNode{
    /**
     * 编号
     */
    public int no;
    /**
     * 名字
     */
    public String name;
    /**
     * 昵称
     */
    public String nickName;
    /**
     * 指向下一个节点
     */
    public HeroNode next;

    /**
     * 构造器
     * @param no 编号
     * @param name 名字
     * @param nickName 昵称
     */
    public HeroNode(int no,String name,String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

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

}

输出结果
在这里插入图片描述

删除结点

删除结点的操作其实和修改操作比较类似。
在这里插入图片描述

package LinkList;

import java.awt.image.BufferStrategy;
import java.beans.beancontext.BeanContext;

/**
 * 单链表
 */
public class SingleLinkListTest {

    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        HeroNode heroNode1 = new HeroNode(1,"宋江","及时雨");
        HeroNode heroNode2 = new HeroNode(6,"卢俊义","玉麒麟");
        HeroNode heroNode3 = new HeroNode(5,"鲁智深","花和尚");
        HeroNode heroNode4 = new HeroNode(4,"杨志","青面兽");
        HeroNode heroNode5 = new HeroNode(2,"吴用","智多星");

       /* singleLinkList.insert(heroNode1);
        singleLinkList.insert(heroNode2);
        singleLinkList.insert(heroNode3);*/

        //测试按照编号升序插入
        singleLinkList.insertByOrder(heroNode1);
        singleLinkList.insertByOrder(heroNode2);
        singleLinkList.insertByOrder(heroNode3);
        singleLinkList.insertByOrder(heroNode4);
        singleLinkList.insertByOrder(heroNode5);
        //测试修改
        HeroNode heroNode6 = new HeroNode(2,"吴用","大笨蛋");
        singleLinkList.update(heroNode6);

        singleLinkList.delete(2);

        singleLinkList.show();
    }
}

/**
 * 定义SingleLinkList管理 管理节点
 */
class SingleLinkList{

    /**
     * 初始化一个头结点,不存放具体的数据。
     */
    private HeroNode head = new HeroNode(0,"","");

    /**
     * 添加结点
     *
     * 不考虑当前编号,找到当前链表的最后一个节点,最后一个节点为NULL。
     * 将最后这个节点的next 指向新的节点。
     */
    public void insert(HeroNode heroNode){
        //因为头结点不动,我们需要一个辅助遍历
        HeroNode temp = head;

        //遍历链表
        while (true){
            //当temp的next==null 则找到了最后一个节点。
            if (temp.next == null){
                break;
            }
            //若没有找到,就将temp后移下一个节点
            temp = temp.next;
        }
        //退出循环时,temp指向了链表的最后,就可以将这个节点的next,指向新的节点。
        temp.next = heroNode;
    }

    /**
     * 按照no大小按升序序插入节点
     * @param heroNode
     */
    public void insertByOrder(HeroNode heroNode){
        //因为头结点不动,需要借助一个辅助,来帮助找到合适添加位置
        //单链表,temp是位于添加位置的前一个结点,否则插入失败, 因为我们可以根据上一个结点找到下一个节点位置,但是不能根据下一个节点找到上一个结点位置。
        HeroNode temp = head;
        // 添加no是否存在,默认为false
        boolean flag = false;

        while (true){
            // 找到链表结尾
            if (temp.next == null){
                break;
            }
            // 找到插入位置,就在temp的后面
            if (temp.next.no > heroNode.no){
                break;
            }else if (temp.next.no == heroNode.no){ //要添加的no已经存在
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }

        if (flag){
            throw new RuntimeException("待插入的编号:"+ heroNode.no + ",已经存在,插入失败。");
        }else {
            //将结点插入到链表,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }


    /**
     * 根据结点的编号no来修改结点data信息
     * @param newHeroNode
     */
    public void update(HeroNode newHeroNode){


        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        //找到需要修改修改的结点
        HeroNode temp = head.next;
        // 表示是否找到该结点
        boolean  flag = false;
        while (true){
            if (temp == null){ //找到了链表最后一个节点,遍历完该链表了。
                break;
            }
            // 找到该结点
            if (temp.no == newHeroNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //flag为true找到该结点,修改结点
        if (flag){
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
        }else {
            throw  new RuntimeException("当前链表没有结点no = "+ newHeroNode.no);
        }
    }

    /**
     * 删除结点
     * 比较的时候,是temp.next.no 和需要删除结点的no
     * @param no 待删除结点no
     */
    public void delete(int no){
        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        HeroNode temp = head;
        boolean flag = false;
        while (true){
            //找到了结点最后位置,依旧没有找到该元素
            if (temp.next == null){
                break;
            }
            //找到待删除结点的前一个结点temp
            if (temp.next.no == no){
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag){
            System.out.println("成功删除结点:"+ temp.next);
            temp.next = temp.next.next;
        }
    }

    /**
     * 遍历链表
     */
    public void show(){
        //通过一辅助遍历 遍历整个链表。
        //判断链表是否为空
        if (isEmpty()){
            System.out.println("链表当前没有元素");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            // 是否到了最后一个节点
            if (temp == null){
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }

    /**
     * 链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return head.next == null;
    }

}


/**
 * 定义HeroNode 每个HeroNode 对象就是一个节点
 */
class HeroNode{
    /**
     * 编号
     */
    public int no;
    /**
     * 名字
     */
    public String name;
    /**
     * 昵称
     */
    public String nickName;
    /**
     * 指向下一个节点
     */
    public HeroNode next;

    /**
     * 构造器
     * @param no 编号
     * @param name 名字
     * @param nickName 昵称
     */
    public HeroNode(int no,String name,String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

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

}

经典面试题

求单链表的结点个数

package LinkList;

import java.awt.image.BufferStrategy;
import java.beans.beancontext.BeanContext;

/**
 * 单链表
 */
public class SingleLinkListTest {

    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        HeroNode heroNode1 = new HeroNode(1,"宋江","及时雨");
        HeroNode heroNode2 = new HeroNode(6,"卢俊义","玉麒麟");
        HeroNode heroNode3 = new HeroNode(5,"鲁智深","花和尚");
        HeroNode heroNode4 = new HeroNode(4,"杨志","青面兽");
        HeroNode heroNode5 = new HeroNode(2,"吴用","智多星");

       /* singleLinkList.insert(heroNode1);
        singleLinkList.insert(heroNode2);
        singleLinkList.insert(heroNode3);*/

        //测试按照编号升序插入
        singleLinkList.insertByOrder(heroNode1);
        singleLinkList.insertByOrder(heroNode2);
        singleLinkList.insertByOrder(heroNode3);
        singleLinkList.insertByOrder(heroNode4);
        singleLinkList.insertByOrder(heroNode5);
        //测试修改
        HeroNode heroNode6 = new HeroNode(2,"吴用","大笨蛋");
        singleLinkList.update(heroNode6);

        singleLinkList.delete(2);

        singleLinkList.show();

        System.out.println("有效元素个数:" + singleLinkList.listLength(singleLinkList.getHead()));
    }
}

/**
 * 定义SingleLinkList管理 管理节点
 */
class SingleLinkList{

    /**
     * 初始化一个头结点,不存放具体的数据。
     */
    private HeroNode head = new HeroNode(0,"","");

    /**
     *
     * @return 头结点
     */
    public HeroNode getHead() {
        return head;
    }

    /**
     * 添加结点
     *
     * 不考虑当前编号,找到当前链表的最后一个节点,最后一个节点为NULL。
     * 将最后这个节点的next 指向新的节点。
     */
    public void insert(HeroNode heroNode){
        //因为头结点不动,我们需要一个辅助遍历
        HeroNode temp = head;

        //遍历链表
        while (true){
            //当temp的next==null 则找到了最后一个节点。
            if (temp.next == null){
                break;
            }
            //若没有找到,就将temp后移下一个节点
            temp = temp.next;
        }
        //退出循环时,temp指向了链表的最后,就可以将这个节点的next,指向新的节点。
        temp.next = heroNode;
    }

    /**
     * 按照no大小按升序序插入节点
     * @param heroNode
     */
    public void insertByOrder(HeroNode heroNode){
        //因为头结点不动,需要借助一个辅助,来帮助找到合适添加位置
        //单链表,temp是位于添加位置的前一个结点,否则插入失败, 因为我们可以根据上一个结点找到下一个节点位置,但是不能根据下一个节点找到上一个结点位置。
        HeroNode temp = head;
        // 添加no是否存在,默认为false
        boolean flag = false;

        while (true){
            // 找到链表结尾
            if (temp.next == null){
                break;
            }
            // 找到插入位置,就在temp的后面
            if (temp.next.no > heroNode.no){
                break;
            }else if (temp.next.no == heroNode.no){ //要添加的no已经存在
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }

        if (flag){
            throw new RuntimeException("待插入的编号:"+ heroNode.no + ",已经存在,插入失败。");
        }else {
            //将结点插入到链表,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }


    /**
     * 根据结点的编号no来修改结点data信息
     * @param newHeroNode
     */
    public void update(HeroNode newHeroNode){


        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        //找到需要修改修改的结点
        HeroNode temp = head.next;
        // 表示是否找到该结点
        boolean  flag = false;
        while (true){
            if (temp == null){ //找到了链表最后一个节点,遍历完该链表了。
                break;
            }
            // 找到该结点
            if (temp.no == newHeroNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //flag为true找到该结点,修改结点
        if (flag){
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
        }else {
            throw  new RuntimeException("当前链表没有结点no = "+ newHeroNode.no);
        }
    }

    /**
     * 删除结点
     * 比较的时候,是temp.next.no 和需要删除结点的no
     * @param no 待删除结点no
     */
    public void delete(int no){
        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        HeroNode temp = head;
        boolean flag = false;
        while (true){
            //找到了结点最后位置,依旧没有找到该元素
            if (temp.next == null){
                break;
            }
            //找到待删除结点的前一个结点temp
            if (temp.next.no == no){
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag){
            System.out.println("成功删除结点:"+ temp.next);
            temp.next = temp.next.next;
        }
    }


    /**
     * 获取到链表结点的个数(如果是带头结点的链表,不统计头结点)
     * @param head 链表的头结点
     * @return
     */
    public int listLength(HeroNode head){
        //空链表 返回0
        if (head.next == null){
            return 0;
        }
        int length = 0;
        //定义一个辅助变量,不统计头结点
        HeroNode  cur = head.next;
        while (cur != null){
            length++;
            cur = cur.next;//遍历,指向下一个节点。
        }
        return length;
    }

    /**
     * 遍历链表
     */
    public void show(){
        //通过一辅助遍历 遍历整个链表。
        //判断链表是否为空
        if (isEmpty()){
            System.out.println("链表当前没有元素");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            // 是否到了最后一个节点
            if (temp == null){
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }

    /**
     * 链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return head.next == null;
    }

}


/**
 * 定义HeroNode 每个HeroNode 对象就是一个节点
 */
class HeroNode{
    /**
     * 编号
     */
    public int no;
    /**
     * 名字
     */
    public String name;
    /**
     * 昵称
     */
    public String nickName;
    /**
     * 指向下一个节点
     */
    public HeroNode next;

    /**
     * 构造器
     * @param no 编号
     * @param name 名字
     * @param nickName 昵称
     */
    public HeroNode(int no,String name,String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

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

}

查找单链表中倒数第K个结点

思路分析

  • 编写一个方法,接收head头结点,一个location位置。
  • location表示是倒数第index个结点。
  • 先把链表从头到尾遍历,获取到链表的总长度。
  • 得到总长度后,从链表的第一个开始遍历(size-index)个。
package LinkList;

import java.awt.image.BufferStrategy;
import java.beans.beancontext.BeanContext;

/**
 * 单链表
 */
public class SingleLinkListTest {

    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        HeroNode heroNode1 = new HeroNode(1,"宋江","及时雨");
        HeroNode heroNode2 = new HeroNode(6,"卢俊义","玉麒麟");
        HeroNode heroNode3 = new HeroNode(5,"鲁智深","花和尚");
        HeroNode heroNode4 = new HeroNode(4,"杨志","青面兽");
        HeroNode heroNode5 = new HeroNode(2,"吴用","智多星");

       /* singleLinkList.insert(heroNode1);
        singleLinkList.insert(heroNode2);
        singleLinkList.insert(heroNode3);*/

        //测试按照编号升序插入
        singleLinkList.insertByOrder(heroNode1);
        singleLinkList.insertByOrder(heroNode2);
        singleLinkList.insertByOrder(heroNode3);
        singleLinkList.insertByOrder(heroNode4);
        singleLinkList.insertByOrder(heroNode5);
        //测试修改
        HeroNode heroNode6 = new HeroNode(2,"吴用","大笨蛋");
        singleLinkList.update(heroNode6);

        singleLinkList.delete(2);

        singleLinkList.show();

        System.out.println("有效元素个数:" + singleLinkList.listLength(singleLinkList.getHead()));

        System.out.println("倒数第三个元素:" + singleLinkList.locateElem(singleLinkList.getHead(),3));
    }
}

/**
 * 定义SingleLinkList管理 管理节点
 */
class SingleLinkList{

    /**
     * 初始化一个头结点,不存放具体的数据。
     */
    private HeroNode head = new HeroNode(0,"","");

    /**
     *
     * @return 头结点
     */
    public HeroNode getHead() {
        return head;
    }

    /**
     * 添加结点
     *
     * 不考虑当前编号,找到当前链表的最后一个节点,最后一个节点为NULL。
     * 将最后这个节点的next 指向新的节点。
     */
    public void insert(HeroNode heroNode){
        //因为头结点不动,我们需要一个辅助遍历
        HeroNode temp = head;

        //遍历链表
        while (true){
            //当temp的next==null 则找到了最后一个节点。
            if (temp.next == null){
                break;
            }
            //若没有找到,就将temp后移下一个节点
            temp = temp.next;
        }
        //退出循环时,temp指向了链表的最后,就可以将这个节点的next,指向新的节点。
        temp.next = heroNode;
    }

    /**
     * 按照no大小按升序序插入节点
     * @param heroNode
     */
    public void insertByOrder(HeroNode heroNode){
        //因为头结点不动,需要借助一个辅助,来帮助找到合适添加位置
        //单链表,temp是位于添加位置的前一个结点,否则插入失败, 因为我们可以根据上一个结点找到下一个节点位置,但是不能根据下一个节点找到上一个结点位置。
        HeroNode temp = head;
        // 添加no是否存在,默认为false
        boolean flag = false;

        while (true){
            // 找到链表结尾
            if (temp.next == null){
                break;
            }
            // 找到插入位置,就在temp的后面
            if (temp.next.no > heroNode.no){
                break;
            }else if (temp.next.no == heroNode.no){ //要添加的no已经存在
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }

        if (flag){
            throw new RuntimeException("待插入的编号:"+ heroNode.no + ",已经存在,插入失败。");
        }else {
            //将结点插入到链表,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }


    /**
     * 根据结点的编号no来修改结点data信息
     * @param newHeroNode
     */
    public void update(HeroNode newHeroNode){


        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        //找到需要修改修改的结点
        HeroNode temp = head.next;
        // 表示是否找到该结点
        boolean  flag = false;
        while (true){
            if (temp == null){ //找到了链表最后一个节点,遍历完该链表了。
                break;
            }
            // 找到该结点
            if (temp.no == newHeroNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //flag为true找到该结点,修改结点
        if (flag){
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
        }else {
            throw  new RuntimeException("当前链表没有结点no = "+ newHeroNode.no);
        }
    }

    /**
     * 删除结点
     * 比较的时候,是temp.next.no 和需要删除结点的no
     * @param no 待删除结点no
     */
    public void delete(int no){
        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        HeroNode temp = head;
        boolean flag = false;
        while (true){
            //找到了结点最后位置,依旧没有找到该元素
            if (temp.next == null){
                break;
            }
            //找到待删除结点的前一个结点temp
            if (temp.next.no == no){
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag){
            System.out.println("成功删除结点:"+ temp.next);
            temp.next = temp.next.next;
        }
    }


    /**
     * 获取到链表结点的个数(如果是带头结点的链表,不统计头结点)
     * @param head 链表的头结点
     * @return
     */
    public int listLength(HeroNode head){
        //空链表 返回0
        if (head.next == null){
            return 0;
        }
        int length = 0;
        //定义一个辅助变量,不统计头结点
        HeroNode  cur = head.next;
        while (cur != null){
            length++;
            cur = cur.next;//遍历,指向下一个节点。
        }
        return length;
    }


    /**
     * 查找单链表中倒数第K个结点
     * @param head 头结点
     * @param location 第N个
     * @return
     */
    public HeroNode locateElem(HeroNode head,int location){
        //链表为空,找不到 返回null
        if (isEmpty()){
            return null;
        }
        //第一次遍历,获取链表个数
        int length = listLength(head);
        //第二次遍历 length - location 就是倒数的第K个结点
        if (location <= 0 || location > length){
            return null;
        }
        //定义一个辅助变量,遍历
        HeroNode cur = head.next;
        for (int i = 0; i < length - location; i++) {
            cur = cur.next;
        }
        return cur;
    }

    /**
     * 遍历链表
     */
    public void show(){
        //通过一辅助遍历 遍历整个链表。
        //判断链表是否为空
        if (isEmpty()){
            System.out.println("链表当前没有元素");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            // 是否到了最后一个节点
            if (temp == null){
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }

    /**
     * 链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return head.next == null;
    }

}


/**
 * 定义HeroNode 每个HeroNode 对象就是一个节点
 */
class HeroNode{
    /**
     * 编号
     */
    public int no;
    /**
     * 名字
     */
    public String name;
    /**
     * 昵称
     */
    public String nickName;
    /**
     * 指向下一个节点
     */
    public HeroNode next;

    /**
     * 构造器
     * @param no 编号
     * @param name 名字
     * @param nickName 昵称
     */
    public HeroNode(int no,String name,String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

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

}

输出结果
在这里插入图片描述

单链表单转

在这里插入图片描述
在这里插入图片描述

package LinkList;

import java.awt.image.BufferStrategy;
import java.beans.beancontext.BeanContext;

/**
 * 单链表
 */
public class SingleLinkListTest {

    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        HeroNode heroNode1 = new HeroNode(1,"宋江","及时雨");
        HeroNode heroNode2 = new HeroNode(6,"卢俊义","玉麒麟");
        HeroNode heroNode3 = new HeroNode(5,"鲁智深","花和尚");
        HeroNode heroNode4 = new HeroNode(4,"杨志","青面兽");
        HeroNode heroNode5 = new HeroNode(2,"吴用","智多星");

       /* singleLinkList.insert(heroNode1);
        singleLinkList.insert(heroNode2);
        singleLinkList.insert(heroNode3);*/

        //测试按照编号升序插入
        singleLinkList.insertByOrder(heroNode1);
        singleLinkList.insertByOrder(heroNode2);
        singleLinkList.insertByOrder(heroNode3);
        singleLinkList.insertByOrder(heroNode4);
        singleLinkList.insertByOrder(heroNode5);
        //测试修改
/*        HeroNode heroNode6 = new HeroNode(2,"吴用","大笨蛋");
        singleLinkList.update(heroNode6);

        singleLinkList.delete(2);

        singleLinkList.show();

        System.out.println("有效元素个数:" + singleLinkList.listLength(singleLinkList.getHead()));

        System.out.println("倒数第三个元素:" + singleLinkList.locateElem(singleLinkList.getHead(),3));*/

        //测试反转
        System.out.println("原始单链表:");
        singleLinkList.show();

        singleLinkList.reverseList(singleLinkList.getHead());
        System.out.println("反转后的单链表:");
        singleLinkList.show();

    }
}

/**
 * 定义SingleLinkList管理 管理节点
 */
class SingleLinkList{

    /**
     * 初始化一个头结点,不存放具体的数据。
     */
    private HeroNode head = new HeroNode(0,"","");

    /**
     *
     * @return 头结点
     */
    public HeroNode getHead() {
        return head;
    }

    /**
     * 添加结点
     *
     * 不考虑当前编号,找到当前链表的最后一个节点,最后一个节点为NULL。
     * 将最后这个节点的next 指向新的节点。
     */
    public void insert(HeroNode heroNode){
        //因为头结点不动,我们需要一个辅助遍历
        HeroNode temp = head;

        //遍历链表
        while (true){
            //当temp的next==null 则找到了最后一个节点。
            if (temp.next == null){
                break;
            }
            //若没有找到,就将temp后移下一个节点
            temp = temp.next;
        }
        //退出循环时,temp指向了链表的最后,就可以将这个节点的next,指向新的节点。
        temp.next = heroNode;
    }

    /**
     * 按照no大小按升序序插入节点
     * @param heroNode
     */
    public void insertByOrder(HeroNode heroNode){
        //因为头结点不动,需要借助一个辅助,来帮助找到合适添加位置
        //单链表,temp是位于添加位置的前一个结点,否则插入失败, 因为我们可以根据上一个结点找到下一个节点位置,但是不能根据下一个节点找到上一个结点位置。
        HeroNode temp = head;
        // 添加no是否存在,默认为false
        boolean flag = false;

        while (true){
            // 找到链表结尾
            if (temp.next == null){
                break;
            }
            // 找到插入位置,就在temp的后面
            if (temp.next.no > heroNode.no){
                break;
            }else if (temp.next.no == heroNode.no){ //要添加的no已经存在
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }

        if (flag){
            throw new RuntimeException("待插入的编号:"+ heroNode.no + ",已经存在,插入失败。");
        }else {
            //将结点插入到链表,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }


    /**
     * 根据结点的编号no来修改结点data信息
     * @param newHeroNode
     */
    public void update(HeroNode newHeroNode){


        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        //找到需要修改修改的结点
        HeroNode temp = head.next;
        // 表示是否找到该结点
        boolean  flag = false;
        while (true){
            if (temp == null){ //找到了链表最后一个节点,遍历完该链表了。
                break;
            }
            // 找到该结点
            if (temp.no == newHeroNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //flag为true找到该结点,修改结点
        if (flag){
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
        }else {
            throw  new RuntimeException("当前链表没有结点no = "+ newHeroNode.no);
        }
    }

    /**
     * 删除结点
     * 比较的时候,是temp.next.no 和需要删除结点的no
     * @param no 待删除结点no
     */
    public void delete(int no){
        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        HeroNode temp = head;
        boolean flag = false;
        while (true){
            //找到了结点最后位置,依旧没有找到该元素
            if (temp.next == null){
                break;
            }
            //找到待删除结点的前一个结点temp
            if (temp.next.no == no){
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag){
            System.out.println("成功删除结点:"+ temp.next);
            temp.next = temp.next.next;
        }
    }


    /**
     * 获取到链表结点的个数(如果是带头结点的链表,不统计头结点)
     * @param head 链表的头结点
     * @return
     */
    public int listLength(HeroNode head){
        //空链表 返回0
        if (head.next == null){
            return 0;
        }
        int length = 0;
        //定义一个辅助变量,不统计头结点
        HeroNode  cur = head.next;
        while (cur != null){
            length++;
            cur = cur.next;//遍历,指向下一个节点。
        }
        return length;
    }


    /**
     * 查找单链表中倒数第K个结点
     * @param head 头结点
     * @param location 第N个
     * @return
     */
    public HeroNode locateElem(HeroNode head,int location){
        //链表为空,找不到 返回null
        if (isEmpty()){
            return null;
        }
        //第一次遍历,获取链表个数
        int length = listLength(head);
        //第二次遍历 length - location 就是倒数的第K个结点
        if (location <= 0 || location > length){
            return null;
        }
        //定义一个辅助变量,遍历
        HeroNode cur = head.next;
        for (int i = 0; i < length - location; i++) {
            cur = cur.next;
        }
        return cur;
    }

    /**
     * 反转单链表
     * @param head 头结点
     */
    public void reverseList(HeroNode head){
        //单链表为空或者只有一个节点,直接返回
        if (head.next == null || head.next.next == null){
            return;
        }
        //定义一个辅助变量,帮助我们遍历原来的链表
        HeroNode cur = head.next;
        //指向当前节点的下一个结点
        HeroNode next = null;
        HeroNode reverseHead = new HeroNode(0,"","");
        //遍历原来的链表。遍历每一个结点,就将其取出,并放在新的链表reverseHead的前端。
        while (cur != null){
            //暂时保留当前节点的下一个节点,因为后面需要使用
            next = cur.next;
            //将cur的下一个结点指向新链表的最前端
            cur.next = reverseHead.next;
            //将cur连接到新的链表
            reverseHead.next = cur;
            //cur指向下一个节点 cur后移
            cur = next;
        }
        //head.next 指向reverseHead.next,实现反转。
        head.next = reverseHead.next;
    }

    /**
     * 遍历链表
     */
    public void show(){
        //通过一辅助遍历 遍历整个链表。
        //判断链表是否为空
        if (isEmpty()){
            System.out.println("链表当前没有元素");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            // 是否到了最后一个节点
            if (temp == null){
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }

    /**
     * 链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return head.next == null;
    }

}


/**
 * 定义HeroNode 每个HeroNode 对象就是一个节点
 */
class HeroNode{
    /**
     * 编号
     */
    public int no;
    /**
     * 名字
     */
    public String name;
    /**
     * 昵称
     */
    public String nickName;
    /**
     * 指向下一个节点
     */
    public HeroNode next;

    /**
     * 构造器
     * @param no 编号
     * @param name 名字
     * @param nickName 昵称
     */
    public HeroNode(int no,String name,String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

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

}

输出结果
在这里插入图片描述

从尾带头打印单链表

两种方式:1、反向遍历。2、Stack 栈
在这里插入图片描述

package LinkList;

import com.sun.xml.internal.bind.v2.model.core.ID;

import java.awt.image.BufferStrategy;
import java.beans.beancontext.BeanContext;
import java.util.Stack;

/**
 * 单链表
 */
public class SingleLinkListTest {

    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        HeroNode heroNode1 = new HeroNode(1,"宋江","及时雨");
        HeroNode heroNode2 = new HeroNode(6,"卢俊义","玉麒麟");
        HeroNode heroNode3 = new HeroNode(5,"鲁智深","花和尚");
        HeroNode heroNode4 = new HeroNode(4,"杨志","青面兽");
        HeroNode heroNode5 = new HeroNode(2,"吴用","智多星");

       /* singleLinkList.insert(heroNode1);
        singleLinkList.insert(heroNode2);
        singleLinkList.insert(heroNode3);*/

        //测试按照编号升序插入
        singleLinkList.insertByOrder(heroNode1);
        singleLinkList.insertByOrder(heroNode2);
        singleLinkList.insertByOrder(heroNode3);
        singleLinkList.insertByOrder(heroNode4);
        singleLinkList.insertByOrder(heroNode5);
        //测试修改
/*        HeroNode heroNode6 = new HeroNode(2,"吴用","大笨蛋");
        singleLinkList.update(heroNode6);

        singleLinkList.delete(2);

        singleLinkList.show();

        System.out.println("有效元素个数:" + singleLinkList.listLength(singleLinkList.getHead()));

        System.out.println("倒数第三个元素:" + singleLinkList.locateElem(singleLinkList.getHead(),3));*/

        //测试反转
/*        System.out.println("原始单链表:");
        singleLinkList.show();

        singleLinkList.reverseList(singleLinkList.getHead());
        System.out.println("反转后的单链表:");
        singleLinkList.show();*/

        //测试逆序打印
        System.out.println("原始单链表:");
        singleLinkList.show();
        
        System.out.println("逆序输出单链表:");
        singleLinkList.reversePrint(singleLinkList.getHead());


    }
}

/**
 * 定义SingleLinkList管理 管理节点
 */
class SingleLinkList{

    /**
     * 初始化一个头结点,不存放具体的数据。
     */
    private HeroNode head = new HeroNode(0,"","");

    /**
     *
     * @return 头结点
     */
    public HeroNode getHead() {
        return head;
    }

    /**
     * 添加结点
     *
     * 不考虑当前编号,找到当前链表的最后一个节点,最后一个节点为NULL。
     * 将最后这个节点的next 指向新的节点。
     */
    public void insert(HeroNode heroNode){
        //因为头结点不动,我们需要一个辅助遍历
        HeroNode temp = head;

        //遍历链表
        while (true){
            //当temp的next==null 则找到了最后一个节点。
            if (temp.next == null){
                break;
            }
            //若没有找到,就将temp后移下一个节点
            temp = temp.next;
        }
        //退出循环时,temp指向了链表的最后,就可以将这个节点的next,指向新的节点。
        temp.next = heroNode;
    }

    /**
     * 按照no大小按升序序插入节点
     * @param heroNode
     */
    public void insertByOrder(HeroNode heroNode){
        //因为头结点不动,需要借助一个辅助,来帮助找到合适添加位置
        //单链表,temp是位于添加位置的前一个结点,否则插入失败, 因为我们可以根据上一个结点找到下一个节点位置,但是不能根据下一个节点找到上一个结点位置。
        HeroNode temp = head;
        // 添加no是否存在,默认为false
        boolean flag = false;

        while (true){
            // 找到链表结尾
            if (temp.next == null){
                break;
            }
            // 找到插入位置,就在temp的后面
            if (temp.next.no > heroNode.no){
                break;
            }else if (temp.next.no == heroNode.no){ //要添加的no已经存在
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }

        if (flag){
            throw new RuntimeException("待插入的编号:"+ heroNode.no + ",已经存在,插入失败。");
        }else {
            //将结点插入到链表,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }


    /**
     * 根据结点的编号no来修改结点data信息
     * @param newHeroNode
     */
    public void update(HeroNode newHeroNode){


        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        //找到需要修改修改的结点
        HeroNode temp = head.next;
        // 表示是否找到该结点
        boolean  flag = false;
        while (true){
            if (temp == null){ //找到了链表最后一个节点,遍历完该链表了。
                break;
            }
            // 找到该结点
            if (temp.no == newHeroNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //flag为true找到该结点,修改结点
        if (flag){
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
        }else {
            throw  new RuntimeException("当前链表没有结点no = "+ newHeroNode.no);
        }
    }

    /**
     * 删除结点
     * 比较的时候,是temp.next.no 和需要删除结点的no
     * @param no 待删除结点no
     */
    public void delete(int no){
        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        HeroNode temp = head;
        boolean flag = false;
        while (true){
            //找到了结点最后位置,依旧没有找到该元素
            if (temp.next == null){
                break;
            }
            //找到待删除结点的前一个结点temp
            if (temp.next.no == no){
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag){
            System.out.println("成功删除结点:"+ temp.next);
            temp.next = temp.next.next;
        }
    }


    /**
     * 获取到链表结点的个数(如果是带头结点的链表,不统计头结点)
     * @param head 链表的头结点
     * @return
     */
    public int listLength(HeroNode head){
        //空链表 返回0
        if (head.next == null){
            return 0;
        }
        int length = 0;
        //定义一个辅助变量,不统计头结点
        HeroNode  cur = head.next;
        while (cur != null){
            length++;
            cur = cur.next;//遍历,指向下一个节点。
        }
        return length;
    }


    /**
     * 查找单链表中倒数第K个结点
     * @param head 头结点
     * @param location 第N个
     * @return
     */
    public HeroNode locateElem(HeroNode head,int location){
        //链表为空,找不到 返回null
        if (isEmpty()){
            return null;
        }
        //第一次遍历,获取链表个数
        int length = listLength(head);
        //第二次遍历 length - location 就是倒数的第K个结点
        if (location <= 0 || location > length){
            return null;
        }
        //定义一个辅助变量,遍历
        HeroNode cur = head.next;
        for (int i = 0; i < length - location; i++) {
            cur = cur.next;
        }
        return cur;
    }

    /**
     * 反转单链表
     * @param head 头结点
     */
    public void reverseList(HeroNode head){
        //单链表为空或者只有一个节点,直接返回
        if (head.next == null || head.next.next == null){
            return;
        }
        //定义一个辅助变量,帮助我们遍历原来的链表
        HeroNode cur = head.next;
        //指向当前节点的下一个结点
        HeroNode next = null;
        HeroNode reverseHead = new HeroNode(0,"","");
        //遍历原来的链表。遍历每一个结点,就将其取出,并放在新的链表reverseHead的前端。
        while (cur != null){
            //暂时保留当前节点的下一个节点,因为后面需要使用
            next = cur.next;
            //将cur的下一个结点指向新链表的最前端
            cur.next = reverseHead.next;
            //将cur连接到新的链表
            reverseHead.next = cur;
            //cur指向下一个节点 cur后移
            cur = next;
        }
        //head.next 指向reverseHead.next,实现反转。
        head.next = reverseHead.next;
    }


    /**
     * 逆序打印链表
     * @param head 头结点
     */
    public void reversePrint(HeroNode head){
        //空链表不打印
        if (head.next == null){
            return;
        }
        //创建一个栈,将各个节点入栈
        Stack<HeroNode> heroNodeStack = new Stack<>();
        HeroNode cur = head.next;
        while (cur != null){
            heroNodeStack.push(cur);
            cur = cur.next;
        }
        //出栈
        while (heroNodeStack.size() > 0){
            System.out.println(heroNodeStack.pop());//先进后出
        }
    }

    /**
     * 遍历链表
     */
    public void show(){
        //通过一辅助遍历 遍历整个链表。
        //判断链表是否为空
        if (isEmpty()){
            System.out.println("链表当前没有元素");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            // 是否到了最后一个节点
            if (temp == null){
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }

    /**
     * 链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return head.next == null;
    }

}


/**
 * 定义HeroNode 每个HeroNode 对象就是一个节点
 */
class HeroNode{
    /**
     * 编号
     */
    public int no;
    /**
     * 名字
     */
    public String name;
    /**
     * 昵称
     */
    public String nickName;
    /**
     * 指向下一个节点
     */
    public HeroNode next;

    /**
     * 构造器
     * @param no 编号
     * @param name 名字
     * @param nickName 昵称
     */
    public HeroNode(int no,String name,String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

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

}

合并两个有序的单链表

在这里插入图片描述

/**
 * 单链表
 */
public class SingleLinkListTest {

    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        SingleLinkList singleLinkList1 = new SingleLinkList();
        SingleLinkList singleLinkList3 = new SingleLinkList();
        HeroNode heroNode1 = new HeroNode(1,"宋江","及时雨");
        HeroNode heroNode2 = new HeroNode(4,"卢俊义","玉麒麟");
        HeroNode heroNode3 = new HeroNode(7,"鲁智深","花和尚");
        HeroNode heroNode4 = new HeroNode(2,"杨志","青面兽");
        HeroNode heroNode5 = new HeroNode(5,"吴用","智多星");
        HeroNode heroNode6 = new HeroNode(8,"周通","小霸王");

        //测试按照编号升序插入
        singleLinkList.insertByOrder(heroNode1);
        singleLinkList.insertByOrder(heroNode2);
        singleLinkList.insertByOrder(heroNode3);
        singleLinkList1.insertByOrder(heroNode4);
        singleLinkList1.insertByOrder(heroNode5);
        singleLinkList1.insertByOrder(heroNode6);
 
        System.out.println("原始单链表1:");
        singleLinkList.show();
        System.out.println("原始单链表2:");
        singleLinkList1.show();

        System.out.println("合并后");
        SingleLinkList.twoLinkedList(singleLinkList.getHead(),singleLinkList1.getHead(),singleLinkList3.getHead());
        singleLinkList3.show();
    }
}


/**
 * 定义SingleLinkList管理 管理节点
 */
class SingleLinkList{

    /**
     * 初始化一个头结点,不存放具体的数据。
     */
    private HeroNode head = new HeroNode(0,"","");

    /**
     *
     * @return 头结点
     */
    public HeroNode getHead() {
        return head;
    }

    /**
     * 添加结点
     *
     * 不考虑当前编号,找到当前链表的最后一个节点,最后一个节点为NULL。
     * 将最后这个节点的next 指向新的节点。
     */
    public void insert(HeroNode heroNode){
        //因为头结点不动,我们需要一个辅助遍历
        HeroNode temp = head;

        //遍历链表
        while (true){
            //当temp的next==null 则找到了最后一个节点。
            if (temp.next == null){
                break;
            }
            //若没有找到,就将temp后移下一个节点
            temp = temp.next;
        }
        //退出循环时,temp指向了链表的最后,就可以将这个节点的next,指向新的节点。
        temp.next = heroNode;
    }

    /**
     * 按照no大小按升序序插入节点
     * @param heroNode
     */
    public void insertByOrder(HeroNode heroNode){
        //因为头结点不动,需要借助一个辅助,来帮助找到合适添加位置
        //单链表,temp是位于添加位置的前一个结点,否则插入失败, 因为我们可以根据上一个结点找到下一个节点位置,但是不能根据下一个节点找到上一个结点位置。
        HeroNode temp = head;
        // 添加no是否存在,默认为false
        boolean flag = false;

        while (true){
            // 找到链表结尾
            if (temp.next == null){
                break;
            }
            // 找到插入位置,就在temp的后面
            if (temp.next.no > heroNode.no){
                break;
            }else if (temp.next.no == heroNode.no){ //要添加的no已经存在
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }

        if (flag){
            throw new RuntimeException("待插入的编号:"+ heroNode.no + ",已经存在,插入失败。");
        }else {
            //将结点插入到链表,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }


    /**
     * 遍历链表
     */
    public void show(){
        //通过一辅助遍历 遍历整个链表。
        //判断链表是否为空
        if (isEmpty()){
            System.out.println("链表当前没有元素");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            // 是否到了最后一个节点
            if (temp == null){
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }

    /**
     * 链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return head.next == null;
    }

    /**
     * 合并两个有序链表
     * @param head1 头结点1
     * @param head2 头结点2
     * @param newHeroNode 空头结点
     */
    public static void twoLinkedList(HeroNode head1, HeroNode head2,HeroNode newHeroNode) {

        // 如果两个链表均为空,则无需合并,直接返回
        if (head1.next == null && head2.next == null) {
            return;
        }
        // 如果链表1为空,则将head3.next指向head2.next,实现链表2中的节点连接到链表3
        if (head1.next == null) {
            newHeroNode.next = head2.next;
        } else {
            // 将head3.next指向head1.next,实现链表1中的节点连接到链表3
            newHeroNode.next = head1.next;
            // 定义一个辅助的指针(变量),帮助我们遍历链表2
            HeroNode cur2 = head2.next;
            // 定义一个辅助的指针(变量),帮助我们遍历链表3
            HeroNode cur3 = newHeroNode;
            HeroNode next = null;
            // 遍历链表2,将其节点按顺序连接至链表3
            while (cur2 != null) {
                // 链表3遍历完毕后,可以直接将链表2剩下的节点连接至链表3的末尾
                if (cur3.next == null) {
                    cur3.next = cur2;
                    break;
                }
                // 在链表3中,找到第一个大于链表2中的节点编号的节点
                // 因为是单链表,找到的节点是位于添加位置的前一个节点,否则无法插入
                if (cur2.no <= cur3.next.no) {
                    next = cur2.next;  // 先暂时保存链表2中当前节点的下一个节点,方便后续使用
                    cur2.next = cur3.next;  // 将cur2的下一个节点指向cur3的下一个节点
                    cur3.next = cur2;  // 将cur2连接到链表3上
                    cur2 = next;  // 让cur2后移
                }
                // 遍历链表3
                cur3 = cur3.next;
            }
        }
    }




}


/**
 * 定义HeroNode 每个HeroNode 对象就是一个节点
 */
class HeroNode{
    /**
     * 编号
     */
    public int no;
    /**
     * 名字
     */
    public String name;
    /**
     * 昵称
     */
    public String nickName;
    /**
     * 指向下一个节点
     */
    public HeroNode next;

    /**
     * 构造器
     * @param no 编号
     * @param name 名字
     * @param nickName 昵称
     */
    public HeroNode(int no,String name,String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

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

}

完整代码

package LinkList;

import com.sun.xml.internal.bind.v2.model.core.ID;

import java.awt.image.BufferStrategy;
import java.beans.beancontext.BeanContext;
import java.util.Stack;

/**
 * 单链表
 */
public class SingleLinkListTest {

    public static void main(String[] args) {
        SingleLinkList singleLinkList = new SingleLinkList();
        SingleLinkList singleLinkList1 = new SingleLinkList();
        SingleLinkList singleLinkList3 = new SingleLinkList();
        HeroNode heroNode1 = new HeroNode(1,"宋江","及时雨");
        HeroNode heroNode2 = new HeroNode(4,"卢俊义","玉麒麟");
        HeroNode heroNode3 = new HeroNode(7,"鲁智深","花和尚");
        HeroNode heroNode4 = new HeroNode(2,"杨志","青面兽");
        HeroNode heroNode5 = new HeroNode(5,"吴用","智多星");
        HeroNode heroNode6 = new HeroNode(8,"周通","小霸王");

       /* singleLinkList.insert(heroNode1);
        singleLinkList.insert(heroNode2);
        singleLinkList.insert(heroNode3);*/

        //测试按照编号升序插入
        singleLinkList.insertByOrder(heroNode1);
        singleLinkList.insertByOrder(heroNode2);
        singleLinkList.insertByOrder(heroNode3);
        singleLinkList1.insertByOrder(heroNode4);
        singleLinkList1.insertByOrder(heroNode5);
        singleLinkList1.insertByOrder(heroNode6);
        //测试修改
/*        HeroNode heroNode6 = new HeroNode(2,"吴用","大笨蛋");
        singleLinkList.update(heroNode6);

        singleLinkList.delete(2);

        singleLinkList.show();

        System.out.println("有效元素个数:" + singleLinkList.listLength(singleLinkList.getHead()));

        System.out.println("倒数第三个元素:" + singleLinkList.locateElem(singleLinkList.getHead(),3));*/

        //测试反转
/*        System.out.println("原始单链表:");
        singleLinkList.show();

        singleLinkList.reverseList(singleLinkList.getHead());
        System.out.println("反转后的单链表:");
        singleLinkList.show();*/

        //测试逆序打印
/*        System.out.println("原始单链表:");
        singleLinkList.show();

        System.out.println("逆序输出单链表:");
        singleLinkList.reversePrint(singleLinkList.getHead());*/

        System.out.println("原始单链表1:");
        singleLinkList.show();
        System.out.println("原始单链表2:");
        singleLinkList1.show();


        System.out.println("合并后");
        SingleLinkList.twoLinkedList(singleLinkList.getHead(),singleLinkList1.getHead(),singleLinkList3.getHead());
        singleLinkList3.show();
    }
}

/**
 * 定义SingleLinkList管理 管理节点
 */
class SingleLinkList{

    /**
     * 初始化一个头结点,不存放具体的数据。
     */
    private HeroNode head = new HeroNode(0,"","");

    /**
     *
     * @return 头结点
     */
    public HeroNode getHead() {
        return head;
    }

    /**
     * 添加结点
     *
     * 不考虑当前编号,找到当前链表的最后一个节点,最后一个节点为NULL。
     * 将最后这个节点的next 指向新的节点。
     */
    public void insert(HeroNode heroNode){
        //因为头结点不动,我们需要一个辅助遍历
        HeroNode temp = head;

        //遍历链表
        while (true){
            //当temp的next==null 则找到了最后一个节点。
            if (temp.next == null){
                break;
            }
            //若没有找到,就将temp后移下一个节点
            temp = temp.next;
        }
        //退出循环时,temp指向了链表的最后,就可以将这个节点的next,指向新的节点。
        temp.next = heroNode;
    }

    /**
     * 按照no大小按升序序插入节点
     * @param heroNode
     */
    public void insertByOrder(HeroNode heroNode){
        //因为头结点不动,需要借助一个辅助,来帮助找到合适添加位置
        //单链表,temp是位于添加位置的前一个结点,否则插入失败, 因为我们可以根据上一个结点找到下一个节点位置,但是不能根据下一个节点找到上一个结点位置。
        HeroNode temp = head;
        // 添加no是否存在,默认为false
        boolean flag = false;

        while (true){
            // 找到链表结尾
            if (temp.next == null){
                break;
            }
            // 找到插入位置,就在temp的后面
            if (temp.next.no > heroNode.no){
                break;
            }else if (temp.next.no == heroNode.no){ //要添加的no已经存在
                flag = true; //说明编号存在
                break;
            }
            temp = temp.next;
        }

        if (flag){
            throw new RuntimeException("待插入的编号:"+ heroNode.no + ",已经存在,插入失败。");
        }else {
            //将结点插入到链表,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }


    /**
     * 根据结点的编号no来修改结点data信息
     * @param newHeroNode
     */
    public void update(HeroNode newHeroNode){


        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        //找到需要修改修改的结点
        HeroNode temp = head.next;
        // 表示是否找到该结点
        boolean  flag = false;
        while (true){
            if (temp == null){ //找到了链表最后一个节点,遍历完该链表了。
                break;
            }
            // 找到该结点
            if (temp.no == newHeroNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //flag为true找到该结点,修改结点
        if (flag){
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
        }else {
            throw  new RuntimeException("当前链表没有结点no = "+ newHeroNode.no);
        }
    }

    /**
     * 删除结点
     * 比较的时候,是temp.next.no 和需要删除结点的no
     * @param no 待删除结点no
     */
    public void delete(int no){
        if (isEmpty()){
            throw  new RuntimeException("当前链表为空");
        }
        HeroNode temp = head;
        boolean flag = false;
        while (true){
            //找到了结点最后位置,依旧没有找到该元素
            if (temp.next == null){
                break;
            }
            //找到待删除结点的前一个结点temp
            if (temp.next.no == no){
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag){
            System.out.println("成功删除结点:"+ temp.next);
            temp.next = temp.next.next;
        }
    }


    /**
     * 获取到链表结点的个数(如果是带头结点的链表,不统计头结点)
     * @param head 链表的头结点
     * @return
     */
    public int listLength(HeroNode head){
        //空链表 返回0
        if (head.next == null){
            return 0;
        }
        int length = 0;
        //定义一个辅助变量,不统计头结点
        HeroNode  cur = head.next;
        while (cur != null){
            length++;
            cur = cur.next;//遍历,指向下一个节点。
        }
        return length;
    }


    /**
     * 查找单链表中倒数第K个结点
     * @param head 头结点
     * @param location 第N个
     * @return
     */
    public HeroNode locateElem(HeroNode head,int location){
        //链表为空,找不到 返回null
        if (isEmpty()){
            return null;
        }
        //第一次遍历,获取链表个数
        int length = listLength(head);
        //第二次遍历 length - location 就是倒数的第K个结点
        if (location <= 0 || location > length){
            return null;
        }
        //定义一个辅助变量,遍历
        HeroNode cur = head.next;
        for (int i = 0; i < length - location; i++) {
            cur = cur.next;
        }
        return cur;
    }

    /**
     * 反转单链表
     * @param head 头结点
     */
    public void reverseList(HeroNode head){
        //单链表为空或者只有一个节点,直接返回
        if (head.next == null || head.next.next == null){
            return;
        }
        //定义一个辅助变量,帮助我们遍历原来的链表
        HeroNode cur = head.next;
        //指向当前节点的下一个结点
        HeroNode next = null;
        HeroNode reverseHead = new HeroNode(0,"","");
        //遍历原来的链表。遍历每一个结点,就将其取出,并放在新的链表reverseHead的前端。
        while (cur != null){
            //暂时保留当前节点的下一个节点,因为后面需要使用
            next = cur.next;
            //将cur的下一个结点指向新链表的最前端
            cur.next = reverseHead.next;
            //将cur连接到新的链表
            reverseHead.next = cur;
            //cur指向下一个节点 cur后移
            cur = next;
        }
        //head.next 指向reverseHead.next,实现反转。
        head.next = reverseHead.next;
    }


    /**
     * 逆序打印链表
     * @param head 头结点
     */
    public void reversePrint(HeroNode head){
        //空链表不打印
        if (head.next == null){
            return;
        }
        //创建一个栈,将各个节点入栈
        Stack<HeroNode> heroNodeStack = new Stack<>();
        HeroNode cur = head.next;
        while (cur != null){
            heroNodeStack.push(cur);
            cur = cur.next;
        }
        //出栈
        while (heroNodeStack.size() > 0){
            System.out.println(heroNodeStack.pop());//先进后出
        }
    }

    /**
     * 遍历链表
     */
    public void show(){
        //通过一辅助遍历 遍历整个链表。
        //判断链表是否为空
        if (isEmpty()){
            System.out.println("链表当前没有元素");
            return;
        }
        HeroNode temp = head.next;
        while (true){
            // 是否到了最后一个节点
            if (temp == null){
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //将next后移
            temp = temp.next;
        }
    }

    /**
     * 链表是否为空
     * @return
     */
    public boolean isEmpty(){
        return head.next == null;
    }


    /**
     * 合并两个有序链表
     * @param head1 头结点1
     * @param head2 头结点2
     * @param newHeroNode 空头结点
     */
    public static void twoLinkedList(HeroNode head1, HeroNode head2,HeroNode newHeroNode) {

        // 如果两个链表均为空,则无需合并,直接返回
        if (head1.next == null && head2.next == null) {
            return;
        }
        // 如果链表1为空,则将head3.next指向head2.next,实现链表2中的节点连接到链表3
        if (head1.next == null) {
            newHeroNode.next = head2.next;
        } else {
            // 将head3.next指向head1.next,实现链表1中的节点连接到链表3
            newHeroNode.next = head1.next;
            // 定义一个辅助的指针(变量),帮助我们遍历链表2
            HeroNode cur2 = head2.next;
            // 定义一个辅助的指针(变量),帮助我们遍历链表3
            HeroNode cur3 = newHeroNode;
            HeroNode next = null;
            // 遍历链表2,将其节点按顺序连接至链表3
            while (cur2 != null) {
                // 链表3遍历完毕后,可以直接将链表2剩下的节点连接至链表3的末尾
                if (cur3.next == null) {
                    cur3.next = cur2;
                    break;
                }
                // 在链表3中,找到第一个大于链表2中的节点编号的节点
                // 因为是单链表,找到的节点是位于添加位置的前一个节点,否则无法插入
                if (cur2.no <= cur3.next.no) {
                    next = cur2.next;  // 先暂时保存链表2中当前节点的下一个节点,方便后续使用
                    cur2.next = cur3.next;  // 将cur2的下一个节点指向cur3的下一个节点
                    cur3.next = cur2;  // 将cur2连接到链表3上
                    cur2 = next;  // 让cur2后移
                }
                // 遍历链表3
                cur3 = cur3.next;
            }
        }
    }




}


/**
 * 定义HeroNode 每个HeroNode 对象就是一个节点
 */
class HeroNode{
    /**
     * 编号
     */
    public int no;
    /**
     * 名字
     */
    public String name;
    /**
     * 昵称
     */
    public String nickName;
    /**
     * 指向下一个节点
     */
    public HeroNode next;

    /**
     * 构造器
     * @param no 编号
     * @param name 名字
     * @param nickName 昵称
     */
    public HeroNode(int no,String name,String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

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

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值