Java 链表的原理 + 代码模拟实现无头单向非循环链表


前言

这次文章为大家带来了链表,模拟无头单向非循环链表的实现,希望可以对你有所帮助。😊😊


一、链表

1.链表的概念及结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
在这里插入图片描述


  • 链表性质

1.链表在逻辑上是连续的。
2.所以物理上可能连续,也可能不连续。(结点一般是从堆上申请出来的,申请的空间按照一定策略分配)


实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

  1. 单向或者双向
    在这里插入图片描述

  1. 带头或者不带头
    在这里插入图片描述

  1. 循环或者非循环
    在这里插入图片描述

本文分享的是无头单向非循环链表

二、模拟链表实现

public class SingleLinkedList {

    //定义一个静态内部类,作为链表的结点
    static class Node {
        int val;
        Node next;

        public Node(int val) {
            this.val = val;
        }
    }

    Node head;//头结点,即第一个结点

    //头插法
    public void addFirst(int data){
        //首先,先new一个新结点
        Node node = new Node(data);

        //头插,那么插入的这个结点就将变为第一个结点

        //所以要先存储旧的头结点
        node.next = head;

        //然后将类中的头结点更新
        head = node;
    }

    //尾插法
    public void addLast(int data){
        //首先,先new一个新结点
        Node node = new Node(data);

        //尾插,即插入的这个结点变为最后一个结点


        //此刻要考虑一种情况,如果此时链表当中没有数据,
        //那么新结点一定是第一个数据,直接更新头结点即可
        if(head == null) {
            head = node;
        }

        //先找到最后一个结点,即next==null的结点
        Node cur = head;//不改变类内存储的头结点,防止链表丢失
        while(cur.next != null) {
            cur = cur.next;
        }

        //将新结点的地址给末结点的next即可
        cur.next = node;
    }

    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
        //老规矩,要检查位置合法性,
        if(index > size()) { //size方法在下面
            //如果不合法则抛出异常
            throw new SingleLinedListRunException("输入位置不合法");
            //异常类的定义也在下面
        }

        //如果index为0,即首插法,
        if(index == 0) {
            addFirst(data);
            return;
        }

        //如果index==size()即尾插法
        if(index == size()) {
            addLast(data);
            return;
        }

        Node node = new Node(data);
        Node cur = head;

        //如果要将一个结点插入到某个位置,
        //那么找到这个位置的前一个结点
        //将这个结点的next改为新节点地址即可
        for (int i = 0; i < index-1; i++) {
            cur = cur.next;
        }

        //找到前一个结点后
        //首先将前一个结点中存储的地址存入新结点中
        node.next = cur.next;
        //然后将前一个结点的next改为新结点,就完成了插入
        cur.next = node;

    }

    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
        Node cur = head;
        //遍历整个链表,找到即返回true
        while (cur != null) {
            if(cur.val == key) {
                return true;
            }
            cur = cur.next;
        }

        //走到这一步,即整个链表中不存在key
        return false;
    }

    //删除第一次出现关键字为key的节点
    public void remove(int key){
        //考虑特殊情况,如果头结点就是要删除的结点呢?
        //直接改变类内存储的头结点
        if(head.val == key) {
            head = head.next;
            return;
        }

        Node cur = head;
        //要删除这个结点,只需找到这个结点的前一个结点
        while (cur.next != null) {
            if(cur.next.val == key) {
                break;
            }
            cur = cur.next;
        }

        //将前一个结点存储的地址改为要删除结点的后一个结点的地址
        cur.next = cur.next.next;
    }

    //删除所有值为key的节点
    public void removeAllKey(int key){
        Node cur = head;
        //要删除这个结点,只需找到这个结点的前一个结点
        while (cur.next != null) {
            if(cur.next.val == key) {
                //将前一个结点存储的地址改为要删除结点的后一个结点的地址
                //思路与上一个方法相同,只不过没有让代码找到一个就停止
                cur.next = cur.next.next;
            }
            cur = cur.next;
        }

        //此时除头结点外,其他值为key的结点都已经被删除了
        //那么现在就要考虑头结点了
        if(head.val == key) {
            head = head.next;
        }
    }

    //得到单链表的长度
    public int size(){
        //遍历链表,记录不为空的结点个数
        Node cur = head;
        int size = 0;
        while (cur != null) {
            size++;
            cur = cur.next;
        }
        return size;
    }

    public void clear() {
        //可以直接将类内存储的头结点变为null,其他结点,不再被引用,则会被一一回收
        head = null;
    }

    public void display() {
        //遍历并打印链表
        Node cur = head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }
}
//异常类的定义
public class SingleLinedListRunException extends RuntimeException{
    public SingleLinedListRunException(String message) {
        super(message);
    }
}

总结

以上就是今天要讲的内容,本文简单介绍了链表并模拟实现了无头单向非循环链表,如果对你有所帮助,请帮作者点个赞吧👍。

路漫漫,不止修身也养性。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值