let 113. Path Sum II

本文介绍了一种使用递归回溯算法解决寻找二叉树中所有和为特定值的路径的问题。通过深入剖析递归回溯的关键点,即何时进行回溯以及何时结束搜索,给出了解决该问题的AC代码实现。

主题思想: 递归回溯, 关键点在于什么时候选择回溯,什么时候应该结束。

当遇到null 时结束,则应该在遍历完一个节点的左右子树过后,再删除栈顶元素。

AC 代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int sum) {

        List<List<Integer>> ans=new ArrayList<List<Integer>>();
        if(root==null) return ans;

        findPath(root,new ArrayList<Integer>(), ans,0,sum);

        return ans;
    }

    public  void findPath(TreeNode root, List<Integer> tmpList,List<List<Integer>> ans,int tmp,int sum){

        if(root==null) return ;

        tmpList.add(root.val);
        if(root.left==null&&root.right==null){
            if(tmp+root.val==sum){
                ans.add(new ArrayList<Integer>(tmpList));
            }
        }

        findPath(root.left,tmpList,ans,tmp+root.val,sum);

        findPath(root.right,tmpList,ans,tmp+root.val,sum);
        //here is important
        tmpList.remove(tmpList.size()-1);
    }
}
// 初始化标记点(按sort排序,区分起点终点) function initRouteAndAnimation() { if (points.length < 2) { console.warn('至少需要2个点位生成路线'); if (points.length === 1) createPointMarker(points[0], 0, 1); return; } // 1. 排序并过滤有效点位(确保顺序正确) const sortedPoints = [...points] .filter(p => p.lng !== undefined && p.lat !== undefined) .sort((a, b) => (a.sort || 0) - (b.sort || 0)); // 按sort字段排序 const total = sortedPoints.length; // console.log('排序后点位(含途经点):', sortedPoints.map(p => p.title)); // 2. 绘制所有点位标记(起点、途经点、终点) sortedPoints.forEach((p, i) => createPointMarker(p, i, total, sortedPoints)); // 3. 按高德官方方法配置导航路线(含途经点) AMap.plugin('AMap.Driving', () => { // 初始化驾车导航实例 const driving = new AMap.Driving({ map: map, // 路线直接显示在地图上 panel: null, // 不显示导航面板(如需显示可指定DOM元素) policy: AMap.DrivingPolicy.LEAST_TIME, // 最快路线策略 strokeColor: "#32CD32", // 路线颜色 strokeWeight: 4 // 路线宽度 }); // 4. 定义起点、终点和途经点(严格遵循官方格式) const start = new AMap.LngLat(sortedPoints[0].lng, sortedPoints[0].lat); // 起点 const end = new AMap.LngLat(sortedPoints[total - 1].lng, sortedPoints[total - 1].lat); // 终点 const waypoints = []; // 途经点数组 // 提取中间点作为途经点(从第2个到倒数第2个) for (let i = 1; i < total - 1; i++) { waypoints.push(new AMap.LngLat(sortedPoints[i].lng, sortedPoints[i].lat)); } // console.log('官方方法途经点数量:', waypoints.length); // 5. 调用官方search方法规划路线(含途经点) // 官方格式:driving.search(起点, 终点, {waypoints: 途经点数组}, 回调) driving.search( start, end, { waypoints: waypoints }, // 官方要求的途经点配置格式 (status, result) => { // console.log('官方路线规划状态:', status); if (status === 'complete' && result.routes && result.routes.length > 0) { // 规划成功:使用官方返回的路线启动动画 // console.log('官方路线包含的步骤数:', result.routes[0].steps.length); initCarAnimation(result.routes[0]); } else { // 规划失败:降级为直线连接所有点 // console.error('官方路线规划失败,使用备用路线'); const backupPath = sortedPoints.map(p => new AMap.LngLat(p.lng, p.lat)); initCarAnimation({ distance: calculateDistance(backupPath.map(ll => [ll.lng, ll.lat])), steps: [{ path: backupPath }] }); } } ); }); } // 初始化行驶动画(严格沿官方路线移动) function initCarAnimation(route) { console.log('开始初始化行驶动画'); if (carMarker) carMarker.remove(); // 1. 提取原始路径点(来自高德路线规划) const path = []; try { route.steps.forEach(step => { if (step.path && step.path.length > 0) { step.path.forEach(coord => { const lnglat = coord instanceof AMap.LngLat ? coord : new AMap.LngLat(coord.lng, coord.lat); path.push(lnglat); }); } }); } catch (err) { console.error('提取路径点失败:', err); return; } if (path.length < 2) { console.error('路径点不足(至少需要2个),无法执行动画'); return; } // 2. 优化路径点插值(更密集,解决抖动) // 2. 优化路径点插值(严格控制总数,确保15秒匀速) const interpolatedPath = []; const maxTotalPoints = 300; // 最大总点数(平衡平滑与速度) const totalDistance = path.reduce((sum, p, i) => { if (i > 0) sum += path[i - 1].distance(p); return sum; }, 0); // 计算路线总长度(米) const pointsPerMeter = maxTotalPoints / totalDistance; // 每米点数(动态调整) for (let i = 0; i < path.length - 1; i++) { const p1 = path[i]; const p2 = path[i + 1]; const distance = p1.distance(p2); // 按总长度分配点数,避免过多 const segmentPoints = Math.max(2, Math.min(500, Math.ceil(distance * pointsPerMeter))); for (let s = 0; s < segmentPoints; s++) { const ratio = s / (segmentPoints - 1); const lng = p1.lng + (p2.lng - p1.lng) * ratio; const lat = p1.lat + (p2.lat - p1.lat) * ratio; interpolatedPath.push(new AMap.LngLat(lng, lat)); } } // 确保总点数不超过最大值(强制截断,避免速度异常) const finalPath = interpolatedPath .slice(0, maxTotalPoints) .filter(lnglat => { // 过滤经纬度无效的点(如NaN、超出合理范围) return !isNaN(lnglat.lng) && !isNaN(lnglat.lat) && lnglat.lng > -180 && lnglat.lng < 180 && lnglat.lat > -90 && lnglat.lat < 90; }); // 若过滤后路径点不足,补充默认路径(避免动画无法启动) if (finalPath.length < 2) { console.error('有效路径点不足,使用原始路径'); finalPath = path.slice(0, 2); // 至少保留2个点 } console.log(`路径点优化:总长度${Math.round(totalDistance)}米 → 最终${finalPath.length}个点(15秒走完)`); // 4. 配置行驶图标(确保正向无旋转) const carIcon = new AMap.Icon({ image: ICONS.car || './img/student.png', size: new AMap.Size(87, 96), // 与图片实际尺寸一致 imageSize: new AMap.Size(87, 96) }); // 5. 创建车辆标记(锚点精准对齐) carMarker = new AMap.Marker({ position: finalPath[0], icon: carIcon, offset: new AMap.Pixel(-44, -48), // 70/2=35,90/2=45(中心对齐) zIndex: 1000, map: map, rotation: 0 // 强制初始角度为0 }); // 6. 将startAnimation放入内部,确保访问所有变量 function startAnimation() { const duration = 15000; // 固定15秒 console.log(`动画启动:总时长${duration/1000}秒,路径点总数${finalPath.length}个`); if (typeof carMarker.moveAlong === 'function') { // 执行动画(禁用自动旋转) carMarker.moveAlong( finalPath, duration, { autoRotation: false }, (status) => { console.log(`动画状态:${status}`); if (status === 'complete') { // 反向播放时,直接使用finalPath的反向,无需依赖route参数 const reversedPath = [...finalPath].reverse(); // 简化参数,避免route.steps异常 initCarAnimation({ steps: [{ path: reversedPath }] // 仅传递必要的路径信息 }); } } ); } else { // 备选动画(无moveAlong时使用) console.warn('使用备选动画方案'); customMoveAnimation(finalPath, duration); } } // 7. 加载动画插件并启动 if (typeof AMap.MoveAnimation === 'undefined') { console.log('正在加载动画插件...'); AMap.plugin('AMap.MoveAnimation', () => { console.log('动画插件加载完成'); startAnimation(); }); } else { startAnimation(); } } // 自定义动画备选方案(当AMap.MoveAnimation不可用时) function customMoveAnimation(path, duration) { let frameIndex = 0; const totalFrames = path.length; const frameDuration = duration / totalFrames; function updatePosition() { if (frameIndex < totalFrames && carMarker) { // 强制设置当前路径点,无插值 carMarker.setPosition(path[frameIndex]); frameIndex++; // 用setTimeout确保时间精准,避免累积误差 setTimeout(updatePosition, frameDuration); } else if (carMarker) { const reversedPath = [...path].reverse(); initCarAnimation({ steps: [{ path: reversedPath }] }); } } updatePosition(); // 直接启动,不依赖requestAnimationFrame(减少延迟) }为什么设置了不随着高德路线路线偏移移动,还是生成的动画一直按照路线偏移移动,并且移动到一半卡住不动
最新发布
08-13
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值