leetcode 437路径总和III (深浅复制,hashmap(dic))

这篇博客详细介绍了LeetCode第437题《路径总和III》的解题思路,强调了题目难度并非表面那么简单。博主分享了在解决问题过程中遇到的深浅复制问题,指出浅复制可能导致不同递归路径的干扰,并提供了正确使用深复制的方法。此外,博主还探讨了如何利用HashMap优化算法,减少重复计算,提高效率,并给出了字典操作的实例。
摘要由CSDN通过智能技术生成

路径总和

在这里插入图片描述题目难度显示是简单,但是这道题并不容易,并且背后可以说道的东西不少。多次代码优化的过程让我收获不少,特此记录。
首先分析我最初的思路,考虑从上往下计算,每个节点保存可能的路径和。如,10这节点可以保存[10],5这个节点保存[10,5],3这个节点保存[3,5,10]。如此类推。代码如下:

class Solution(object):
    def pathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: int
        """
        node = [] 
        return self.nodepath(node, root, sum)

    def nodepath(self,node,root,sum):
        count = 0
        if not root:
            return 0
        for i in range(len(node)):
            node[i] += root.val
            if sum == node[i]:
                count += 1
        node.append(root.val)
        if root.val == sum:
            count += 1 
        count = count + self.nodepath(node[:], root.right,sum) + self.nodepath(node[:], root.left,sum)
        return count

深浅复制傻傻搞不清

首先值得说的就是这个深浅复制问题。浅复制只复制了原来的地址。没有复制内容。之前我们已经遇到过一次这个问题在DP问题初始化DP表的时候。

DP = [[0]*n]*m就是一种错误的初始化方式。因为每一列都不是独立的,对随之发生变化。

应该是DP = [[0 for i i range(n)]for j in range(m)]这样才可以保存独立。

这次我们是在递归上遇到了,由于list是可变对象,不同递归出口的path列表会相互影响。也就是说,在完成node.left的递归后list发生了变化,导致node.right开始运算时用的是上一次递归结束的list。

解决这一问题的方式是传进子递归函数的数组写为p[:]这样可以解决

对于深浅复制有以下实验:

>>> import copy
>>> a = [[10], 20]
>>> b = a[:]
>>> c = list(a)
>>> d = a * 1
>>> e = copy.copy(a)
>>> f = copy.deepcopy(a)
>>> a.append(21)
>>> a[0].append(11)
>>> print id(a), a
30553152 [[10, 11], 20, 21]
>>> print id(b), b
44969816 [[10, 11], 20]
>>> print id(c), c
44855664 [[10, 11], 20]
>>> print id(d), d
44971832 [[10, 11], 20]
>>> print id(e), e
44833088 [[10, 11], 20]
>>> print id(f), f
44834648 [[10], 20]

从以上可以看出,使用 a[:], list(a), a*1, copy.copy(a)四种方式复制列表结果都可以得到一个新的列表,但是如果列表中含有列表,所有b, c, d, e四个新列表的子列表都是指引到同一个对象上。
浅拷贝:只是复制了父元素,对于子元素还是收到原来列表的影响
只有使用copy.deepcopy(a)方法得到的新列表f才是包括子列表在内的完全复制。

hashmap的运用(dic)

可以看到上一版代码的主要循环在每次生成了一个节点的所有可能,并查询是否存在sum。如果用hash表就可以消除这个操作。因此从别人那里学习到的代码如下:

class Solution(object):
    def pathSum(self, root, sum):
        dic = {0:1}
        self.res = 0
        cur = 0
        self.helper(root,sum,cur,dic)
        return self.res
    
    def helper(self,root,sum,cur,dic):
        if not root:
            return
        cur += root.val
        self.res += dic.get(cur-sum,0)
        dic[cur] = dic.get(cur,0)+1
        self.helper(root.left,sum,cur,dic)
        self.helper(root.right,sum,cur,dic)
        dic[cur] -= 1

这代码妙啊,一下子看出了和自己的差距。我一点一点分析可以学到的东西。

  1. 全局变量
    可以看到代码里定义了一个self.res,这变量是可以在这个类下面使用的,在每次迭代中保持统一。
  2. 字典的示用
    首先是字典的定义dic = {},赋值方法可以dic[x] = y
    在访问字典的值时候,最好是用dic.get(key, x),其中x是当这个键不存在在字典中,返回的数值。
  3. dic[cur] -= 1
    为了防止在这一次递归完成(达到叶节点)后,后面在另外一枝上循环是使用到。测试的反例为[1,-2,-3] 1

关于字典的使用我还会慢慢的进一步补充。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值