算法通关村第一关——链表青铜挑战笔记

本文详细介绍了单链表的基本概念,包括节点、头结点和虚拟结点的作用。讲解了如何在Java中创建链表节点,以及增删改查的操作方法,如遍历、表头插入、中间插入、末尾插入和删除等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单链表

概念

链表

由多个节点组成的线性结构,每个节点有值和指向后继元素的next指针,最后一个元素next指向null。就像一条链子一样,有头有尾,环环相扣。
注意:一个节点可以有多个前驱,但只能有一个后继。引用一下教程文档的比喻,好比一夫一妻,可以有多个人喜欢你,但你只能有一个妻子,你只能指向她一个。

节点、头结点、虚拟结点

在链表中,每个点都由值和指向下一结点的地址组成,组成的这些独立的单元,成为结点
第一个结点称为**头结点。**头结点很重要,我们知道第一个元素是谁,就可以通过它访问遍历整个链表。
虚拟结点 dummyNode
我理解就是专门存储头结点地址的,方便我们找到和操作头结点。
即dummyNode.next = head。
不然获取head在很多时候会是一个麻烦事,而头结点又是如此的重要,所以拿一个结点来专门管理它。至于叫虚拟结点,我认为是因为它并不包含在链表中,不属于链表,只是我们为了方便而虚构出来的。

创建

根据链表的概念,我们在Java中创建一个相同结构的类来表示结点。即,两个属性,一个用于存放值,一个用于存放下一个结点。如下:

pubic class ListNode {
    private int data;
    private ListNode next;

    public ListNode(int data) {
        this.data = data;
    }
    // ...getter & setter 方法
}

简便一点的写法,可以把属性的修饰符改为public,这样可以直接操作属性,然后给一个构造器,给value赋值,next就给null(为了规范,不写的话也可以,操作的时候直接设置地址其实是一样的),如下:

pubic class ListNode {
    public int val;
    public ListNode next;

    public ListNode(int x) {
        val = x;
        next = null;
    }
}

增删改查

遍历链表

单链表不管什么操作,都是从头向后逐个访问。所以操作之后还能否找到表头非常重要。

pubilc static int getListLength(Node head) {
    int length = 0;
    Node node = head;
    while(node != null) {
        length++;
        node = node.next;
    }
    return length;
}

插入链表

单链表的插入需要考虑三种情况:首部、中部和尾部。

表头插入

操作过程
创建新结点newNode
-> newNode = head
-> head = newNode

只要把结点指向表头即可。注意head要重新指向新的表头

**打个比方:**老鹰捉小鸡,来了一个新的队首的同学当“头”,那我们就要叫这个新同学老鹰了,之前的老鹰就变成了小鸡。

中间插入

操作过程
遍历找到目标结点的前一个位置(这里叫它前驱结点)
-> newNode.next = 前驱结点.next
-> 前驱结点.next = newNode
**注意连接结点的顺序不能颠倒,因为你前驱结点指向newNode之后,他和原来的后继结点就断开了,就找不到了。
打个比方:可以把链表想象成一群盲人手拉着手,现在有一个新的盲人要加入进来。假设新人叫小新,要加入到小左和小右中间来。这时候如果小左松开手去拉小新,那就再也找不到小右了,只留小新在风中凌乱。那怎么办呢?小左先把小新的手小心翼翼交到小右手里,确保小新已经牢牢拉住小右后,小左就可以放心去拉住小新了。这样小左-小新-小右就安全的连在一起了。

末尾插入

只需要将尾结点指向新结点就可以。
尾结点 = newNode
因为不指定,newNode本来就是指向null的。

代码实现
/**
     * 链表插入
     *
     * @param head       链表头节点
     * @param nodeInsert 待插入节点
     * @param position   待插入位置,取值从2开始
     * @return 插入后得到的链表头节点
     */
    public static Node insertNode(Node head, Node nodeInsert, int position) {
        // 需要判空,否则后面可能会有空指针异常
        if (head == null) {
            return nodeInsert;// 如果头是空,那要插入的这个就是头了
        }
        //越界判断
        int size = getLength(head);// 已存放的元素个数
        if (position > size + 1 || position < 1) {
            System.out.println("位置参数越界");
            return head;
        }

        //在链表开头插入
        if (position == 1) {
            nodeInsert.next = head;
			// return nodeInsert;
            // 上面return还可以这么写:
            head = nodeInsert;
            return head;
        }

        Node pNode = head;
        int count = 1;
        while (count < position - 1) {
            pNode = pNode.next;
            count++;
        }
        nodeInsert.next = pNode.next;
        pNode.next = nodeInsert;

        return head;
    }

删除链表

表头删除

删除表头元素,只要执行 **head = head.next **就可以。原来的结点不可达,会被JVM回收掉。

中间删除

同样使用cur.next来比较,找到位置后,将 cur.next = cur.next.next

末尾删除

还是到尾结点的前一个位置,用cur.next来判断。执行cur.next = null,最后的尾结点不可达,会被JVM回收掉。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值