Autumn Recruitment - Algorithm - Backtracking Algorithm

Autumn Recruitment - Algorithm - Backtracking Algorithm

introduce

​ Backtracking is actually a heuristic algorithm. The biggest difference between this algorithm and brute force search is that in the backtracking algorithm, it is a step-by-step careful forward test, and the situation detected at each step will be evaluated. If the current If the situation can no longer meet the requirements, then there is no need to continue, that is to say, it can help us avoid many detours.
​ The feature of the backtracking algorithm is that when an illegal situation occurs, the algorithm can go back to the previous situation, it can be one step back, sometimes it can even go back multiple steps, and then try other paths and methods. This also means that if you want to use the backtracking algorithm, you must ensure that there are multiple attempts every time.

template

The following deduplication and pruning can’t be done. You can directly use set or List’s contain method to process before returning the result. The efficiency will decrease, but at least it can be correct

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        trim,去重、做剪枝
        choose,选择合适的结果
        backtrack(路径, 选择列表)
        back,撤销选择

The common types are full permutation and combination:

The main difference is in the step of [for selection in selection list], the full arrangement is generally traversed from the beginning, while the combination side is traversed from the last interface

example

47. Full Arrangement II

image-20220731231756894

class Solution {
    
    

    List<List<Integer>> res;
    public List<List<Integer>> permuteUnique(int[] nums){
    
    
        res = new LinkedList<>();
        Arrays.sort(nums);
        boolean[] used = new boolean[nums.length];
        backtrace(nums,new LinkedList<>(),used);
        return res;
    }

    private void backtrace(int[] candidates,LinkedList<Integer> chain,boolean[] used) {
    
    
        //base
        if (chain.size()==candidates.length){
    
    
            res.add(new LinkedList<>(chain));
            return;
        }else if (chain.size()>candidates.length){
    
    
            return;
        }

        //for
        for (int i = 0; i < candidates.length; i++) {
    
    
            //trim
            //112 ==> 112 211 211
            if (used[i])    continue;
            if (i>0&&candidates[i]==candidates[i-1]&&!used[i-1])    continue;
            //choose
            chain.add(candidates[i]);
            used[i]=true;
            backtrace(candidates,chain,used);
            //back
            chain.removeLast();
            used[i]=false;
        }
    }
}

40. Portfolio Sum II

image-20220731212850165

class Solution {
    
    

    List<List<Integer>> res;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
    
    
        res = new LinkedList<>();
        Arrays.sort(candidates);
        backtrace(target,candidates,0,new LinkedList<>(),0);
        return res;
    }

    private void backtrace(int target, int[] candidates,int sum,LinkedList<Integer> chain,int pos) {
    
    
        //base
        if (sum==target){
    
    
            res.add(new LinkedList<>(chain));
            return;
        }else if (sum>target){
    
    
            return;
        }

        //for
        for (int i = pos; i < candidates.length; i++) {
    
    
            //trim
            //加入给了 1,1,6,7 target=8 , ==> 可以选择: 116 ,有两个1要对两个1,7去重
            //116 是通过回溯递归获得的第二个1
            //17  17,是通过for循环获得的第二个1
            //循环会造成i>pos,回溯递归i=pos,借此进行剪枝、去重
            if (i>pos&&candidates[i]==candidates[i-1]){
    
    
                continue;
            }
			//choose
            chain.add(candidates[i]);
            backtrace(target,candidates,sum+candidates[i],chain,i+1);
            //back
            chain.removeLast();

        }

    }
}

Guess you like

Origin blog.csdn.net/qq_50665031/article/details/126092639