diff --git a/solution/0000-0099/0047.Permutations II/README.md b/solution/0000-0099/0047.Permutations II/README.md index 1cf1c45d826bf..8a3cb5cd85def 100644 --- a/solution/0000-0099/0047.Permutations II/README.md +++ b/solution/0000-0099/0047.Permutations II/README.md @@ -58,12 +58,12 @@ tags: 我们可以先对数组进行排序,这样就可以将重复的数字放在一起,方便我们进行去重。 -然后,我们设计一个函数 $dfs(i)$,表示当前需要填写第 $i$ 个位置的数。函数的具体实现如下: +然后,我们设计一个函数 $\textit{dfs}(i)$,表示当前需要填写第 $i$ 个位置的数。函数的具体实现如下: - 如果 $i = n$,说明我们已经填写完毕,将当前排列加入答案数组中,然后返回。 -- 否则,我们枚举第 $i$ 个位置的数 $nums[j]$,其中 $j$ 的范围是 $[0, n - 1]$。我们需要保证 $nums[j]$ 没有被使用过,并且与前面枚举的数不同,这样才能保证当前排列不重复。如果满足条件,我们就可以填写 $nums[j]$,并继续递归地填写下一个位置,即调用 $dfs(i + 1)$。在递归调用结束后,我们需要将 $nums[j]$ 标记为未使用,以便于进行后面的枚举。 +- 否则,我们枚举第 $i$ 个位置的数 $nums[j]$,其中 $j$ 的范围是 $[0, n - 1]$。我们需要保证 $nums[j]$ 没有被使用过,并且与前面枚举的数不同,这样才能保证当前排列不重复。如果满足条件,我们就可以填写 $nums[j]$,并继续递归地填写下一个位置,即调用 $\textit{dfs}(i + 1)$。在递归调用结束后,我们需要将 $nums[j]$ 标记为未使用,以便于进行后面的枚举。 -在主函数中,我们首先对数组进行排序,然后调用 $dfs(0)$,即从第 0 个位置开始填写,最终返回答案数组即可。 +在主函数中,我们首先对数组进行排序,然后调用 $\textit{dfs}(0)$,即从第 0 个位置开始填写,最终返回答案数组即可。 时间复杂度 $O(n \times n!)$,空间复杂度 $O(n)$。其中 $n$ 是数组的长度。需要进行 $n!$ 次枚举,每次枚举需要 $O(n)$ 的时间来判断是否重复。另外,我们需要一个标记数组来标记每个位置是否被使用过,因此空间复杂度为 $O(n)$。 @@ -141,12 +141,12 @@ class Solution { class Solution { public: vector> permuteUnique(vector& nums) { - sort(nums.begin(), nums.end()); + ranges::sort(nums); int n = nums.size(); vector> ans; vector t(n); vector vis(n); - function dfs = [&](int i) { + auto dfs = [&](this auto&& dfs, int i) { if (i == n) { ans.emplace_back(t); return; @@ -171,7 +171,7 @@ public: ```go func permuteUnique(nums []int) (ans [][]int) { - sort.Ints(nums) + slices.Sort(nums) n := len(nums) t := make([]int, n) vis := make([]bool, n) @@ -203,8 +203,8 @@ function permuteUnique(nums: number[]): number[][] { nums.sort((a, b) => a - b); const n = nums.length; const ans: number[][] = []; - const t: number[] = new Array(n); - const vis: boolean[] = new Array(n); + const t: number[] = Array(n); + const vis: boolean[] = Array(n).fill(false); const dfs = (i: number) => { if (i === n) { ans.push(t.slice()); @@ -228,34 +228,75 @@ function permuteUnique(nums: number[]): number[][] { #### Rust ```rust -use std::collections::HashSet; impl Solution { - fn dfs(i: usize, nums: &mut Vec, res: &mut Vec>) { + pub fn permute_unique(mut nums: Vec) -> Vec> { + nums.sort(); let n = nums.len(); - if i == n { - res.push(nums.clone()); - return; - } - let mut set = HashSet::new(); - for j in i..n { - if set.contains(&nums[j]) { - continue; + let mut ans = Vec::new(); + let mut t = vec![0; n]; + let mut vis = vec![false; n]; + + fn dfs( + nums: &Vec, + t: &mut Vec, + vis: &mut Vec, + ans: &mut Vec>, + i: usize, + ) { + if i == nums.len() { + ans.push(t.clone()); + return; + } + for j in 0..nums.len() { + if vis[j] || (j > 0 && nums[j] == nums[j - 1] && !vis[j - 1]) { + continue; + } + t[i] = nums[j]; + vis[j] = true; + dfs(nums, t, vis, ans, i + 1); + vis[j] = false; } - set.insert(nums[j]); - nums.swap(i, j); - Self::dfs(i + 1, nums, res); - nums.swap(i, j); } - } - pub fn permute_unique(mut nums: Vec) -> Vec> { - let mut res = vec![]; - Self::dfs(0, &mut nums, &mut res); - res + dfs(&nums, &mut t, &mut vis, &mut ans, 0); + ans } } ``` +#### JavaScript + +```js +/** + * @param {number[]} nums + * @return {number[][]} + */ +var permuteUnique = function (nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + const ans = []; + const t = Array(n); + const vis = Array(n).fill(false); + const dfs = i => { + if (i === n) { + ans.push(t.slice()); + return; + } + for (let j = 0; j < n; ++j) { + if (vis[j] || (j > 0 && nums[j] === nums[j - 1] && !vis[j - 1])) { + continue; + } + t[i] = nums[j]; + vis[j] = true; + dfs(i + 1); + vis[j] = false; + } + }; + dfs(0); + return ans; +}; +``` + #### C# ```cs diff --git a/solution/0000-0099/0047.Permutations II/README_EN.md b/solution/0000-0099/0047.Permutations II/README_EN.md index 14f98233bb16d..db501a859c053 100644 --- a/solution/0000-0099/0047.Permutations II/README_EN.md +++ b/solution/0000-0099/0047.Permutations II/README_EN.md @@ -54,16 +54,16 @@ tags: ### Solution 1: Sorting + Backtracking -We can first sort the array, which allows us to place duplicate numbers together, making it easier for us to remove duplicates. +We can first sort the array so that duplicate numbers are placed together, making it easier to remove duplicates. -Next, we design a function $dfs(i)$, indicating that we need to fill in the number at the $i$th position. The specific implementation of the function is as follows: +Then, we design a function $\textit{dfs}(i)$, which represents the current number to be placed at the $i$-th position. The specific implementation of the function is as follows: -- If $i = n$, it means we have finished filling in, add the current permutation to the answer array, and then return. -- Otherwise, we enumerate the number $nums[j]$ at the $i$th position, where the range of $j$ is $[0, n - 1]$. We need to ensure that $nums[j]$ has not been used and is different from the number enumerated before, so as to ensure that the current permutation is not repeated. If the conditions are met, we can fill in $nums[j]$, and continue to recursively fill in the next position, that is, call $dfs(i + 1)$. After the recursive call ends, we need to mark $nums[j]$ as unused for later enumeration. +- If $i = n$, it means we have filled all positions, add the current permutation to the answer array, and then return. +- Otherwise, we enumerate the number $nums[j]$ for the $i$-th position, where the range of $j$ is $[0, n - 1]$. We need to ensure that $nums[j]$ has not been used and is different from the previously enumerated number to ensure that the current permutation is not duplicated. If the conditions are met, we can place $nums[j]$ and continue to recursively fill the next position by calling $\textit{dfs}(i + 1)$. After the recursive call ends, we need to mark $nums[j]$ as unused to facilitate subsequent enumeration. -In the main function, we first sort the array, then call $dfs(0)$, that is, start filling from the 0th position, and finally return the answer array. +In the main function, we first sort the array, then call $\textit{dfs}(0)$ to start filling from the 0th position, and finally return the answer array. -The time complexity is $O(n \times n!)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array. We need to enumerate $n!$ times, and each enumeration takes $O(n)$ time to judge whether it is repeated. In addition, we need a marker array to mark whether each position has been used, so the space complexity is $O(n)$. +The time complexity is $O(n \times n!)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array. We need to perform $n!$ enumerations, and each enumeration requires $O(n)$ time to check for duplicates. Additionally, we need a marker array to mark whether each position has been used, so the space complexity is $O(n)$. Similar problems: @@ -139,12 +139,12 @@ class Solution { class Solution { public: vector> permuteUnique(vector& nums) { - sort(nums.begin(), nums.end()); + ranges::sort(nums); int n = nums.size(); vector> ans; vector t(n); vector vis(n); - function dfs = [&](int i) { + auto dfs = [&](this auto&& dfs, int i) { if (i == n) { ans.emplace_back(t); return; @@ -169,7 +169,7 @@ public: ```go func permuteUnique(nums []int) (ans [][]int) { - sort.Ints(nums) + slices.Sort(nums) n := len(nums) t := make([]int, n) vis := make([]bool, n) @@ -201,8 +201,8 @@ function permuteUnique(nums: number[]): number[][] { nums.sort((a, b) => a - b); const n = nums.length; const ans: number[][] = []; - const t: number[] = new Array(n); - const vis: boolean[] = new Array(n); + const t: number[] = Array(n); + const vis: boolean[] = Array(n).fill(false); const dfs = (i: number) => { if (i === n) { ans.push(t.slice()); @@ -226,34 +226,75 @@ function permuteUnique(nums: number[]): number[][] { #### Rust ```rust -use std::collections::HashSet; impl Solution { - fn dfs(i: usize, nums: &mut Vec, res: &mut Vec>) { + pub fn permute_unique(mut nums: Vec) -> Vec> { + nums.sort(); let n = nums.len(); - if i == n { - res.push(nums.clone()); - return; - } - let mut set = HashSet::new(); - for j in i..n { - if set.contains(&nums[j]) { - continue; + let mut ans = Vec::new(); + let mut t = vec![0; n]; + let mut vis = vec![false; n]; + + fn dfs( + nums: &Vec, + t: &mut Vec, + vis: &mut Vec, + ans: &mut Vec>, + i: usize, + ) { + if i == nums.len() { + ans.push(t.clone()); + return; + } + for j in 0..nums.len() { + if vis[j] || (j > 0 && nums[j] == nums[j - 1] && !vis[j - 1]) { + continue; + } + t[i] = nums[j]; + vis[j] = true; + dfs(nums, t, vis, ans, i + 1); + vis[j] = false; } - set.insert(nums[j]); - nums.swap(i, j); - Self::dfs(i + 1, nums, res); - nums.swap(i, j); } - } - pub fn permute_unique(mut nums: Vec) -> Vec> { - let mut res = vec![]; - Self::dfs(0, &mut nums, &mut res); - res + dfs(&nums, &mut t, &mut vis, &mut ans, 0); + ans } } ``` +#### JavaScript + +```js +/** + * @param {number[]} nums + * @return {number[][]} + */ +var permuteUnique = function (nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + const ans = []; + const t = Array(n); + const vis = Array(n).fill(false); + const dfs = i => { + if (i === n) { + ans.push(t.slice()); + return; + } + for (let j = 0; j < n; ++j) { + if (vis[j] || (j > 0 && nums[j] === nums[j - 1] && !vis[j - 1])) { + continue; + } + t[i] = nums[j]; + vis[j] = true; + dfs(i + 1); + vis[j] = false; + } + }; + dfs(0); + return ans; +}; +``` + #### C# ```cs diff --git a/solution/0000-0099/0047.Permutations II/Solution.cpp b/solution/0000-0099/0047.Permutations II/Solution.cpp index 696efd76b0895..783c7da618e9d 100644 --- a/solution/0000-0099/0047.Permutations II/Solution.cpp +++ b/solution/0000-0099/0047.Permutations II/Solution.cpp @@ -1,12 +1,12 @@ class Solution { public: vector> permuteUnique(vector& nums) { - sort(nums.begin(), nums.end()); + ranges::sort(nums); int n = nums.size(); vector> ans; vector t(n); vector vis(n); - function dfs = [&](int i) { + auto dfs = [&](this auto&& dfs, int i) { if (i == n) { ans.emplace_back(t); return; @@ -24,4 +24,4 @@ class Solution { dfs(0); return ans; } -}; \ No newline at end of file +}; diff --git a/solution/0000-0099/0047.Permutations II/Solution.go b/solution/0000-0099/0047.Permutations II/Solution.go index 4ecb9325786e8..7b884868c916d 100644 --- a/solution/0000-0099/0047.Permutations II/Solution.go +++ b/solution/0000-0099/0047.Permutations II/Solution.go @@ -1,5 +1,5 @@ func permuteUnique(nums []int) (ans [][]int) { - sort.Ints(nums) + slices.Sort(nums) n := len(nums) t := make([]int, n) vis := make([]bool, n) diff --git a/solution/0000-0099/0047.Permutations II/Solution.js b/solution/0000-0099/0047.Permutations II/Solution.js new file mode 100644 index 0000000000000..f2bb287df5864 --- /dev/null +++ b/solution/0000-0099/0047.Permutations II/Solution.js @@ -0,0 +1,28 @@ +/** + * @param {number[]} nums + * @return {number[][]} + */ +var permuteUnique = function (nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + const ans = []; + const t = Array(n); + const vis = Array(n).fill(false); + const dfs = i => { + if (i === n) { + ans.push(t.slice()); + return; + } + for (let j = 0; j < n; ++j) { + if (vis[j] || (j > 0 && nums[j] === nums[j - 1] && !vis[j - 1])) { + continue; + } + t[i] = nums[j]; + vis[j] = true; + dfs(i + 1); + vis[j] = false; + } + }; + dfs(0); + return ans; +}; diff --git a/solution/0000-0099/0047.Permutations II/Solution.rs b/solution/0000-0099/0047.Permutations II/Solution.rs index 084af4c8cc311..e9733f086439d 100644 --- a/solution/0000-0099/0047.Permutations II/Solution.rs +++ b/solution/0000-0099/0047.Permutations II/Solution.rs @@ -1,26 +1,34 @@ -use std::collections::HashSet; impl Solution { - fn dfs(i: usize, nums: &mut Vec, res: &mut Vec>) { + pub fn permute_unique(mut nums: Vec) -> Vec> { + nums.sort(); let n = nums.len(); - if i == n { - res.push(nums.clone()); - return; - } - let mut set = HashSet::new(); - for j in i..n { - if set.contains(&nums[j]) { - continue; + let mut ans = Vec::new(); + let mut t = vec![0; n]; + let mut vis = vec![false; n]; + + fn dfs( + nums: &Vec, + t: &mut Vec, + vis: &mut Vec, + ans: &mut Vec>, + i: usize, + ) { + if i == nums.len() { + ans.push(t.clone()); + return; + } + for j in 0..nums.len() { + if vis[j] || (j > 0 && nums[j] == nums[j - 1] && !vis[j - 1]) { + continue; + } + t[i] = nums[j]; + vis[j] = true; + dfs(nums, t, vis, ans, i + 1); + vis[j] = false; } - set.insert(nums[j]); - nums.swap(i, j); - Self::dfs(i + 1, nums, res); - nums.swap(i, j); } - } - pub fn permute_unique(mut nums: Vec) -> Vec> { - let mut res = vec![]; - Self::dfs(0, &mut nums, &mut res); - res + dfs(&nums, &mut t, &mut vis, &mut ans, 0); + ans } } diff --git a/solution/0000-0099/0047.Permutations II/Solution.ts b/solution/0000-0099/0047.Permutations II/Solution.ts index 95c29dd6f7451..eeac20f6a595d 100644 --- a/solution/0000-0099/0047.Permutations II/Solution.ts +++ b/solution/0000-0099/0047.Permutations II/Solution.ts @@ -2,8 +2,8 @@ function permuteUnique(nums: number[]): number[][] { nums.sort((a, b) => a - b); const n = nums.length; const ans: number[][] = []; - const t: number[] = new Array(n); - const vis: boolean[] = new Array(n); + const t: number[] = Array(n); + const vis: boolean[] = Array(n).fill(false); const dfs = (i: number) => { if (i === n) { ans.push(t.slice());