力扣刷题记录(8)LeetCode:40、131、93

40. 组合总和 II

力扣刷题记录(8)LeetCode:40、131、93_第1张图片

这道题跟上篇文章的39题差不多,就是要多解决一个问题——去重。如果是将所有答案都存储在容器中之后再去重,那就比较耗时。这里要想办法在遍历的过程中去重。可以将遍历的过程看作是在遍历一棵树,当确立了根节点,其余可以取的值就都是该根节点的孩子结点。去重的操作就是保证同层的结点的值不能重复,但是在保证每个元素只取一次的前提下同一个路径(从根节点到某一叶子结点)的元素是可以重复的。

力扣刷题记录(8)LeetCode:40、131、93_第2张图片

同层重复的元素不可取,同枝重复的元素可取。

!!!【错误的代码】

可能好多人刚开始会这样写,我也是


        for(int i=startIndex;i0 && candidates[i]==candidates[i-1])  continue;
            //放进路径中
            path.push_back(candidates[i]);
            sum+=candidates[i];
            //在下一层寻找值
            backtracking(candidates,target,i+1);
            //返回上一层前,弹出当前值
            sum-=candidates[i];
            path.pop_back();
    
        }

这样写无法判断当前元素与上一个元素是同层还是同枝,会把同层同枝中相同的元素全部舍弃,会遗漏部分组合。 

 

那么现在的问题就是如果当前取的元素和上一个元素是重复的,该如何判断该元素与上一个元素是同层还是同枝呢?

第一种方法是创建一个bool类型的数组,当取了第i个值时,就将第i个值设为true。如果前一个值也为true,说明它被上一层取了,那么当前值与前一个值同枝,即使重复也没有关系。如果前一个值为false,那说明上一层没取它,那么当前值与前一个值一定同层。这点不好理解,我们可以看看上图,如果我第一个值取1,第二个值取了2,这时2的前一个值1对应的是false,1和2同层。

public:
    vector path;
    vector> ans;
    int sum=0;
    void backtracking(vector candidates,int target,int startIndex,vector used)
    {
        
        if(sum==target) 
        {
            ans.push_back(path);
            return;
        }
        //这里多加了个条件sum+candidates[i]<=target,其实也可以这里不写,在上面if前加if(sum>target) return
        //但是在这里加条件效率更高,就不需要存取i的值,不需要递归到下一层
        for(int i=startIndex;i0 && candidates[i]==candidates[i-1])
            {
                if(used[i-1]==false)    continue;
            }
            path.push_back(candidates[i]);
            sum+=candidates[i];
            used[i]=true;
            backtracking(candidates,target,i+1,used);
            sum-=candidates[i];
            path.pop_back();
            used[i]=false;
            
        }
    }
    vector> combinationSum2(vector& candidates, int target) {
        vector used(candidates.size(),false);
        sort(candidates.begin(),candidates.end());
        backtracking(candidates,target,0,used);
        return ans;
    }
};

第二种方法是通过startIndex来判断, 

 力扣刷题记录(8)LeetCode:40、131、93_第3张图片

如果当前i的值大于startIndex说明当前值一定与前一个值同层。通过画图就很容易理解了。

所有只需要当i>startIndex时,判断i与前一个值是否相等,如果相等就舍弃当前值,直接遍历下一个值。

class Solution {
public:
    vector path;
    vector> ans;
    int sum=0;
    void backtracking(vector candidates,int target,int startIndex)
    {
        if(sum==target) 
        {
            ans.push_back(path);
            return;
        }
        //这里多加了个条件sum+candidates[i]<=target,其实也可以这里不写,在上面if前加if(sum>target) return
        //但是在这里加条件效率更高,就不需要存取i的值,不需要递归到下一层
        for(int i=startIndex;istartIndex && candidates[i]==candidates[i-1])  continue;
            //放进路径中
            path.push_back(candidates[i]);
            sum+=candidates[i];
            //在下一层寻找值
            backtracking(candidates,target,i+1);
            //返回上一层前,弹出当前值
            sum-=candidates[i];
            path.pop_back();
    
        }
    }
    vector> combinationSum2(vector& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        backtracking(candidates,target,0);
        return ans;
    }
};

131. 分割回文串 

力扣刷题记录(8)LeetCode:40、131、93_第4张图片 

 做这道题的时候不要把问题想的那么复杂,其实这道题只需要解决应该在哪个地方对字符串进行切割,这需要理清楚切割的逻辑。

切割逻辑:从字符串起始位置开始切割,如果切割出的子串是回文串就将它放入路径中,如果切割出的子串不是回文串,我们就循环到下一个位置继续切割,直到找出回文子串将它放到路径中。

接下来就是在剩余的字符串中继续从起始位置切割,切割出回文子串,然后不断递归。

!!!重点是寻找切割点

力扣刷题记录(8)LeetCode:40、131、93_第5张图片 

class Solution {
public:
    vector path;
    vector> ans;
    //判断是否为回文串
    bool ispal(string s)
    {
        if(s.size()==0) return false;
        int left=0,right=s.size()-1;
        while(left> partition(string s) {
        backtracking(s);

        return ans;
    }
};

93. 复原 IP 地址 

 力扣刷题记录(8)LeetCode:40、131、93_第6张图片

这道题和上一题差不多,需要解决的问题就是寻找切割点,判断切割出来的数字是否有效。

还是横向遍历每个切割点,对每个切割点加一判断,符合条件就加入path中。不符合就继续循环下一个切割点。递归+回溯。

结束条件:path中有四个整数,且s中的字符串已经被切割完。

class Solution {
public:
    vector path;
    vector ans;
    //判断字符串是否有效
    bool isEffective(string s)
    {
        if(s=="")  return false;
        int size=s.size(),sum=0;
        if(size!=1 && s[0]=='0')    return false;
        switch(size)
        {
            case 1:
                sum=s[0]-'0';
                break;
            case 2:
                sum=(s[0]-'0')*10+s[1]-'0';
                break;
            case 3:
                sum=(s[0]-'0')*100+(s[1]-'0')*10+s[2]-'0';
                break;
        }
        if(sum>=0&&sum<=255)    return true;
        return false;
    }
    //回溯
    void backtracking(string s)
    {
        if(path.size()>4)   return;
        //结束条件,s被切割完且path中有四个整数
        if(s.size()==0 && path.size()==4)
        {
            string temp=path[0]+'.'+path[1]+'.'+path[2]+'.'+path[3];
            ans.push_back(temp);
            return;
        }
        for(int i=0;i2) break;
            string temp=s.substr(0,i+1);
            //如果有效,继续递归下一层寻找有效值
            if(isEffective(temp))
            {
                path.push_back(temp);
                backtracking(s.substr(i+1,s.size()));
                path.pop_back();
            }
            
        }
    }
    vector restoreIpAddresses(string s) {
        backtracking(s);
        return ans;
    }
};

 

 

你可能感兴趣的:(leetcode,算法,数据结构,c++)