总有适合你的方法去删除链表节点

本文详细介绍了链表的基本概念及不同类型,包括单链表、双链表和循环链表,并提供了链表中删除节点的三种实现方法及其代码示例。

目录

什么是链表?

链表的类型:

单链表:

双链表:

循环链表:

链表中怎么删除节点?

方法一:迭代的思想,设置傀儡节点。

方法二:递归的思路

方法三:原链表删除。暴力

相似力扣题型推荐:


什么是链表?

声明:节点和结点都描述的是一个东西(大家都这样说的),我真的很容易打错,理解万岁!

链表是一种通过指针串联在一起的线性结构,每一个结点是由两部分组成,一个是数据域,一个是指针域(存放下一个结点的位置),最后一个节点的指针域指向null(空指针的意思)。

 这里的A,B等大写字母都是我为了方便书写定义的,真正的表示方式使用十六进制表示的。

链表的入口点称为列表的头结点,也就是head。

链表的类型:

单链表:

 刚才描述的就是。

双链表:

双链表每一个结点都有两个指针域,一个指向下一个结点,另一个指向上一个节点。

双链表既可以 向前查询也可以向后查询。

如图:

 

循环链表:

顾名思义,就是链表首位相连。

循环链表可以用来解决约瑟夫环问题。

链表中怎么删除节点?

下面都是以单链表为例。

力扣上 :移除数组元素

连接:https://leetcode-cn.com/problems/remove-linked-list-elements/

方法一:迭代的思想,设置傀儡节点。

代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //用迭代  傀儡节点;
        ListNode dummyNode=new ListNode();
        dummyNode.next=head;
        ListNode cur=dummyNode;
        while(cur.next!=null){
            if(cur.next.val==val){
                cur.next=cur.next.next;
            }else{
                cur=cur.next;
            }
        }
        return dummyNode.next;
    }
}

思路:

有了傀儡节点之后就不需要对头结点进行单独考虑了,很方便。

方法二:递归的思路

代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //递归的思想;
        if(head==null){
            return head;
        }
        head.next=removeElements(head.next,val);
        return head.val==val?head.next:head;
    }
}

思路:
        递归的思路,  首先,安排除了第一个节点以外的节点,看它的值是否等于目标值,如果等于就原地处理,如果不等于就往后走。其次,处理完其它节点后再来看第一个节点的值是否等于目标值,如果等于就把头节点往后 挪,如果不等于就直接返回头结点。

方法三:原链表删除。暴力

代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //第三种做法 优先查看头结点,在看其它节点。
        while(head!=null && head.val==val){// 删除一个头结点后新的头结点的值可能也是目标值所以用循环来解决。
            head=head.next;
        }
        if(head==null){
            return head;
        }
        ListNode cur=head;
        while(cur.next!=null){
            if(cur.next.val==val){
                cur.next=cur.next.next;
            }else{
                cur=cur.next;
            }
        }
        return head;
    }
}

思路:先处理头节点,在处理其他所有节点。

相似力扣题型推荐:

  • 删除链表的节点;
  • 设计链表。(强烈推荐真的先把链表学好的童鞋来吧这道题好好做做,真的做会后没提升你来刀我。。。。。。)

<think> 我们参考了多个引用中关于链表节点删除方法和算法思路。从引用中可以看出,链表节点删除主要有两种常见情况:根据位置删除和根据节点本身删除(只给出要删除节点,但没有前驱节点信息)。 1. 根据位置删除节点(如单链表): 引用[1]中给出了一个单链表删除指定位置节点的代码示例。思路是: - 首先进行越界检查(位置不能小于0或大于等于链表长度)。 - 如果删除的是头节点(位置0),则直接将头节点指向下一个节点。 - 否则,遍历链表找到要删除位置的前一个节点(previous)和当前要删除节点(current)。 - 将前一个节点的next指向当前节点的下一个节点,然后调整链表大小(size)并返回被删除节点的值。 2. 根据节点本身删除(只给出要删除节点): 引用[3]和引用[4]给出了相同的思路:由于没有前驱节点的信息,无法直接修改前驱节点的next指针。因此采用“替身法”: - 将待删除节点的值替换为下一个节点的值。 - 然后删除下一个节点(即将待删除节点的next指针指向下一个节点的下一个节点)。 这种方法实际上是将下一个节点的值复制到当前节点,然后删除下一个节点,从而避免了前驱节点的查找。但要注意这种方法不适用于删除节点(因为尾节点没有下一个节点)。 3. 双向链表节点删除: 引用[2]给出了双向链表删除第n个数据的算法思路: - 遍历链表找到要删除节点的前一个节点(位置n-1)。 - 定位到要删除节点(即前一个节点的下一个节点)。 - 修改指针:将前一个节点的next指向待删除节点的下一个节点;将待删除节点的下一个节点的prior指向前一个节点(双向链表有两个指针)。 - 释放待删除节点的内存。 下面我们将分别给出单链表(根据位置删除和根据节点删除)和双向链表(根据位置删除)的算法描述和代码示例。 注意:引用[1]中删除操作的单链表实现代码(TypeScript)和引用[4]中的C++代码(删除给定节点)以及引用[2]的双向链表删除思路。 </think>### 链表节点删除的实现方法及算法思路 在链表删除节点需要根据链表类型(单链表/双向链表)和删除场景调整指针操作。以下是核心实现方法和算法思路: --- ### **一、单链表节点删除** #### 1. **根据位置删除节点** **算法思路**: 1. 越界检查:位置需在 `[0, size-1]` 范围内 2. 处理头节点删除: - 若删除位置为 `0`,直接让头指针指向下一节点 3. 处理中间/尾部节点: - 遍历链表找到目标节点的**前驱节点**(`previous`) - 将前驱节点的 `next` 指向目标节点的后继节点 4. 释放目标节点内存,链表长度减1 **代码实现**(TypeScript): ```typescript removeAt(position: number): T | null { if (position < 0 || position >= this.size) return null; // 越界检查 let current = this.head; // 删除节点 if (position === 0) { this.head = current!.next; } else { // 查找前驱节点 let previous = null; let index = 0; while (index++ < position && current) { previous = current; current = current.next; } // 修改前驱节点指针 previous!.next = current!.next; } this.size--; return current!.value; } ``` #### 2. **直接删除给定节点(无前驱信息)** **算法思路**(引用[3][4]的"替身法"): 1. 将目标节点的值替换为后继节点的值 2. 将目标节点的 `next` 指向后继节点的下一节点 3. **适用场景**:非尾节点且无法获取前驱节点 **代码实现**(JavaScript): ```javascript function deleteNode(node) { node.val = node.next.val; // 值替换 node.next = node.next.next; // 指针跳过下一节点 } ``` *例:删除 `[4,5,1,9]` 中的 `5` → 将 `5` 替换为 `1` → 指针跳过原 `1` → 结果 `[4,1,9]`* --- ### **二、双向链表节点删除** **算法思路**(引用[2]): 1. 遍历定位到目标节点的**前驱节点**(`cur`指向位置 `n-1`) 2. 获取目标节点 `del = cur.next` 3. 修改指针: ```markdown del->prior->next = del->next // 前驱指向后继 del->next->prior = del->prior // 后继指向前驱 ``` 4. 释放目标节点内存 **关键优势**: 双向链表可直接通过前驱/后继指针修改相邻节点关系,无需单链表的遍历回溯。 --- ### **三、边界处理与优化** 1. **头节点/尾节点**: - 头节点删除需更新链表头指针 - 尾节点删除需将前驱节点的 `next` 置为 `null` 2. **空链表检查**:删除前验证链表是否为空 3. **虚拟头节点**(引用[1]): - 添加永存头节点简化边界操作 - 避免对真实头节点的特殊处理 --- ### **复杂度分析** | 操作类型 | 时间复杂度 | 空间复杂度 | |----------------|------------|------------| | 单链表按位置删除 | $O(n)$ | $O(1)$ | | 双向链表按位置删除| $O(n)$ | $O(1)$ | | 直接删除给定节点 | $O(1)$ | $O(1)$ | --- ### **应用场景** 1. 内存受限系统(删除操作无需移动数据) 2. 高频插入/删除场景(如LRU缓存淘汰算法) 3. 实现栈/队列等动态数据结构 **典型问题**: - 删除链表倒数第N个节点(快慢指针) - 删除重复节点(哈希表+双指针)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值