java数据结构与算法之单链表

478 阅读6分钟

完成效果:单链表的增,删,改

什么是链表

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

百度百科

特点: 在内存中不是连续的,查找慢(从头开始依次查找)增删元素快

图解:

image.png

HeroNode 类分析

//每个 HeroNode 就是一个节点
public class HeroNode {
    public int id;
    public String name;
    public String nickName;

    //指向下一个节点
    public HeroNode next;

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

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

这个类很好理解,作为单链表的 bean 类

其中最关键的是 id 和 next 参数,其他的都是 HeroNode 的参数

增加元素


public class SingleLinkedList {
     //头结点,不存放数据
    public HeroNode head = new HeroNode(0, "", "");


    //添加节点
    public void add(HeroNode heroNode) {

        //头结点不能移动,所以使用一个临时变量来保存
        HeroNode temp = head;

        //当 temp.next 一直!=null,则说明不是最后一个元素
        //如果 temp.next == null 则 while 会自动结束,执行之后的代码
        while (temp.next != null) {
            //后移 temp
            temp = temp.next;
        }

        //当退出 while 时,temp 指向链表最后一个位置
        //然后将 temp.next 指向新的元素
        temp.next = heroNode;
    }
    
    
     //显示链表所有数据
    public void show() {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }

        //头结点不能动
        HeroNode temp = head.next;

        //如果 temp!=null 说明当前还不是最后一个,那么久循环遍历
        while (temp != null) {
            System.out.println(temp);

            //后移动 temp
            temp = temp.next;
        }
    }
}

使用:

linkedList.add(new HeroNode(1, "宋江", "及时雨"));
linkedList.add(new HeroNode(5, "秦明", "霹雳火"));
linkedList.add(new HeroNode(6, "柴进", "小旋风"));
linkedList.add(new HeroNode(3, "吴用", "智多星"));
linkedList.add(new HeroNode(2, "李逵", "黑旋风"));
linkedList.add(new HeroNode(4, "林冲", "豹子头"));

System.out.println("正常添加~");
linkedList.show();

运行结果为:

HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=5, name='秦明', nickName='霹雳火'}
HeroNode{no=6, name='柴进', nickName='小旋风'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=2, name='李逵', nickName='黑旋风'}

分析: 在单链表中,最核心的就是如下这段代码:

(增加,删除,修改都是 用过 while 来遍历所有数据的)

//当 temp.next 一直!=null,则说明不是最后一个元素
//如果 temp.next == null 则 while 会自动结束,执行之后的代码
while (temp.next != null) {
       //后移 temp
       temp = temp.next;
}

在单链表中,添加元素是从最后面添加的,所以需要while 到最后一个位置,最后一个位置的 next == null,所以当 while 循环结束时,就是最后一个元素的位置.

图解:

image.png

image.png

根据 ID从小大大排序 增加元素

先上代码,然后分析:

    /**
     * 添加节点 按照  id 来排序
     */
    public void addID(HeroNode heroNode) {

        //头结点不能移动,所以使用一个临时变量来遍历
        HeroNode temp = head;

        //存入的 heroNode 编号是否存在
        boolean flag = false;

        //temp.next != null就一直循环
        while (temp.next != null) {
            //当前的节点 > 传入的节点
            if (temp.next.id > heroNode.id) {
                /*
                 * 假设现在 temp.next = 6
                 *         heroNode.no = 3
                 *
                 * 那么 heroNode.no 应该添加到 temp 和 temp.next 之间
                 */
                break;
            } else if (temp.next.id == heroNode.id) {
                //添加的编号存在
                flag = true;
                break;
            }
            //temp 后移
            temp = temp.next;
        }

        if (flag) {
            System.out.printf("%d 号已经存在,名字为:%s\n", heroNode.id, heroNode.name);
            return;
        }
        //当退出 while 时,加入链表
        heroNode.next = temp.next;
        temp.next = heroNode;
    }

使用:

        linkedList.addID(new HeroNode(1, "宋江", "及时雨"));
        linkedList.addID(new HeroNode(5, "秦明", "霹雳火"));
        linkedList.addID(new HeroNode(5,"秦明","霹雳火"));
        linkedList.addID(new HeroNode(6, "柴进", "小旋风"));
        linkedList.addID(new HeroNode(3, "吴用", "智多星"));
        linkedList.addID(new HeroNode(2, "李逵", "黑旋风"));
        linkedList.addID(new HeroNode(4, "林冲", "豹子头"));

        System.out.println("根据 ID 添加~");
        linkedList.show();

运行结果为:

5 号已经存在,名字为:秦明
根据 ID 添加~
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='李逵', nickName='黑旋风'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
HeroNode{no=5, name='秦明', nickName='霹雳火'}
HeroNode{no=6, name='柴进', nickName='小旋风'}

根据 id 排序有 2 种情况:

  • 当前 id 存在(不能添加)

  • 当前 id 不存在,增加到合适的位置 假设现在单链表元素id 为(1,4,7,9,11),新增加的元素id为 5 ,那么 5 就应该增加到 4 和 7 之间

图解:

image.png

这部分不好理解,看不懂的话看视频

在来解释一下赋值的这两句话:

//当退出 while 时,加入链表
heroNode.next = temp.next;
temp.next = heroNode;

图解:

image.png

修改元素

/*
     * 根据 id 修改编号
     */
    public void upData(HeroNode heroNode) {

        HeroNode temp = head;

        //false 表示没有相同的 ID 不能更改
        boolean flag = false;

        //如果不为 null 就一直循环
        while (temp.next != null) {
            if (temp.id == heroNode.id) {
                //如果 temp.id == heroNode.id 那么则代表可以更改
                flag = true;
                break;
            }
            //temp 后移动
            temp = temp.next;
        }
        if (flag) {
            temp.name = heroNode.name;
            temp.nickName = heroNode.nickName;
        } else {
            System.out.printf("编号为:%d 名字为:%s 的英雄,不能更改\n", heroNode.id, heroNode.name);
        }
    }

使用:

        linkedList.addID(new HeroNode(1, "宋江", "及时雨"));
        linkedList.addID(new HeroNode(5, "秦明", "霹雳火"));
        linkedList.addID(new HeroNode(6, "柴进", "小旋风"));
        linkedList.addID(new HeroNode(3, "吴用", "智多星"));
        linkedList.addID(new HeroNode(2, "李逵", "黑旋风"));

        //测试相同的 id
        linkedList.addID(new HeroNode(1, "李逵", "黑旋风"));

        //测试不同的 id
        linkedList.upData(new HeroNode(8, "李逵", "黑旋风"));

        System.out.println("修改编号~");
        linkedList.show();

运行结果为:

1 号已经存在,名字为:李逵
编号为:8 名字为:李逵 的英雄,不能更改
修改编号~
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='李逵', nickName='黑旋风'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=5, name='秦明', nickName='霹雳火'}
HeroNode{no=6, name='柴进', nickName='小旋风'}

分析: 修改元素的前提就是链表中存在该元素才能修改

  • 根据 id 来判断当前链表中是否存在相同的元素

    • 相同则可以修改
    • 不相同则不能修改
  • 在方法中需要传入需要修改的值

  • 修改元素的时候找到该元素的位置,然后修改即可

图解:

image.png

删除元素

删除元素代码:

    /**
     * 根据编号删除
     */
    public void remove(int removeId) {

        HeroNode temp = head;

        //true 表示有可删除的编号
        boolean flag = false;
        while (temp.next != null) {

            //删除元素是通过 next 值来接触练习的
            //所以通过判断 next和传入的 id 如果相同则删除掉即可
            if (temp.next.id == removeId) {
                flag = true;
                break;
            }
            temp = temp.next;
        }

        //true 有相同的 id,可以删除
        if (flag) {
            //直接指向下一个元素,就完成的删除.
            temp.next =  temp.next.next;
        }else{
            System.out.printf("编号为:%d,的英雄不存在,不能删除\n",removeId);
        }
    }

使用:

//删除节点
    private static void initRemove(SingleLinkedList linkedList) {
        linkedList.addID(new HeroNode(1, "宋江", "及时雨"));
        linkedList.addID(new HeroNode(5, "秦明", "霹雳火"));
        linkedList.addID(new HeroNode(6, "柴进", "小旋风"));
        linkedList.addID(new HeroNode(3, "吴用", "智多星"));
        linkedList.addID(new HeroNode(2, "李逵", "黑旋风"));

        linkedList.remove(1);
        linkedList.remove(2);
        linkedList.remove(3);
        linkedList.remove(4);
        linkedList.remove(5);
        System.out.println("删除节点~");
        linkedList.show();
    }

运行结果为:

编号为:4,的英雄不存在,不能删除
删除节点~
HeroNode{no=6, name='柴进', nickName='小旋风'}

分析: 删除元素和替换元素思路是一样的,首先需要根据 id找到删除的位置,然后把当前指向位置指向下一个位置即可

image.png

如果需要删除元素二,那么只需要吧 id = 1 的 next 指向 id = 3 (hero.next = hero.next.next)

完整代码

猜你喜欢:

数据结构与算法目录

博主主页

原创不易,您的点赞加就是对我最大的支持!