4.链表和递归

一. Leetcode中和链表相关的问题(203号问题)

删除链表中等于给定值 val 的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5

解法1: 不使用虚拟头节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {

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

        if(head == null){
            return null;
        }

        ListNode prev = head;
        while(prev.next != null){
            if(prev.next.val == val){
                ListNode delNode = prev.next;
                prev.next = delNode.next;
                delNode.next = null;
            }
            else{
                prev = prev.next;
            }
        }

        return head;
    }
}

解法2:使用虚拟头节点

public class Solution2 {
    public ListNode removeElements(ListNode head, int val) {

        ListNode dummyHead = new ListNode(-1);  //创建虚拟头节点
        dummyHead.next = head;


        ListNode prev = dummyHead;   //这样就确定第一个元素不会是val
        while (prev.next != null) {
            if (prev.next.val == val) {
                ListNode delNode = prev.next;
                prev.next = delNode.next;
                delNode.next = null;
            } else {
                prev = prev.next;
            }
        }

        return dummyHead.next;
    }
}

python3版本

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        dummyHead = ListNode(-1)
        dummyHead.next = head
        
        prev = dummyHead
        while prev.next != None:
            if prev.next.val == val:
                delNode = prev.next
                prev.next = delNode.next
                delNode.next = None
            else:
                prev = prev.next
        
        return dummyHead.next
            
        

二. 测试自己的Leetcode链表代码

在本地测试自己上一节的代码, 我们这样编辑代码:
ListNode.java

public class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
    }

    // 链表节点的构造函数
    // 使用arr为参数, 创建一个链表, 当前的ListNode为链表头节点
    public  ListNode(int[] arr){
        if(arr == null || arr.length == 0){
            throw new IllegalArgumentException("arr can not be empty");
        }

        this.val = arr[0];
        ListNode cur = this;
        for(int i = 1; i < arr.length; i++){
            cur.next = new ListNode(arr[i]);
            cur = cur.next;
        }
    }


    // 以当前的节点为头节点饿的链表信息字符串
    @Override
    public String toString(){
        StringBuilder res = new StringBuilder();

        ListNode cur = this;

        while(cur != null){
            res.append(cur.val + "->");
            cur = cur.next;
        }

        res.append("NULL");

        return res.toString();
    }

Solution.java存放解法1, Solution2存放解法2. 我们在它们中增加测试代码
以Solution.java为例:

class Solution {
    public ListNode removeElements(ListNode head, int val) {

      ···
    }

    // 测试
    public static void main(String[] args){
        int[] nums = {1,2,6,3,4,5,6};
        ListNode head = new ListNode(nums);
        System.out.println(head);


        ListNode res = (new Solution()).removeElements(head, 6);
        System.out.println(res);
    }
}

运行结果:

1->2->6->3->4->5->6->NULL
1->2->3->4->5->NULL


三. 递归基础与递归宏观语义

递归

  • 本质上,将原来的问题, 转化为更小的同一问题

image

将该例子用代码实现 Sum.java:

public class Sum {
    public static int sum(int[] arr) {
        return sum(arr, 0);
    }

    // 计算arr[l...n)这个区间内所有数字的和
    private static int sum(int[] arr, int l) {
        if (l == arr.length) {   //递归结束条件, 即最小最基本的问题
            return 0;
        }
        return arr[l] + sum(arr, l + 1);  // 把原问题转化为更小的问题
    }

    // 测试
    public static void main(String[] args) {

        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8};
        System.out.println(sum(nums));
    }

运行结果

36

四. 链表的天然递归结构性质

image

根据链表这样的特性, 我们来解决第一节的了额头从的203号问题。


编写对应代码

Solution3.java

public class Solution3 {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) {
            return null;
        }

        head.next = removeElements(head.next, val);
        return head.val == val ? head.next : head;
    }


    // 测试
    public static void main(String[] args) {
        int[] nums = {1, 2, 6, 3, 4, 5, 6};
        ListNode head = new ListNode(nums);
        System.out.println(head);


        ListNode res = (new Solution3()).removeElements(head, 6);
        System.out.println(res);
    }
}

运行结果:

1->2->6->3->4->5->6->NULL
1->2->3->4->5->NULL

五. 递归算法的调试

推荐使用打印字符串的调试方式。
以Solution3.java为例子,我们这样修改:

public class Solution3 {
    public ListNode removeElements(ListNode head, int val, int depth) {
        String depthString = generateDepthString(depth);

        System.out.print(depthString);
        System.out.println("call: remove" + val + "in" + head);


        if (head == null) {
            System.out.print(depthString);
            System.out.println("return: " + head);
            return head;
        }

        head.next = removeElements(head.next, val, depth + 1);  // depth表示递归深度,每次递归都+1
        System.out.print(depthString);
        System.out.println("After remove" + val + ":" + head.next);

        ListNode ret;
        if (head.val == val) {
            ret = head.next;
        } else {
            ret = head;
        }

        System.out.print(depthString);
        System.out.println("Return: " + ret);
        return ret;
    }

    // 为了打印递归深度
    private String generateDepthString(int depth) {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < depth; i++) {
            res.append("--");    // 有多少递归深度,就打印多少个"--"
        }
        return res.toString();
    }

    // 测试
    public static void main(String[] args) {
        int[] nums = {1, 2, 6, 3, 4, 5, 6};
        ListNode head = new ListNode(nums);
        System.out.println(head);


        ListNode res = (new Solution3()).removeElements(head, 6, 0);
        System.out.println(res);
    }
}

运行结果:

1->2->6->3->4->5->6->NULL
call: remove6 in 1->2->6->3->4->5->6->NULL
--call: remove6 in 2->6->3->4->5->6->NULL
----call: remove6 in 6->3->4->5->6->NULL
------call: remove6 in 3->4->5->6->NULL
--------call: remove6 in 4->5->6->NULL
----------call: remove6 in 5->6->NULL
------------call: remove6 in 6->NULL
--------------call: remove6 in null
--------------return: null
------------After remove6: null
------------Return: null
----------After remove6: null
----------Return: 5->NULL
--------After remove6: 5->NULL
--------Return: 4->5->NULL
------After remove6: 4->5->NULL
------Return: 3->4->5->NULL
----After remove6: 3->4->5->NULL
----Return: 3->4->5->NULL
--After remove6: 3->4->5->NULL
--Return: 2->3->4->5->NULL
After remove6: 2->3->4->5->NULL
Return: 1->2->3->4->5->NULL
1->2->3->4->5->NULL
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值