40 Combination Sum II
Given a collection of candidate numbers (C) and a target number (T), find all unique combinations inCwhere the candidate numbers sums toT.
Each number inCmay only be usedoncein the combination.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
For example, given candidate set[10, 1, 2, 7, 6, 1, 5]
and target8
,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
These I thought this was a really great problem, I wrote about the details of my implementation in my other book Algorithms and Data Structures B.
struct Key
{
Key() {};
Key(const int val, const int iter, const int sum) :
val(val), iter(iter), sum(sum) {}
int val, iter, sum;
bool operator==(const Key &other) const
{
return (val == other.val
&& iter == other.iter
&& sum == other.sum);
}
};
namespace std {
template <>
struct hash<Key>
{
std::size_t operator()(const Key& k) const
{
return ((hash<int>()(k.val)
^ (hash<int>()(k.iter) << 1)) >> 1)
^ (hash<int>()(k.sum) << 1);
}
};
}
struct TreeNode2 {
TreeNode2(Key key) :
key(key) {}
TreeNode2() {};
Key key;
vector<TreeNode2 *> children;
};
class CombinationTree {
public:
explicit CombinationTree(vector<int> nums, int target) :
target(target) {
root = new TreeNode2();
sort(nums.begin(), nums.end());
elms = nums;
for (int i = 0; i < nums.size() && nums.at(i) <= target; i++) {
Key key = Key(nums.at(i), i, nums.at(i));
TreeNode2 *base_node = new TreeNode2(key);
// identifing another subpath within a prebuilt path
// but the idea is that if we put this path from the root
// the it will become part of the valid set when
// seaching all the root to leaf paths
if (dp_hash_map.find(key) != dp_hash_map.end()) {
root->children.push_back(dp_hash_map[key]);
continue; // subtree exist
}
dp_hash_map.insert(make_pair(key, base_node));
root->children.push_back(base_node);
base_node->children = build_combination_tree(base_node);
}
}
vector<vector<int>> root_leaf_paths(TreeNode2 *root, int target) {
// good idea to use static parameters here (single reference)
// just need to make sure to reset on deconstruction
vector<vector<int>> main;
vector<int> temp;
unordered_set<string> unique_paths;
string temp_str = "";
// ignore the root
for (TreeNode2 *n : root->children)
root_leaf_paths_rec(n, temp, main, target, unique_paths, temp_str);
return main;
}
static void print_level_order(TreeNode2 *root)
{
if (!root) return;
cout << "Value:Iterator:Accumulative Sum:Number Children" << endl << endl;
queue<TreeNode2*> q;
q.push(root);
q.push(nullptr);
while (!q.empty()) {
TreeNode2 *temp = q.front();
if (temp)
cout << temp->key.val << ":" <<
temp->key.iter << ":" << temp->key.sum << ":"
<< temp->children.size() << " ";
q.pop();
if (temp) {
for (TreeNode2 *t : temp->children)
if (t) q.push(t);
}
else {
if (!q.empty() && q.front()) {
q.push(nullptr);
cout << endl;
}
}
}
}
TreeNode2 *root;
private:
vector<int> elms;
unordered_map<Key, TreeNode2*> dp_hash_map;
int target;
vector<TreeNode2*> build_combination_tree(TreeNode2* cur_node)
{
// current node attributes
int cur_iter = cur_node->key.iter;
int cur_sum = cur_node->key.sum;
int cur_val = cur_node->key.val;
vector<TreeNode2*> next_children;
for (int i = cur_iter + 1; i < elms.size() && cur_sum < target; i++) {
// if the sub tree exists, then do not bother creating
// the same subtree for it
Key key = Key(elms.at(i), i, cur_sum + elms.at(i));
if (dp_hash_map.find(key) != dp_hash_map.end()) {
// point to the already developed subtree, and continue.
next_children.push_back(dp_hash_map[key]);
}
else {
TreeNode2* next_node = new TreeNode2(key);
dp_hash_map.insert(make_pair(key, next_node));
next_children.push_back(next_node);
next_node->children = build_combination_tree(next_node);
}
}
return next_children;
}
/*
* Performs traversal of combination tree
* to collect valid summations
*/
void root_leaf_paths_rec(TreeNode2 *root,
vector<int> &temp,
vector<vector<int>> &main, const int target,
unordered_set<string> &unique_paths,
string &temp_str) {
if (root) {
temp_str += to_string(root->key.val) + " ";
temp.push_back(root->key.val);
if (root->children.empty() && root->key.sum == target
&& unique_paths.find(temp_str) == unique_paths.end()) {
main.push_back(temp);
unique_paths.insert(temp_str);
temp.pop_back();
pop_back_last_num(temp_str);
return;
}
for (TreeNode2 *t : root->children) {
if (t) root_leaf_paths_rec(t, temp, main, target, unique_paths, temp_str);
}
}
temp.pop_back();
pop_back_last_num(temp_str);
}
void pop_back_last_num(string &complete_str)
{
int iter = complete_str.length() - 2; // ignore first space
while (iter >= 0 && complete_str.at(iter) != ' ')
iter--;
complete_str = complete_str.substr(0, max(0,iter + 1));
}
};
Some Testing:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target)
{
CombinationTree t = CombinationTree(candidates, target);
//t.print_level_order(t.root);
return t.root_leaf_paths(t.root, target);
}
int main(int argc, char **argv)
{
vector<int> test1 = { 10, 1, 2, 7, 6, 1, 5 };
print2d(combinationSum2(test1, 8));
vector<int> test2 = { 1,1,2 };
print2d(combinationSum2(test2, 3));
vector<int> test3 = { 1,-2,3,4,-5,6,7,-8,90,-23,45,67,89 };
print2d(combinationSum2(test3, 6));
}
Last updated