返回

浅析 Java 与 C++ 在 Leetcode 667 优美子排列 II 中的差异

后端







## 引言

算法题解平台 Leetcode 提供了一个丰富的题目库,供程序员磨炼算法技能和准备技术面试。其中,Leetcode 667 优美子排列 II 是一个经典问题,它要求给定一个数组,生成其所有不同且有区别的排列。本篇文章将重点分析 Java 和 C++ 两种语言在解决此问题时的差异,并通过对不同语言编写的解题代码进行深入比较,帮助开发者更好地理解两者的特性和异同。

## Java 解题方案

在 Java 中,一种常见的解决 Leetcode 667 问题的方案是使用回溯算法。回溯算法是一种通过试错来搜索解决方案的算法,它将问题转化为一个搜索树,然后通过系统地探索树中的所有分支来寻找解决方案。

```java
import java.util.ArrayList;
import java.util.List;

public class Solution {

    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        backtrack(result, new ArrayList<>(), nums);
        return result;
    }

    private void backtrack(List<List<Integer>> result, List<Integer> current, int[] nums) {
        if (current.size() == nums.length) {
            result.add(new ArrayList<>(current));
            return;
        }

        for (int i = 0; i < nums.length; i++) {
            if (current.contains(nums[i])) {
                continue;
            }
            current.add(nums[i]);
            backtrack(result, current, nums);
            current.remove(current.size() - 1);
        }
    }
}

分析

Java 回溯算法的实现主要包括以下步骤:

  1. 初始化一个结果列表 result 和一个当前排列列表 current
  2. current 的长度与 nums 的长度相等时,表明当前排列已完成,将 current 添加到 result 中。
  3. 对于 nums 中的每个元素,如果 current 中已包含该元素,则跳过。否则,将该元素添加到 current 中,并继续回溯。
  4. 回溯时,移除 current 中最后一个元素。

C++ 解题方案

在 C++ 中,同样可以采用回溯算法来解决 Leetcode 667 优美子排列 II 问题。

#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());  // 对数组进行排序
        backtrack(result, vector<int>(), nums, vector<bool>(nums.size(), false));
        return result;
    }

private:
    void backtrack(vector<vector<int>>& result, vector<int> current, vector<int>& nums, vector<bool>& visited) {
        if (current.size() == nums.size()) {
            result.push_back(current);
            return;
        }

        for (int i = 0; i < nums.size(); i++) {
            if (visited[i]) {
                continue;
            }
            if (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) {
                continue;
            }
            current.push_back(nums[i]);
            visited[i] = true;
            backtrack(result, current, nums, visited);
            current.pop_back();
            visited[i] = false;
        }
    }
};

分析

C++ 回溯算法的实现与 Java 版本类似,主要包括以下步骤:

  1. 初始化一个结果列表 result、一个当前排列列表 current、一个访问标志列表 visited 和对 nums 进行排序。
  2. current 的长度与 nums 的长度相等时,表明当前排列已完成,将 current 添加到 result 中。
  3. 对于 nums 中的每个元素,如果 visited 中对应位置已被访问,则跳过。如果当前元素与前一个元素相等且前一个元素尚未被访问,则也跳过。否则,将该元素添加到 current 中,并将对应位置的 visited 设为 true,并继续回溯。
  4. 回溯时,移除 current 中最后一个元素,并将对应位置的 visited 设为 false

差异分析

通过比较 Java 和 C++ 的解题方案,可以发现两者的主要差异如下:

  1. 数据结构: Java 使用 ArrayList 存储结果和当前排列,而 C++ 使用 vector。
  2. 排序: C++ 对 nums 进行排序,以避免重复排列,而 Java 不需要排序。
  3. 访问标志: C++ 使用一个布尔数组 visited 来记录每个元素是否已访问,以避免生成重复排列,而 Java 则使用 contains 方法检查是否包含元素。
  4. 语法: C++ 使用 lambda 表达式对 nums 进行排序,而 Java 使用 Collections.sort 方法。

总结

通过分析 Leetcode 667 优美子排列 II 问题的 Java 和 C++ 解题方案,开发者可以深入了解两种语言在解决算法问题时的异同。Java 回溯算法简单易懂,而 C++ 回溯算法通过排序和访问标志优化了重复排列的检查过程。了解这些差异对于初涉两门语言的开发者至关重要,它有助于他们更深入地理解语言的特性和最佳实践,从而为后续的学习和实践奠定坚实的基础。