关于课程2-9 的一点小问题。

关于课程2-9 的一点小问题。

    老师在这节课里面说了在以removeElements的那个递归里面是无法写成void的形式。我改写了算法发现的确无法做到,但我对自己算法的一个地方为什么没有按照我的想法实现有些疑惑请老师指教。

public static void removeElements5(ListNode head, int val) {
    while (head.next != null && head.val == val) {
        head = head.next;
    }
    while(head.next != null && head.next.val == val ){
        ListNode node = head.next;;
        head.next = node.next;
        node.next = null;
    }
    if(head.next == null) {
        return;
    }
    removeElements5(head.next, val);
}
public static void main(String[] args) {
    int[] nums = {1, 6, 6, 3, 2, 3, 4, 5, 6};
    ListNode head = new ListNode(nums);
    System.out.println(head);

    removeElements5(head, 6);
    System.out.println(head);
}

我的思路是第一个循环判断如果节点头和要删除的值相等就直接把下一个节点定义为节点头。直到节点头和要删除的值不相等位置。 

然后第二个循环判断该节点的下一个节点的值是否需要删除, 有的话就把它删除掉。 

然后在下面对这个函数进行反复调用,每次调用的是下一个节点。

我试验了以后发现只要节点头的值和需要删除的值不相等,

类似于 int[] nums = {1, 6, 6, 3, 2, 3, 4, 5, 6} 

结果就没有问题。 

但是 像这种 int[] nums = {6, 6, 6, 3, 2, 3, 4, 5, 6};

结果就很奇怪

也就是

while (head.next != null && head.val == val) {
   head = head.next;
}

这段没有写对,可以问下按照我的思路是哪里出了问题么,调试了很久始终没发现哪里有问题? 

正在回答 回答被采纳积分+1

登陆购买课程后可参与讨论,去登陆

1回答
liuyubobobo 2020-09-13 14:45:33

在 Leetcode 的这个问题中,是没有 dummyHead 的。head 就直接指向了第一个真正的存储元素的节点。


在你的 while 循环中,head.val == val 的节点,不一定要 head.next != null ,也需要删除,所以这个循环条件应该是:

while (head != null && head.val == val) {
    head = head.next;
}


一下这段代码是我基于你的代码修改的,可以在 Leetcode 203 号问题获得通过的代码:

class Solution {
    
    public ListNode removeElements(ListNode head, int val) {
        
        if(head == null) return head;
        
        while (head != null && head.val == val) {
            head = head.next;
        }
        
        if(head == null) return head;
        
        while(head.next != null && head.next.val == val ){
            ListNode node = head.next;;
            head.next = node.next;
            node.next = null;
        }
        
        if(head.next == null) {
            return head;
        }
        
        removeElements(head.next, val);
        
        return head;
    }
}


在你的这个实现思路下,在递归调用 removeElements 的时候,由于已经确保了 head.next 不会被删除,所以递归可以不返回值。如果对此有困惑,强烈建议:

  1. 再仔细体会一遍课程中介绍的 removeElements 的微观解读中,为什么要有返回值。测试一下,如果没有返回值,为什么是错误的。

  2. 使用同样微观解读的方式,一步一步看一下,上面的这个代码为什么是正确的?

进步就发生在这个过程中哦。加油!:)

  • 提问者 慕前端8369922 #1
    感谢老师的解答。 但我还是有一些不了解。 我建立了一个ListNodes int[] nums = {4, 5}; ListNode head = new ListNode(nums); ListNodes的class就是l203问题里面包含的那几个方法,以及老师视频里面写的tostring方法。 此时 System.out.println(head); 输出为: 4->5->NULL 然后我写了三个测试方法: public static void test(ListNode head) { head = new ListNode(0); head = head.next; } 然后 test(head); System.out.println(head); 输出为: 4->5->NULL 没有任何改变 第二个测试方法 public static void test2(ListNode head) { ListNode newNode = new ListNode(999); head.next = newNode; } 然后 test2(head); System.out.println(head); 输出为 4->999->NULL 成功的改变了。 然后第三个 public static void test3(ListNode head) { head.val = -999; } 然后 test3(head); System.out.println(head); 输出为 -999->999->NULL 也成功改变了 我的问题是为什么我无法改变head本身呢,我只能改变head里面的值,但这个对象本身我无法改变引用的指向? 这是为什么呢?
    2020-09-14 02:00:29
  • liuyubobobo 回复 提问者 慕前端8369922 #2
    我理解你的问题是第一个测试用例为什么不是 0->4->5->NULL? 这就是在这一小节我要讲的重点。 public static void test(ListNode head) 中,head 是一个参数,同时是一个引用。在这个函数结束以后,这个 head 就消失了。在 test 这个函数作用域中,这个 head 和外面的 head 是两个变量,只不过指向同一个内存而已。你在这个函数内改变这个 head 的指向,不会影响外面的 head 的指向。 在你的运行过程中,head = new ListNode(0); 函数内的 head 指向了一个新的 Node head = head.next;,函数内的 head 指向了空,因为上面创建的新的节点的 next 是空。 然后函数结束,这个 head 变量的生存周期结束。但函数外的 head 不受影响,没有改变。 而你剩下的两个 test,都通过 head 这个引用,直接修改了原链表的值,或者是 val,或者是 next。 请再体会一下在这一小节,我介绍的那种错入的 add 方法,为什么不能成功 add。 这个问题很关键。是透彻理解到底什么是引用(或者C/C++中的指针),进而更深入的理解程序运行机制的一个关键问题。 如果你还有不理解的地方,新开一个问题向我提问,因为评论中我无法用更丰富的格式来回答问题。
    2020-09-14 04:18:46
  • 提问者 慕前端8369922 回复 liuyubobobo #3
    感谢老师解答,这么一说就彻底清楚了
    2020-09-14 06:59:56
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
算法与数据结构
  • 参与学习       2583    人
  • 解答问题       1082    个

慕课网算法名师Liuyubobobo,5年集大成之作 从0到工作5年,算法与数据结构系统解决方案

了解课程
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师