Python算法题集_路径总和 III

 Python算法题集_路径总和 III

  • 题437:路径总和 III
  • 1. 示例说明
  • 2. 题目解析
    • - 题意分解
    • - 优化思路
    • - 测量工具
  • 3. 代码展开
    • 1) 标准求解【DFS递归+暴力枚举】
    • 2) 改进版一【DFS递归+前缀和】
    • 3) 改进版二【DFS递归+字典哈希】
    • 4) 改进版三【DFS递归+默认字典】
  • 4. 最优算法

本文为Python算法题集之一的代码示例

题437:路径总和 III

1. 示例说明

  • 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum路径 的数目。

    路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

示例 1:

Python算法题集_路径总和 III_第1张图片

  输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
  输出:3
  解释:和等于 8 的路径有 3 条,如图所示。

示例 2:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:3

提示:

  • 二叉树的节点个数的范围是 [0,1000]
  • -109 <= Node.val <= 109
  • -1000 <= targetSum <= 1000

2. 题目解析

- 题意分解

  1. 本题是二叉树的路径求和问题
  2. 本题有两个计算部分,1为二叉树遍历,2是节点路径求和
  3. 基本的设计思路是递归自上而下,每个节点的根序列是确定的,枚举根序列的有效解数量进行合计

- 优化思路

  1. 通常优化:减少循环层次

  2. 通常优化:增加分支,减少计算集

  3. 通常优化:采用内置算法来提升计算速度

  4. 分析题目特点,分析最优解

    1. 可以采用前缀和思路求解,两个节点之间的前缀和之差即为之间的路径和

    2. 可以采用哈希列表【字典】来进行根序列的前缀和计数,任何前缀和=当前前缀和-targetSum的,之间路径即为有效路径

    3. 可以采用默认字典结构来简化代码,并检测默认字典结构性能


- 测量工具

  • 本地化测试说明:LeetCode网站测试运行时数据波动很大【可把页面视为功能测试】,因此需要本地化测试解决数据波动问题
  • CheckFuncPerf(本地化函数用时和内存占用测试模块)已上传到CSDN,地址:Python算法题集_检测函数用时和内存占用的模块
  • 本题本地化超时测试用例自己生成,详见【最优算法章节】

3. 代码展开

1) 标准求解【DFS递归+暴力枚举】

使用深度优先算法执行遍历,枚举路径和,检测后返回有效数

页面功能测试,马马虎虎,超过47%

import CheckFuncPerf as cfp

class Solution:
 def pathSum_base(self, root, targetSum):
     def getCount(root, uppers, targetSum):
         result = 0
         if not root:
             return result
         if root.val == targetSum:
             result += 1
         if uppers:
             isumupper = 0
             for iIdx in range(len(uppers)):
                 isumupper += uppers[-iIdx-1]
                 if isumupper+root.val == targetSum:
                     result += 1
         uppers.append(root.val)
         result += getCount(root.left, uppers, targetSum)
         result += getCount(root.right, uppers, targetSum)
         uppers.pop()
         return result
     uppers = []
     return getCount(root, uppers, targetSum)

aSolution = Solution()
aroot = generate_binary_tree(ilen, imode)
result = cfp.getTimeMemoryStr(Solution.pathSum_base, aSolution, aroot, itargetSum)
print(result['msg'], '执行结果 = {}'.format(result['result']))

# 运行结果
函数 pathSum_base 的运行时间为 5516.24 ms;内存使用量为 4.00 KB 执行结果 = 15557

2) 改进版一【DFS递归+前缀和】

使用深度优先算法执行遍历,检测前缀和,然后返回有效数

页面功能测试,马马虎虎,超过58%

import CheckFuncPerf as cfp

class Solution:
 def pathSum_ext1(self, root, targetSum):
     def getCount(root, uppers, targetSum):
         result = 0
         if not root:
             return result
         if root.val == targetSum:
             result += 1
         if uppers:
             ipreupper = uppers[-1]
             imatch = ipreupper + root.val - targetSum
             if imatch == 0:
                 result += 1
             for iIdx in range(1, len(uppers)):
                 if uppers[-iIdx-1] == imatch:
                     result += 1
             uppers.append(ipreupper+root.val)
         else:
             uppers.append(root.val)
         result += getCount(root.left, uppers, targetSum)
         result += getCount(root.right, uppers, targetSum)
         uppers.pop()
         return result
     uppers = []
     return getCount(root, uppers, targetSum)

aSolution = Solution()
aroot = generate_binary_tree(ilen, imode)
result = cfp.getTimeMemoryStr(Solution.pathSum_ext1, aSolution, aroot, itargetSum)
print(result['msg'], '执行结果 = {}'.format(result['result']))

# 运行结果
函数 pathSum_ext1 的运行时间为 3008.68 ms;内存使用量为 0.00 KB 执行结果 = 15557

3) 改进版二【DFS递归+字典哈希】

使用深度优先算法执行遍历,将前缀和放入字典计数,检索返回有效数,效率提升了一个数量级

页面功能测试,天下无双,超越98%

import CheckFuncPerf as cfp

class Solution:
 def pathSum_ext2(self, root, targetSum):
     dict_preuppers = {}
     dict_preuppers[0] = 1
     def getCount(root, icursum, targetSum):
         result = 0
         if not root:
             return result
         icursum += root.val
         result += dict_preuppers.get(icursum-targetSum, 0)
         dict_preuppers[icursum] = dict_preuppers.get(icursum, 0) + 1
         result += getCount(root.left, icursum, targetSum)
         result += getCount(root.right, icursum, targetSum)
         dict_preuppers[icursum] -= 1
         return result
     return getCount(root, 0, targetSum)

aSolution = Solution()
aroot = generate_binary_tree(ilen, imode)
result = cfp.getTimeMemoryStr(Solution.pathSum_ext2, aSolution, aroot, itargetSum)
print(result['msg'], '执行结果 = {}'.format(result['result']))

# 运行结果
函数 pathSum_ext2 的运行时间为 546.13 ms;内存使用量为 180.00 KB 执行结果 = 15557

4) 改进版三【DFS递归+默认字典】

使用深度优先算法执行遍历,采用默认字典defaultdict,将前缀和放入defaultdict计数,检索返回有效数【减少字典的出错处理】

页面功能测试,性能优越,超越90%

import CheckFuncPerf as cfp

class Solution:
 def pathSum_ext3(self, root, targetSum):
     from collections import defaultdict
     dict_preuppers = defaultdict(int)
     dict_preuppers[0] = 1
     def getCount(root, icursum, targetSum):
         result = 0
         if not root:
             return result
         icursum += root.val
         result += dict_preuppers[icursum-targetSum]
         dict_preuppers[icursum] += 1
         result += getCount(root.left, icursum, targetSum)
         result += getCount(root.right, icursum, targetSum)
         dict_preuppers[icursum] -= 1
         return result
     return getCount(root, 0, targetSum)

aSolution = Solution()
aroot = generate_binary_tree(ilen, imode)
result = cfp.getTimeMemoryStr(Solution.pathSum_ext3, aSolution, aroot, itargetSum)
print(result['msg'], '执行结果 = {}'.format(result['result']))

# 运行结果
函数 pathSum_ext3 的运行时间为 446.10 ms;内存使用量为 212.00 KB 执行结果 = 15557

4. 最优算法

根据本地日志分析,最优算法为第4种方式【DFS递归+默认字典】pathSum_ext3

import random
ilen, imode, itargetSum = 1000000, 1, 1024
def generate_binary_tree(node_count, imode):
    if node_count <= 0:
        return None
    root = TreeNode(random.randint(1, 100))
    node_count -= 1
    if imode > 3:
        imode = 1
    if imode == 1:
        left = generate_binary_tree(node_count // 2, imode+1)
        right = generate_binary_tree(node_count // 2, imode+1)
        root.left = left
        root.right = right
    elif imode==2:
        left = generate_binary_tree(node_count, imode+1)
        root.left = left
    else:
        right = generate_binary_tree(node_count, imode+1)
        root.right = right
    return root
aSolution = Solution()
aroot = generate_binary_tree(ilen, imode)
result = cfp.getTimeMemoryStr(Solution.pathSum_base, aSolution, aroot, itargetSum)
print(result['msg'], '执行结果 = {}'.format(result['result']))
result = cfp.getTimeMemoryStr(Solution.pathSum_ext1, aSolution, aroot, itargetSum)
print(result['msg'], '执行结果 = {}'.format(result['result']))
result = cfp.getTimeMemoryStr(Solution.pathSum_ext2, aSolution, aroot, itargetSum)
print(result['msg'], '执行结果 = {}'.format(result['result']))
result = cfp.getTimeMemoryStr(Solution.pathSum_ext3, aSolution, aroot, itargetSum)
print(result['msg'], '执行结果 = {}'.format(result['result']))

# 算法本地速度实测比较
函数 pathSum_base 的运行时间为 5516.24 ms;内存使用量为 4.00 KB 执行结果 = 15557
函数 pathSum_ext1 的运行时间为 3008.68 ms;内存使用量为 0.00 KB 执行结果 = 15557
函数 pathSum_ext2 的运行时间为 546.13 ms;内存使用量为 180.00 KB 执行结果 = 15557
函数 pathSum_ext3 的运行时间为 446.10 ms;内存使用量为 212.00 KB 执行结果 = 15557

一日练,一日功,一日不练十日空

may the odds be ever in your favor ~

你可能感兴趣的:(Python,python,算法,leetcode,力扣,二叉树,路径总和)