10. Depth First Search(with example problems)

0. Basic Concepts

0.1 When to use DFS?

When a problem need you to return all possible solutions, which can be permutation or combination, it is a must to use DFS.

Time Complexity: O( # of solutions * cost time of each solution)

0.2 Recall of Recursion?

In most cases, dfs can be implemented by Recursion. Three component of the recursion would be:

  1. Definition
  2. Decomposition
  3. Out-port

1. Problem Type:

1.1 Combination Problem

Problem will Ask: return all the combination that satisfy the conditions that …
Detection: The elements in the combination is not ordered.
Time complexity: O(2^n)

1.1.1 Examples:

135. Combination Sum

Main idea: sort the candidates, remove the duplicates, then dfs the remained elements.

Tips

  1. Pay attention to the way to remove the duplicates
  2. In DFS function, we usually will set the combination as one of parameters.
class Solution {
public:
    vector<vector<int>> result;
    /**
     * @param candidates: A list of integers
     * @param target: An integer
     * @return: A list of lists of integers
     */
    vector<vector<int>> combinationSum(vector<int> &candidates, int target) {
        // write your code here
        
        if(candidates.empty())
            return result;
            
        // remove the dulpicates
        sort(candidates.begin(), candidates.end());
        int left = 0, right = candidates.size();
        for(int i = 1; i < right; i++)
        {
            if(candidates[i] != candidates[left]){
                left++;
                candidates[left] = candidates[i];
            }
        }
        candidates.resize(left +1);
        
        vector<int> combination;
        backTrace(candidates, target, combination, 0);
        
        return result;
    }
    
    void backTrace(vector<int> &candidates, 
                    int remainTarget, 
                    vector<int> & combination, 
                    int startIndex)
    {
        // corner case
        if(remainTarget < 0)
            return;
            
        if(remainTarget == 0)
        {
            result.push_back(combination);
            return;
        }
        
        for(int i = startIndex; i < candidates.size(); i++)
        {
            combination.push_back(candidates[i]);
            // it could have duplicate elements, thus the startIndex is i.
            backTrace(candidates, remainTarget - candidates[i], combination, i);
            combination.pop_back();
        }
    }
};

153. Combination Sum II

In this backTrace/DFS function, we could have duplicate element in a combination but we need to remove the duplicate combination in result, but how to do so?

Pay attention to the method how to remove the duplicate here
We could write a condition to detect:

	for(int i = startIndex; i < num.size(); i++)
        {
            if(i != startIndex && num[i] == num[i -1])
                continue;
            
            ...
        }
class Solution {
public:
    vector<vector<int>> result;
    /**
     * @param num: Given the candidate numbers
     * @param target: Given the target number
     * @return: All the combinations that sum to target
     */
    vector<vector<int>> combinationSum2(vector<int> &num, int target) {
        // write your code here
        
        sort(num.begin(), num.end());
        
        vector<int> combination;
        
        backTrace(num, target, combination, 0);
        
        return result;
    }
    
    void backTrace(vector<int> &num, int remainTarget, 
                    vector<int> & combination, int startIndex)
    {
        if(remainTarget < 0)
            return;
            
        if(remainTarget == 0)
        {
            result.push_back(combination);
            return;
        }
        
        for(int i = startIndex; i < num.size(); i++)
        {
            if(i != startIndex && num[i] == num[i -1])
                continue;
            
            combination.push_back(num[i]);
            backTrace(num, remainTarget - num[i], combination, i +1);
            combination.pop_back();
        }
        
    }
};

136. Palindrome Partitioning

Write a function to detect whether the string is Palindrome, and a function for back trace.

class Solution {
public:
    vector<vector<string>> result;
    /*
     * @param s: A string
     * @return: A list of lists of string
     */
    vector<vector<string>> partition(string &s) {
        // write your code here
        
        vector<string> subset;
        subset.clear();
        
        helper(s, subset);

        return result;
    }
    
    void helper(const string& remainStr, vector<string>& subset)
    {
        int len = remainStr.length();
        if( len == 0)
        {
            result.push_back(subset);
            return;
        }
        
        for(int i = 0; i < len; i++)
        {
            string tmp = remainStr.substr(0, i+1);
            if(isPalindrome(tmp))
            {
                subset.push_back(tmp);
                helper(remainStr.substr(i+1), subset);
                subset.pop_back();
            }
        }
    }
    
    
    
    bool isPalindrome(string &s){
        int left = 0, right = s.size() -1;
        while(left < right)
        {
            if(s[left] != s[right]){
                return false;
            }
            left++;
            right --;
        }
        return true;
    }
};

1.2 Permutation Problem

Problem will Ask: return all the permutation that satisfy the conditions that …
Detection: The elements in the combination is order-related.
Time complexity: O(n!)

1.2.1 Examples:

15. Permutations

class Solution {
public:
    /*
     * @param nums: A list of integers.
     * @return: A list of permutations.
     */
    vector<vector<int>> permute(vector<int> &nums) {
        // write your code here
        vector<vector<int>> result;
        if(nums.empty())
        {
            result.push_back(vector<int>());
            return result;
        }

        helper(nums, result, nums.size() -1);
        return result;
    }
    
    void helper(vector<int> &nums, vector<vector<int>> &result, int n)
    {
        if(n == 0)
            result.push_back(nums);
            
        for(int i = 0; i <= n; i++){
            swap(nums[i], nums[n]);
            helper(nums, result, n -1);
            swap(nums[i], nums[n]);
        }
    }
};

16. Permutations II

Pay attention to the method how to remove the duplicate here

class Solution {
public:
    vector<vector<int>> result;
    /*
     * @param :  A list of integers
     * @return: A list of unique permutations
     */
    vector<vector<int>> permuteUnique(vector<int> &nums) {
        // write your code here
        vector<int> permutation;
        vector<bool> used(nums.size(), false);
        
        sort(nums.begin(), nums.end());
        
        helper(nums, permutation, used);
        
        return result;
    }
    
    void helper(vector<int> &nums, vector<int> &permutation, vector<bool> & used)
    {
        if(nums.size() == permutation.size())
        {
            result.push_back(permutation);
            return;
        }
        
        for(int i = 0; i < nums.size(); i++){
            if(used[i])
                continue;
            if(i > 0 && used[i-1] == false && nums[i] == nums[i-1])
                continue;
                
            used[i] = true;
            permutation.push_back(nums[i]);
            helper(nums, permutation, used);
            permutation.pop_back();
            used[i] = false;
        }
    }
};

33. N-Queens

In N-Queens problem, queens are not allow to in the same row/column/Diagonal. Thus, besides the basic permutation, we also need to write a function to validate the result.

class Solution {
public:
    vector<vector<string>> result;
    /*
     * @param n: The number of queens
     * @return: All distinct solutions
     */
    vector<vector<string>> solveNQueens(int n) {
        // write your code here
        if(n == 0){
            result.push_back(vector<string>());
            return result;
        }
        
        vector<string> permutation;
        vector<bool> visited(n, false);
        dfs(permutation, visited, n);
        
        return result;
        
        
    }
    void dfs(vector<string>& permutation, vector<bool>& visited, int n){
        if(! isValid(permutation))
            return;
        
        if(n == 0){
            result.push_back(permutation);
            return;
        }
        
        string tmp = "";
        for(int i = 0; i < visited.size(); i++)
            tmp += ".";
        
        for(int i = 0; i < visited.size(); i++){
            if(!visited[i])
            {
                visited[i] = true;
                tmp[i] = 'Q';
                permutation.push_back(tmp);
                
                dfs(permutation, visited, n -1);
                tmp[i] = '.';
                permutation.pop_back();
                visited[i] = false;
            }
        }
        
    }
    
    bool isValid(vector<string>& permutation)
    {
        int n = permutation.size();
        if(n == 0)
            return true;
        int m = permutation[0].length();
        vector<int> pos;
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < m; j++){
                if(permutation[i][j] == 'Q'){
                    pos.push_back(j);
                    continue;
                }
            }
        }
        
        for(int i = 0; i < pos.size(); i++){
            for(int j = i+1; j < pos.size(); j++){
                if(abs(i - j) == abs(pos[i] - pos[j]))
                    return false;
            }
        }
        return true;
    }
};

1.3 Serach in a Graph

1.3.1 Examples:

120. Word Ladder

int ladderLength(string &start, string &end, unordered_set<string> &dict) {
        // write your code here
        unordered_set<string>starts{start},ends{end};
        auto grow=[&dict](unordered_set<string>& starts,unordered_set<string>& ends,int& times)->bool{
            ++times;
            unordered_set<string> ns;
            for(auto s:starts)
                for(auto&c:s){
                    auto origi=c;
                    for(c='a';c<='z';++c)
                        if(c!=origi)
                            if(ends.count(s))
                                return false;
                            else if(dict.count(s)){
                                ns.insert(s);
                                dict.erase(s);
                            }
                    c=origi;
                }
            starts=ns;
            return true;
        };
        auto i=1;
        while(grow(starts,ends,i)&&grow(ends,starts,i));
        return i;
    }

1.3 Stack: Non-Recursion

66. Binary Tree Preorder Traversal

vector<int> preorderTraversal(TreeNode * root) {
        // write your code here
        
        vector<int> result;
        
        if(!root)
            return result;
            
        stack<TreeNode* > stk;
        stk.push(root);
        
        while(! stk.empty() )
        {
            auto tmp = stk.top(); stk.pop();
            result.push_back(tmp->val);
            
            if(tmp ->right)
                stk.push(tmp ->right);
                
            if(tmp ->left)
                stk.push(tmp ->left);
        }
        
        return result;
    }

67. Binary Tree Inorder Traversal

public ArrayList<Integer> inorderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        ArrayList<Integer> result = new ArrayList<>();
        
        while (root != null) {
            stack.push(root);
            root = root.left;
        }
    
        while (!stack.isEmpty()) {
            TreeNode node = stack.peek();
            result.add(node.val);
            
            if (node.right == null) {
                node = stack.pop();
                while (!stack.isEmpty() && stack.peek().right == node) {
                    node = stack.pop();
                }
            } else {
                node = node.right;
                while (node != null) {
                    stack.push(node);
                    node = node.left;
                }
            }
        }
        return result;
    }

68. Binary Tree Postorder Traversal

public ArrayList<Integer> postorderTraversal(TreeNode root) {
    ArrayList<Integer> result = new ArrayList<Integer>();
    Stack<TreeNode> stack = new Stack<TreeNode>();
    TreeNode prev = null; // previously traversed node
    TreeNode curr = root;

    if (root == null) {
        return result;
    }

    stack.push(root);
    while (!stack.empty()) {
        curr = stack.peek();
        if (prev == null || prev.left == curr || prev.right == curr) { // traverse down the tree
            if (curr.left != null) {
                stack.push(curr.left);
            } else if (curr.right != null) {
                stack.push(curr.right);
            }
        } else if (curr.left == prev) { // traverse up the tree from the left
            if (curr.right != null) {
                stack.push(curr.right);
            }
        } else { // traverse up the tree from the right
            result.add(curr.val);
            stack.pop();
        }
        prev = curr;
    }

    return result;
}

17. Subsets

public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        int n = nums.length;
        Arrays.sort(nums);
        
        // 1 << n is 2^n
        // each subset equals to an binary integer between 0 .. 2^n - 1
        // 0 -> 000 -> []
        // 1 -> 001 -> [1]
        // 2 -> 010 -> [2]
        // ..
        // 7 -> 111 -> [1,2,3]
        for (int i = 0; i < (1 << n); i++) {
            List<Integer> subset = new ArrayList<Integer>();
            for (int j = 0; j < n; j++) {
                // check whether the jth digit in i's binary representation is 1
                if ((i & (1 << j)) != 0) {
                    subset.add(nums[j]);
                }
            }
            result.add(subset);
        }
        return result;
    }

15. Permutations

vector<vector<int> > permute(vector<int> nums) {
        vector<vector<int> > permutations;
        if (nums.size() == 0) {
            permutations.push_back(vector<int>());
            return permutations;
        }
        
        int n = nums.size();
        vector<int> stack;
        bool inStack[n];
        for (int i = 0; i < n; i++) {
            inStack[i] = false;
        }
        
        stack.push_back(-1);
        while (stack.size() != 0) {
            // pop the last 
            int last = stack[stack.size() - 1];
            stack.pop_back();
            if (last != -1) {
                inStack[last] = false;
            }
            
            // increase the last, find the next bigger & avaiable number
            int next = -1;
            for (int i = last + 1; i < n; i++) {
                if (inStack[i] == false) {
                    next = i;
                    break;
                }
            }
            if (next == -1) {
                continue;
            }
            
            // generate the next permutation
            stack.push_back(next);
            inStack[next] = true;
            for (int i = 0; i < n; i++) {
                if (!inStack[i]) {
                    stack.push_back(i);
                    inStack[i] = true;
                }
            }
            
            // generate real permutation from index
            vector<int> permutation;
            for (int i = 0; i < n; i++) {
                permutation.push_back(nums[stack[i]]);
            }
            permutations.push_back(permutation);
        }
        
        return permutations;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值