leetCode 237. Delete Node in a Linked List

本文探讨了在仅能访问待删除节点的情况下,如何高效地完成单向链表中的节点删除操作。通过巧妙利用节点之间的连接特性,提出了一种优于传统O(n)复杂度的方法,并给出了具体的实现代码。

    题目链接:https://leetcode.com/problems/delete-node-in-a-linked-list/

    题目内容:

Write a function to delete a node (except the tail) in a singly linked list, given only access to that node.

Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node with value 3, the linked list should become 1 -> 2 -> 4 after calling your function.

    题目分析:

    题目大意是给定一个单向链表(强调了是单向),给定链表中的一个节点,要求从链表中删除这个节点。如果没记错的话,以前删除链表中的节点使用的是O(n)的暴力解法:遍历链表,比较节点中的value值,找到后将该节点的前一个节点的next指针指向该删除节点的next,delete之,再将其赋值为NULL(一气呵成,perfect)。不过这是O(n)的解法,而且在本题中,访问该链表的入口时改删除的节点,不用你找,但是找不到该节点的前一个节点,无法将其指向删除节点的next(单向链表)。思考良久之后突然有了想法,主要是从题目中得到的启发,"为什么题目要强调删除的节点不会是最后一个节点(except the tail)",我的思路如下:为什么一定要删除该节点node,既然找不到删除节点node的上一个节点,只要把下一节点的属性全都赋给删除节点node,然后后面的节点一个一个往上覆盖,最后一个节点丢弃,perfect!(多傻多天真^o^)这种解法是可以的,运行效率也不会差到哪里去,提交至,AC,主要要考虑边界问题,比如最后一个节点丢弃时候的操作,由于已经优化,原来的代码找不到了,也不想再写一遍丢碾。

    优化是酱紫的:事实上,上面解法没必要把node节点以后的节点一个一个往上游覆盖,只需将node的下一个节点nnode的val和next覆盖到node之后,将nnode删掉并赋为空就可以了,后面的链表结构完全不用改动。这就是后来的解法:

void deleteNode(ListNode* node) {
    ListNode *del = node->next;
    node->val = node->next->val;
    node->next= node->next->next;
    delete del;
    del = NULL;
}
    肯定是AC,而且效率的确提高了。但是看到讨论区说有更好的,没找到,找得到的是用其他语言实现,但是思路一样,我就试一下用java来实现,提交之后发现耗时的确少了,至今不知道为什么。后面好几次提交leetCode题目的时候用java版本代替C++代码发现效率都有提升,leetCode心,海底针。

public void deleteNode(ListNode node) {
    ListNode del = node.next;
    node.val = del.val;
    node.next = del.next;
}


<think>好吧,我现在要解决的问题是删除链表中的一个节点,但给定的函数参数只有要删除的节点本身,而不是链表的头节点。这看起来有点挑战性,因为我不能直接遍历链表找到前一个节点。让我仔细想想应该怎么做。首先,题目给出的链表结构是这样的,每个节点有一个val和一个指向下一个节点的next指针。函数deleteNode的参数是一个ListNode*node,也就是需要被删除的那个节点。通常删除链表节点的方法是需要知道前一个节点,然后将前一个节点的next指向当前节点的下一个节点。但这里没有头节点,所以常规方法行不通。那怎么办呢?我想到了可能的方法是把当前节点的值和指针都替换成下一个节点的。比如,假设要删除的节点是A,下一个节点是B。那么,我们可以把A的值设置为B的值,然后把A的next指向B的下一个节点。这样,实际上相当于把B的内容复制到A,然后跳过了B。这样,原来的A节点就变成了B节点的内容,而B节点就被删除了。这种方法的好处是不需要知道前一个节点,只需要操作当前节点和下一个节点即可。不过需要注意的是,这个方法的前提是当前节点不是链表的尾节点。因为如果是尾节点的话,就没有下一个节点可以复制了。但题目中可能已经假设了要删除的节点不是末尾节点,或者题目保证输入的节点不会是尾节点。比如,在LeetCode的类似问题中,通常会给出这样的条件。所以在这种情况下,这种方法是可行的。 那具体怎么实现呢?假设当前节点是node,那么node->next存在。这时候,可以将node的val设置为node->next的val,然后把node的next指向node->next->next。这样就相当于跳过了下一个节点,达到了删除当前节点的效果。或者更直接一点,把当前节点的内存替换成下一个节点的。比如,用*node= *node->next;这样的代码。因为这样会把下一个节点的所有内容(包括val和next)都复制到当前节点,然后原来的下一个节点就被跳过了。不过这样做的话,原来的下一个节点的内存可能没有被释放,如果有动态分配的话可能会有内存泄漏。但题目中的链表可能没有要求释放内存,或者假设节点是动态分配的,由其他部分处理。在这种情况下,这种方法可以正确完成题目要求。那在代码中,原来的注释是//*node= *node->next;,只需要取消注释即可。或者,按照另一种方式,将node的val设为下一个节点的val,然后调整next指针。这两种方法其实是等效的。例如:node->val =node->next->val;node->next =node->next->next;这样写的话,可能更直观一些,而使用结构体赋值的方式可能更简洁。这两种方法都可行,但需要注意内存泄漏的问题。不过在这个问题中,可能不需要处理,因为题目可能接受这样的解法。 那么,正确的代码应该是这样的: classSolution {public:voiddeleteNode(ListNode*node){ListNode*nextNode= node->next;*node= *nextNode; //复制下一个节点的内容到当前节点//或者另一种写法:// node->val= nextNode->val;//node->next =nextNode->next;delete nextNode;//是否需要删除?原题可能没有考虑,但可能引起问题。} };不过,如果原题中的链表节点是动态分配的,并且我们确实需要释放内存的话,那么可能需要删除原来的下一个节点。但直接调用delete nextNode可能会导致问题,因为原链表中的其他节点可能还持有这个指针。例如,如果node是第二个节点,而第三个节点被删除,那么第二个节点的next指针已经被修改为第四个节点,但是第三个节点被delete了,这可能没问题?或者题目可能不要求释放内存,只调整结构即可。比如在LeetCode的测试用例中,可能只关注链表的结构是否正确,而不关心内存是否泄漏。所以在这种情况下,可能不需要delete操作,直接修改指针即可。比如,原题给出的代码中的注释行是直接解引用了node->next赋值给*node。这可能将下一个节点的所有内容复制到当前节点,包括val和next指针。这样,当前节点就变成了下一个节点的内容,而原来的下一个节点就被跳过了。这样,原来的下一个节点实际上没有被删除,但它的内容已经被复制到当前节点,所以链表的结构是正确的。不过,这种情况下,原来的下一个节点可能没有被释放,导致内存泄漏。但可能题目允许这样的做法,或者测试用例不检查内存泄漏。因此,正确的解决方案是使用结构体赋值或者修改val和next指针的方式,将当前节点的内容替换为下一个节点的内容,从而间接删除当前节点。这样不需要访问前一个节点,时间复杂度为O(1)。总结一下,问题的关键在于无法访问前驱节点,所以需要将当前节点的信息替换为下一个节点的,从而跳过下一个节点。这样,当前节点就变成了下一个节点的内容,而原来的节点被覆盖,从而达到了删除当前节点的效果。这种方法的时间复杂度是O(1),非常高效。</think>要删除链表中的指定节点(非末尾节点),可以通过复制下一个节点的值并调整指针来实现: ```cpp class Solution { public: void deleteNode(ListNode* node) { ListNode* nextNode = node->next; *node = *nextNode; // 关键操作:整体结构复制 delete nextNode; // 防止内存泄漏(根据题目具体要求决定是否保留) } }; ``` **解释**: 1. **核心思路**:题目给定的是要删除的节点本身而非头节点,因此无法通过常规的前驱节点操作来实现。这里采用将下一个节点的内容整体复制到当前节点的方法,使得当前节点在逻辑上被替换为下一个节点,从而间接完成删除操作。 2. **结构体赋值**:`*node = *nextNode` 会执行浅拷贝,将下一个节点的值和指针完整复制到当前节点,这样当前节点就继承了下一个节点的所有属性,等同于让当前节点变成下一个节点。 3. **内存管理**:`delete nextNode` 用于释放被跳过的原下一个节点的内存,但需要注意: - 如果题目中的链表节点不允许修改内存分配方式(如某些在线判题系统不检查内存泄漏),可以省略这一步 - 在真实工程场景中,必须确保没有其他指针指向这个被删除的节点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值