LeetCode18。4つの数値の合計(4sum)-ダブルポインター方式とバックトラッキング方式

LeetCode18.4つの数値の合計

整数のn配列numsとターゲット値が与えられると、が?に等しいa + b + c + dとなるように、4つの要素a、b、c、およびdがあるかどうかがtarget決定されます。条件を満たすが繰り返されていない4つのタプルをすべて見つけます。numstarget

注意:

回答に4倍の繰り返しを含めることはできません。

例:

给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

满足要求的四元组集合为:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

ダブルポインター方式

4つの数字の合計を3つの数字の合計に変換してから、2つの数字の合計を変換します

class Solution {
    
    
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
    
    
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());   //首先排序
        if (nums.empty()) return {
    
    };
        
        for(int z = 0; z < nums.size(); z ++){
    
    
            
            if (z > 0 && nums[z] == nums[z - 1]) continue;
            int newTarget = target - nums[z];   // 将四数之和转化为3数
            
            for(int k = z+1; k < nums.size(); k++){
    
       // 三数变成两数
                
                if(k > z+1 && nums[k] == nums[k - 1]) continue;
                int newTarget2 = newTarget - nums[k];
                int i = k + 1, j = nums.size() - 1;
                while (i < j) {
    
                  // 两数之和直接套用《两数之和2》的题
                    if (nums[i] + nums[j] == newTarget2) {
    
    
                        res.push_back({
    
    nums[z], nums[k], nums[i], nums[j]});
                        while (i < j && nums[i] == nums[i + 1]) ++i;   //注意去重
                        while (i < j && nums[j] == nums[j - 1]) --j;
                        ++i; --j;
                    } else if (nums[i] + nums[j] < newTarget2) ++i;
                    
                    else --j;
                }
            }
            
        }
        return res;
        
    }
};

バックトラック

まず、数値を昇順で並べ替え、回答の4倍の不特定の数値をnに設定します。

剪定を4つのカテゴリに分けました。括弧内の剪定を完了するために使用されます。

  • 残りのオプション番号の数がn未満の場合は、それらを切り取ります(再帰的に戻ります)。
  • 2番目のサイクルから始まる各レベルの再帰で、配列内の現在の番号が前の番号と同じである場合、それは切り捨てられます(次のサイクルでは、このタスクは重複を削除することです)。
  • 現在の数+決定された数の合計+(n-1)*並べ替え後の配列内の現在の数の次の数>ターゲットの場合、次の数をどのように選択しても、合計はターゲットよりも大きくなければならないため、プルーニング(再帰的)戻る);
  • 現在の番号+決定された番号の合計+(n-1)*並べ替え後の配列の最後の番号<ターゲットの場合、現在の番号を選択できず、現在の番号と次の番号を足したものがターゲットよりも小さい必要があるため、(次回のために)プルーニングします。サイクル);

cppバージョン

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Solution_18_001 {
    
    

private:
	vector<vector<int>> ans;//结果集
	vector<int> nums; //排序后的数组
	vector<int> path; //路径
	int target;//目标
	int numSize;//数组长度
	void dfs(int idx, int sum) {
    
    
		//满足4个数之和
		if (sum == target && path.size() == 4) {
    
    
			ans.emplace_back(path);
			return;
		}
		for (int i = idx; i < numSize; ++i) {
    
    
			if (numSize - i < int(4 - path.size())) //剪枝
				return;
			if (i > idx && nums[i] == nums[i - 1]) //去重
				continue;
			if (i < numSize - 1 && (sum + nums[i] + int(3 - path.size()) * nums[i + 1] )> target) //剪枝
				return;
			if (i < numSize - 1 && (sum + nums[i] + int(3 - path.size()) * nums[numSize - 1]) < target) //剪枝
				continue;
			//添加到路径中
			path.emplace_back(nums[i]);
			//深度优先搜索
			dfs(i + 1, sum + nums[i]);
			//回溯,从路径中移除
			path.pop_back();
		}
		return;
	}

public:
	vector<vector<int>> fourSum(vector<int> &nums, int target) {
    
    
		sort(nums.begin(), nums.end());
		this->nums = nums;		//复制一下,不破坏原来的nums
		this->target = target; //复制一下,不破坏原来的target
		this->numSize = nums.size();
		if (numSize < 4)
			return ans;
		dfs(0, 0);
		return ans;
	}
};

int main() {
    
    

	//vector初始方案1
//	vector<int> nums; //1, 0, -1, 0, -2, 2
//	nums.push_back(1);
//	nums.push_back(0);
//	nums.push_back(-1);
//	nums.push_back(0);
//	nums.push_back(-2);
//	nums.push_back(2);

	//vector初始方案2
	int arr[] = {
    
    1, 0, -1, 0, -2, 2};
	vector<int> nums(arr, arr + sizeof(arr) / sizeof(int));

	//执行四数求和
	Solution_18_001 s;
	vector<vector<int>> vvec;
	vvec = s.fourSum(nums, 0);

	//打印结果
	vector<vector<int>>::iterator vv = vvec.begin();
	while (vv != vvec.end()) {
    
    
		vector<int>::iterator v = (*vv).begin();
		while (v != (*vv).end()) {
    
    
			std::cout << *v << "\t";
			v++;
		}
		std::cout << std::endl;
		vv++;
	}
}

javaバージョン

// "static void main" must be defined in a public class.



public class Main {
    
    
    
    private List<List<Integer>> res;//结果集
    private int target;//目标

    public List<List<Integer>> fourSum(int[] nums, int target) {
    
    
        res = new ArrayList<>();
        Arrays.sort(nums);  //  排序便于剪枝操作
        this.target = target;
        helper(nums, 0, 0, new ArrayList<>());
        return res;
    }

    private void helper(int[] nums, int index, int target, List<Integer> list) {
    
    
        if (list.size() == 4 && target == this.target) {
    
    
            //  如果数量相等,且和等于target,直接放入结果中
            res.add(new ArrayList<>(list));
            return;
        }

        for (int i = index; i < nums.length; i++) {
    
    
            //  后续数量如果已经凑不齐4个,直接剪掉
            if (nums.length - i < 4 - list.size()) return;
            //  避免重复结果
            if (i > index && nums[i] == nums[i - 1]) continue;
            //  这里借鉴了官方题解评论中一位大哥的写法,这样可以精简代码,否则(3 - list.size()) * nums[i + 1]要写成好几个if条件,这样会粗略一些,不过应该还好
            if (i < nums.length - 1 && (target + nums[i] + (3 - list.size()) * nums[i + 1] ) > this.target) return;
            //  这里同上
            if (i < nums.length - 1 && (target + nums[i] + (3 - list.size()) * nums[nums.length - 1] ) < this.target)
                continue;
            //  下方的代码就是经典的回溯代码了,不再赘述
            list.add(nums[i]);
            helper(nums, i + 1, target + nums[i], list);
            list.remove(list.size() - 1);
        }
    }
    
    public static void main(String[] args) {
    
    
        Main s = new Main();
        int[] nums = {
    
    1, 0, -1, 0, -2, 2};
        List<List<Integer>> list = s.fourSum(nums,0);
        System.out.println(list);
    }
}

参照

leetcodeleetcode.46
。フルアレンジメント-バックトラッキングアルゴリズム+ dfs
フルアレンジメントll(Permutations II)-バックトラッキングアルゴリズム+ dfs
emplace_back()とpush_backの違い

おすすめ

転載: blog.csdn.net/e891377/article/details/108928189