• [leetcode 40. 组合总和 II] 不排序使用哈希表+dfs递归 vs 排序栈+回溯


    题目描述

    给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

    candidates 中的每个数字在每个组合中只能使用一次。

    说明:

    • 所有数字(包括目标数)都是正整数。
    • 解集不能包含重复的组合。

    示例 1:

    输入: candidates = [10,1,2,7,6,1,5], target = 8,
    所求解集为:
    [
      [1, 7],
      [1, 2, 5],
      [2, 6],
      [1, 1, 6]
    ]
    

    示例 2:

    输入: candidates = [2,5,2,1,2], target = 5,
    所求解集为:
    [
      [1,2,2],
      [5]
    ]
    

    方法一 不排序使用哈希表+dfs递归

    • 建立哈希表,统计每个数字出现次数
    • dfs递归剪枝,target减去当前数字,哈希次数减一,进入下一层
    • target==0,找到目标组合,target<0退出
    var combinationSum2 = function(candidates, target) {
        let _hash = {}, ret = [];
        // build hash map
        for (let n of candidates) {
            _hash[n] = typeof _hash[n]=='undefined' ? 1 : _hash[n]+1;
        }
        function dfs(hash, target, solution) {
            if (target == 0) {
                // get it
                ret.push(Object.assign([], solution));
                return true;
            }
            if (target < 0) {
                return false;
            }
            for (let k in hash) {
                if (hash[k] == 0) {continue;}
                solution.push(k);
                hash[k]--;
                dfs(Object.assign({}, hash), target - k, solution);
                // 避免重复,非常关键
                delete hash[k];
                solution.pop();
            }
        }
        dfs(_hash, target, []);
        return ret;
    }
    

    ac之后意外的用时击败了5%的用户,内存击败5%用户,大概这应该算是一种暴力求解,所以速度这么慢?

    方法二 排序栈+回溯

    这是我第一次ac的方法,也是从我39题的基础上直接修改的
    基本思路没有区别,只是在一些边界上稍作变化:

    var combinationSum2 = function(candidates, target) {
        // sort + stack
        candidates.sort();
        let ret = [],stack = [], i = candidates.length-1;
        while (i>=0 || stack.length>0) {
            if (target == 0) {
                // resolved
                let solution = [];
                for (var k in stack) {
                    solution.push(candidates[stack[k]]);
                }
                ret.push(solution);
                let pre = stack.pop();
                target += candidates[pre];
                // 区别1,找到题解,跳过相同数字,避免重复答案
                while (candidates[i] == candidates[pre]){i--;};
                continue;
            } else if (i < 0) {
                pre = stack.pop();
                target+=candidates[pre];
                i = pre - 1;
                // 区别2,索引溢出,本次寻找失败,跳过相同的数字
                while (i>0 && candidates[i] == candidates[pre]) {i--;}
                continue;
            } else if (target >= candidates[i]) {
                // 区别3,入栈之后索引需要移位
                stack.push(i);
                target -= candidates[i];
            }
            i--;
        }
        //
        return ret;
    };
    
  • 相关阅读:
    图片验证码, 登录, 注销, 修改密码
    注册页面及注册功能实现
    高级配置文件, csrf, django settings源码, django auth模块, 文件配置的插拔式设计
    cookie操作, session操作, django中间件
    半自动创建多对多关系表, forms组件
    sweetalert, bulk_create, 分页器
    orm查询优化, MVC与MTV, choices参数, ajax
    聚合查询, 分组查询, F与Q查询, 常见字段及参数, 自定义Char字段, 事务操作
    Contest2058
    ACM版《孔乙己》
  • 原文地址:https://www.cnblogs.com/dapianzi/p/12704549.html
Copyright © 2020-2023  润新知