LeetCode 问题汇总之递归算法

参考文章:http://fisherlei.blogspot.com

最近在刷LeetCode,开始刷题--面试--刷题。。。

下面将遇到的可以用递归求解的问题归纳于此


1. Combination

Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.

For example,
If n = 4 and k = 2, a solution is:

[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

【思路】递归

1.用一临时数组solution存储元素, 其大小应为k

2 base case , solution长度为k,并弹出最后元素

3 else 继续向solution push元素

注意:需要引用传参

void getCombine(
                int n,
                int k,
                int level,
                vector<int> &solution,
                vector<vector<int>> &result){
    if(solution.size() == k){
        result.push_back(solution);
        return;
    }
    for(int i = level; i<=n; i++){
        solution.push_back(i);
        getCombine(n,k,i+1,solution,result);
        solution.pop_back();
    }
}
vector<vector<int> > combine(int n, int k) {
    vector<int> solution;
    vector<vector<int>> result;
    getCombine(n,k,1,solution,result);
    return result;
}



2. Combination Sum

Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

The same repeated number may be chosen from C unlimited number of times.

Note:
All numbers (including target) will be positive integers.
Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
The solution set must not contain duplicate combinations.
For example, given candidate set 2,3,6,7 and target 7,
A solution set is:
[7]
[2, 2, 3]

【思路】 递归, 类似于上一题

base case: solution中元素之和sum为target

if sum值大于target 直接返回,并弹出最后元素

else 从candidate numbers 中继续选出元素加到solution中,同时用sum记录当前和值

注意:

1.结果中元素应保持non-descending order, 应先排序

2. 结果可以包含重复元素,递归中直接传level值就可以了,和上一题的不同. 


void getCombineSum(
                   int &sum,
                   int level,
                   int target,
                   vector<int> &solution,
                   vector<int> &candidates,
                   vector<vector<int>> &result
                   ){
    if(sum > target) return;
    if(sum == target){
        result.push_back(solution);
        return;
    }
    for(int i = level; i<candidates.size(); i++){
        sum += candidates[i];
        solution.push_back(candidates[i]);
        getCombineSum(sum,i,target,solution,candidates,result);
        solution.pop_back();
        sum -= candidates[i];
    }

}
vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
    vector<int> solution;
    vector<vector<int>> result;
    int sum = 0;
    sort(candidates.begin(),candidates.end());
    getCombineSum(sum,0,target,solution,candidates,result);
    return result;
}

3.Combination Sum II

Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

Each number in C may only be used once in the combination.

Note:
All numbers (including target) will be positive integers.
Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
The solution set must not contain duplicate combinations.
For example, given candidate set 10,1,2,7,6,1,5 and target 8,
A solution set is:
[1, 7]
[1, 2, 5]
[2, 6]
[1, 1, 6]

【思路】 与上一题类似,不同之处在于不能重复添加candidated中的元素

void getCombineSum2(
                   int &sum,
                   int level,
                   int target,
                   vector<int> &solution,
                   vector<int> &num,
                   vector<vector<int>> &result
                   ){
    if(sum > target) return;
    if(sum == target) {
        result.push_back(solution);
        return;
    }
    for(int i=level; i<num.size(); i++){
        sum += num[i];
        solution.push_back(num[i]);
        getCombineSum2(sum,i+1,target,solution,num,result);
        sum -= num[i];
        solution.pop_back();
        while(i<num.size()-1 && num[i]== num[i+1]) i++;
    }
}
vector<vector<int> > combinationSum2(vector<int> &num, int target) {
    vector<int> solution;
    vector<vector<int>> result;
    int sum = 0;
    sort(num.begin(), num.end());
    getCombineSum2(sum,0,target,solution,num,result);
    return result;
}


注意防止这种情况,避免重复组合出现:

Input: [1,1], 1
Output: [[1],[1]]
Expected: [[1]]

4. Letter Combinations of a Phone Number

Given a digit string, return all possible letter combinations that the number could represent.

A mapping of digit to letters (just like on the telephone buttons) is given below.

Input:Digit string "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

思路:

递归,和前面几道combinations的题类似,只不过这里需要将数字转化成对应的字符。可以用一个字符数组实现 string []

void getLetterCombination(string &seq,
                          string &digits,
                          int level,
                          vector<string> &result,string trans[])
{
    if(level == digits.size()){
        result.push_back(seq);
        return;
    }
    int group = digits[level] - 48;
    for(int i=0; i<trans[group].size(); i++){
        seq.push_back(trans[group][i]);
        getLetterCombination(seq,digits,level+1,result,trans);
        seq.resize(seq.size()-1);
    }
}
vector<string> letterCombinations(string digits) {
    string trans[] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    vector<string> result;
    string seq;
    getLetterCombination(seq,digits,0,result,trans);
    return result;
}

注意:

特殊数字,0,1的对应字符为""

ACSII码转int要减去48.


5.Validate Binary Search Tree

Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:

  • The left subtree of a node contains only nodes with keys less than the node's key.
  • The right subtree of a node contains only nodes with keys greater than the node's key.
  • Both the left and right subtrees must also be binary search trees.

confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ.


OJ's Binary Tree Serialization:

The serialization of a binary tree follows a level order traversal, where '#' signifies a path terminator where no node exists below.

Here's an example:

   1
  / \
 2   3
    /
   4
    \
     5
The above binary tree is serialized as  "{1,2,3,#,#,4,#,#,5}".

思路

对于根节点,最大最小都不限制;
每一层节点,左子树限制最大值小于根,右子树最小值大于根;
但是比较有趣的是,在递归的过程中还得不断带入祖父节点的值。

bool judgeValidBST(TreeNode *root, int nMin, int nMax)
{
    if(root == NULL) return true;
    if(root->val <=nMax && root->val >=nMin
       && judgeValidBST(root->left,nMin,root->val)
       && judgeValidBST(root->right,root->val,nMax))
        return true;
    else
        return false;
}
bool isValidBST(TreeNode *root) {
    return judgeValidBST(root,INT_MIN, INT_MAX);
}


注意:

此题leetcode定义与算法导论里面定义的不同

这里要求左节点的值小于根节点的值,右节点值大于根节点值

相应的测试用例为

Input:{1,1}

Output:true

Expected:false

6. Subsets

Given a set of distinct integers, S, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,
If S = [1,2,3], a solution is:

[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
思路:递归逻辑

每次输入一个字符,

如果不是最后一个字符,调用递归处理下一个字符

void genSubset(
        vector<vector<int>> &result,
        vector<int> &solu,
        vector<int> &S,
        int level){
            for(int i=level; i<S.size(); i++){
                while(S[i] == S[i+1] && i<S.size()-1){
                    i++;
                }
            solu.push_back(S[i]);
            result.push_back(solu);
            if(i<S.size()-1){
                genSet(S,result,solu,i+1);
            }
            solu.pop_back();
            }
        }
    vector<vector<int> > subsetsWithDup(vector<int> &S) {
        vector<vector<int>> result;
        vector<int> solu;
        if(S.size() == 0) return result;
        result.push_back(solu);
        sort(S.begin(), S.end());
        genSubset(result, solu, S, 0);
        return result;
    }

7. Subsets II

Given a collection of integers that might contain duplicates, S, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,
If S = [1,2,2], a solution is:

[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

思路:和上题类似,需要在弹出字符的地方判断下一个字符是否重复,以免重复加入

void genSubset(
        vector<vector<int>> &result,
        vector<int> &solu,
        vector<int> &S,
        int level){
            for(int i=level; i<S.size(); i++){
                
            solu.push_back(S[i]);
            result.push_back(solu);
            if(i<S.size()-1){
                genSubset(result,solu,S,i+1);
            }
            solu.pop_back();
            while(S[i] == S[i+1] && i<S.size()-1){
                    i++;
                }
            }
        }
    vector<vector<int> > subsetsWithDup(vector<int> &S) {
        vector<vector<int>> result;
        vector<int> solu;
        if(S.size() == 0) return result;
        result.push_back(solu);
        sort(S.begin(), S.end());
        genSubset(result, solu, S, 0);
        return result;
    }

8.  Generate Parentheses

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

For example, given n = 3, a solution set is:

"((()))", "(()())", "(())()", "()(())", "()()()"

典型的递归。一步步构造字符串。当左括号出现次数<n时,就可以放置新的左括号。当右括号出现次数小于左括号出现次数时,就可以放置新的右括号。

void CombinationPar(vector<string>& result, string& sample, int deep,   
                           int n, int leftNum, int rightNum)  
  {  
       if(deep == 2*n)  
       {  
           result.push_back(sample);  
           return;         }  
       if(leftNum<n)  
       {  
          sample.push_back('(');  
            CombinationPar(result, sample, deep+1, n, leftNum+1, rightNum);  
            sample.resize(sample.size()-1);  
       }  
       if(rightNum<leftNum)  
       {   
            sample.push_back(')');  
            CombinationPar(result, sample, deep+1, n, leftNum, rightNum+1);  
            sample.resize(sample.size()-1);  
       }  
  }  
  vector<string> generateParenthesis(int n) {  
       // Start typing your C/C++ solution below  
       // DO NOT write int main() function  
       vector<string> result;  
       string sample;  
       if(n!= 0)  
            CombinationPar(result, sample, 0, n, 0, 0);  
       return result;  
  }


9.Permutations

Given a collection of numbers, return all possible permutations.

For example,
[1,2,3] have the following permutations:
[1,2,3][1,3,2][2,1,3][2,3,1][3,1,2], and [3,2,1].

递归,用标志位记录已使用数字。与combination中的思路一致

void GeneratePermute(vector<vector<int>> &result,
                     vector<int> &solu,
                     int level,
                     vector<int> &visited,
                     vector<int> &num
                     ){
    if(level == num.size()){
        result.push_back(solu);
        return;
    }
    for(int i=0;i<num.size();i++){
        if(visited[i]==0){
            visited[i]=1;
            solu.push_back(num[i]);
            GeneratePermute(result,solu,level+1,visited,num);
            solu.pop_back();
            visited[i]=0;
        }
    }
}
vector<vector<int> > permute(vector<int> &num) {
    vector<vector<int>> result;
    vector<int> solution;
    vector<int> visited(num.size(),0);
    if(num.size() == 0) return result;
    GeneratePermute(result,solution,0,visited,num);
    return result;
}

10.Permutations II

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

For example,
[1,1,2] have the following unique permutations:
[1,1,2][1,2,1], and [2,1,1].

和上题的思路一致,这里要加一个去重复的条件判断。并且需要先对数组进行排序,只有前面重复的数字使用的时候才能用

void GeneratePermuteUni(vector<vector<int>> &result,
                     vector<int> &solu,
                     int level,
                     vector<int> &visited,
                     vector<int> &num
                     ){
    if(level == num.size()){
        result.push_back(solu);
        return;
    }
    for(int i=0;i<num.size();i++){
        if(visited[i]==0){
            if(i>0 && num[i]==num[i-1] && visited[i-1]==0)
                continue;
            visited[i]=1;
            solu.push_back(num[i]);
            GeneratePermuteUni(result,solu,level+1,visited,num);
            solu.pop_back();
            visited[i]=0;
        }
    }
}
vector<vector<int> > permuteUnique(vector<int> &num) {
    vector<vector<int>> result;
    vector<int> solution;
    vector<int> visited(num.size(),0);
    if(num.size() == 0) return result;
    sort(num.begin(), num.end());
    GeneratePermuteUni(result,solution,0,visited,num);
    return result;
}


11. Permutation Sequence


The set [1,2,3,…,n] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3):

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

Given n and k, return the kth permutation sequence.

Note: Given n will be between 1 and 9 inclusive.

方法一采用 nextPermutation 中的方法,找到kth个string

这个方法在Leetcode上大数据不能通过,但是本机测试是没有问题的。


void getNextPermutationSeq(vector<int> &num,
                           int k,
                           int level
                           ){
    while(level<k){
    int vioIndex = num.size()-1;
    while(vioIndex >0){
        if(num[vioIndex]>num[vioIndex-1])
            break;
        vioIndex--;
    }
    if(vioIndex>0){
        vioIndex--;
        int rightIndex = num.size()-1;
        while(rightIndex>=0 && num[rightIndex]<=num[vioIndex]){
            rightIndex--;
        }
        int swap = num[vioIndex];
        num[vioIndex] = num[rightIndex];
        num[rightIndex] = swap;
        vioIndex++;
    }
    int end = num.size()-1;
    while(end>vioIndex){
        int swap = num[vioIndex];
        num[vioIndex] = num[end];
        num[end] = swap;
        vioIndex++;
        end--;
    }
    level++;
    }
}
string getPermutation(int n, int k) {
    string result;
    string solu;
    vector<int> visited;
    for(int i=1;i<=n;i++){
        visited.push_back(i);
    }
    getNextPermutationSeq(visited,k,1);
    for(int i=0;i<n ;i++){
        result.push_back(visited[i]+'0');
    }
    return result;
}


方法二 数学方法。google之


12.

Regular Expression Matching

 

Implement regular expression matching with support for '.' and '*'.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

Solution:
This looks just like a straight forward string matching, isn’t it? Couldn’t we just match the pattern and the input string character by character? The question is, how to match a ‘*’?

A natural way is to use a greedy approach; that is, we attempt to match the previous character as many as we can. Does this work? Let us look at some examples.

s = “abbbc”, p = “ab*c”
Assume we have matched the first ‘a’ on both s and p. When we see “b*” in p, we skip all b’s in s. Since the last ‘c’ matches on both side, they both match.

s = “ac”, p = “ab*c”
After the first ‘a’, we see that there is no b’s to skip for “b*”. We match the last ‘c’ on both side and conclude that they both match.

It seems that being greedy is good. But how about this case?

s = “abbc”, p = “ab*bbc”
When we see “b*” in p, we would have skip all b’s in s. They both should match, but we have no more b’s to match. Therefore, the greedy approach fails in the above case.

One might be tempted to think of a quick workaround. How about counting the number of consecutive b’s in s? If it is smaller or equal to the number of consecutive b’s after “b*” in p, we conclude they both match and continue from there. For the opposite, we conclude there is not a match.

This seem to solve the above problem, but how about this case:
s = “abcbcd”, p = “a.*c.*d”

Here, “.*” in p means repeat ‘.’ 0 or more times. Since ‘.’ can match any character, it is not clear how many times ‘.’ should be repeated. Should the ‘c’ in p matches the first or second ‘c’ in s? Unfortunately, there is no way to tell without using some kind of exhaustive search.

We need some kind of backtracking mechanism such that when a matching fails, we return to the last successful matching state and attempt to match more characters in s with ‘*’. This approach leads naturally to recursion.

The recursion mainly breaks down elegantly to the following two cases:

  1. If the next character of p is NOT ‘*’, then it must match the current character of s. Continue pattern matching with the next character of both s and p.
  2. If the next character of p is ‘*’, then we do a brute force exhaustive matching of 0, 1, or more repeats of current character of p… Until we could not match any more characters.
bool regurMatch(const char *s, const char *p){
   // assert(s && p);
    //base case
    if(*p=='\0') return *s =='\0';
    //next char is not '*':must match current character
    if(*(p+1)!='*'){
        //assert(*p!='*');
        return ((*p==*s)||(*p=='.'&& *s!='\0')) && regurMatch(s+1,p+1);
    }
    //next char is '*'
    while((*p==*s)||(*p=='.' && *s!='\0')){
        if(regurMatch(s,p+2)) return true;
        s++;
    }
    return regurMatch(s,p+2);
}

13. Word search


Given a 2D board and a word, find if the word exists in the grid.

The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

For example,
Given board =

[
  ["ABCE"],
  ["SFCS"],
  ["ADEE"]
]
word  =  "ABCCED" , -> returns  true ,
word  =  "SEE" , -> returns  true ,
word  =  "ABCB" , -> returns  false .

参考http://leetcode.com/2011/09/regular-expression-matching.html

递归,先找到第一个字母位置,然后四个方向递归。同时用一个数组记录已访问位置

bool searchWord(vector<vector<char>> &board,
            string word,
            int i,
            int j,
            int matchLen,
            int op,// 0 up, 1 down, 2 left, 3 right
            vector<vector<int>> &visited){
    if(matchLen == word.size()) return true;
    int row = board.size();
    int col = board[0].size();
    if(i+1<row && op!=0){
        if(visited[i+1][j]==0 && board[i+1][j]==word[matchLen]){
            visited[i+1][j] = 1;
            if(searchWord(board,word,i+1,j,matchLen+1,1,visited))
                return true;
            visited[i+1][j] = 0;
        }
    }
    if(i-1>=0 && op!=1){
        if(visited[i-1][j]==0 && board[i-1][j]==word[matchLen]){
            visited[i-1][j] = 1;
            if(searchWord(board,word,i-1,j,matchLen+1,0,visited))
                return true;
            visited[i-1][j] = 0;
        }
    }
    if(j+1<col && op!=2){
        if(visited[i][j+1]==0 && board[i][j+1]==word[matchLen]){
            visited[i][j+1] = 1;
            if(searchWord(board,word,i,j+1,matchLen+1,3,visited))
                return true;
            visited[i][j+1] = 0;
        }
    }
    if(j-1>=0 && op!=3){
        if(visited[i][j-1]==0 && board[i][j-1]==word[matchLen]){
            visited[i][j-1] = 1;
            if(searchWord(board,word,i,j-1,matchLen+1,2,visited))
                return true;
            visited[i][j-1] = 0;
        }
    }
    return false;
}
bool exist(vector<vector<char> > &board, string word) {
    int row = board.size();
    int col = board[0].size();
    vector<vector<int>> visited(row,vector<int>(col,0));
    for(int i=0;i<row;i++){
        for(int j=0;j<col;j++){
            if(board[i][j] == word[0])
                if(word.size()==1) return true;
                else{
                    visited[i][j] = 1;
                    if(searchWord(board,word,i,j,1,-1,visited))
                        return true;
                    visited[i][j] = 0;
                }
        }
    }
    return false;
}



未完...待续...

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值