【算法学习】剑指 Offer II 083. 没有重复元素集合的全排列|46. 全排列(java / c / c++ / python / go / rust)

简介: 给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。

剑指 Offer II 083. 没有重复元素集合的全排列|46. 全排列:

给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。

样例 1

输入:
    nums = [1,2,3]

输出:
    [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

样例 2

输入:
    nums = [0,1]

输出:
    [[0,1],[1,0]]

样例 3

输入:
    nums = [1]
    
输出:
    [[1]]

提示

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

分析

  • 这道算法题采用递归,回溯法比较简单,谁要是非要用循环非递归,二当家的佩服。
  • 提示中说每个数字各不相同,那我们全排列就可以考虑成数字所在位置或者说是数组的下标的不同排列,因为数字都不同,所以我们就不必关心每个数字是几了。
  • 可以单开辟空间存储中间排列,这样我们需要能判断某个数字是否被选择过,可以用hash表存储当前排列结果,然后去看是否含有当前数字,但是这样似乎比较低效。
  • 每个位置的数字都不一样,所以我们直接存储一下某个位置的数字是否被使用即可。
  • 可以直接使用一个布尔数组存储访问过的位置,但是提示中说数字个数最多6个,那我们最多用6个二进制位就可以表示所有数字的已使用和未使用,一个 int 型变量足以,我们用这个 int 型变量的二进制位变化,去对应数字的已使用和未使用。
  • 也可以直接在原数组用交换的方式模拟排列,每个数字在所有位置上都排一次不就是全排列吗?先轮着放第一个位置,然后轮着放第二个位置,以此类推。

题解

java

不使用交换的方式

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        dfs(nums, new ArrayList<>(nums.length), 0, ans);
        return ans;
    }
    
    private void dfs(int[] nums, List<Integer> row, int flag, List<List<Integer>> ans) {
        if (row.size() == nums.length) {
            ans.add(new ArrayList<>(row));
            return;
        }
        for (int i = 0; i < nums.length; ++i) {
            if (((flag >> i) & 1) == 0) {
                row.add(nums[i]);
                dfs(nums, row, flag | (1 << i), ans);
                row.remove(row.size() - 1);
            }
        }
    }
}

使用交换的方式

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        backtrack(nums, 0, ans);
        return ans;
    }

    private void backtrack(int[] nums, int cur, List<List<Integer>> ans) {
        if (cur == nums.length) {
            ans.add(Arrays.stream(nums).boxed().collect(Collectors.toList()));
            return;
        }
        // 当前位置保持不变,接着排下一个
        backtrack(nums, cur + 1, ans);
        // 换后面的某一个到当前位置
        for (int i = cur + 1; i < nums.length; ++i) {
            swap(nums, cur, i);
            backtrack(nums, cur + 1, ans);
            swap(nums, cur, i);
        }
    }

    private void swap(int[] nums, int a, int b) {
        nums[a] ^= nums[b];
        nums[b] ^= nums[a];
        nums[a] ^= nums[b];
    }
}

c

/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */
int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
    *returnSize = numsSize;
    for (int i = 2; i < numsSize; ++i) {
        *returnSize *= i;
    }

    int **ans = (int **) malloc(sizeof(int *) * (*returnSize));
    *returnColumnSizes = (int *) malloc(sizeof(int) * (*returnSize));
    for (int i = 0; i < *returnSize; ++i) {
        ans[i] = (int *) malloc(sizeof(int) * numsSize);
        (*returnColumnSizes)[i] = numsSize;
    }

    int ansSize = 0;

    backtrack(nums, numsSize, 0, ans, &ansSize);

    return ans;
}

void backtrack(int* nums, int numsSize, int cur, int **ans, int *ansSize) {
    if (cur == numsSize) {
        for (int i = 0; i < numsSize; ++i) {
            ans[*ansSize][i] = nums[i];
        }
        *ansSize += 1;
        return;
    }
    // 当前位置保持不变,接着排下一个
    backtrack(nums, numsSize, cur + 1, ans, ansSize);
    // 换后面的某一个到当前位置
    for (int i = cur + 1; i < numsSize; ++i) {
        swap(nums, cur, i);
        backtrack(nums, numsSize, cur + 1, ans, ansSize);
        swap(nums, cur, i);
    }
}

void swap(int* nums, int a, int b) {
    nums[a] ^= nums[b];
    nums[b] ^= nums[a];
    nums[a] ^= nums[b];
}

c++

class Solution {
private:
    void backtrack(vector<int> &nums, int cur, vector<vector<int>> &ans) {
        if (cur == nums.size()) {
            ans.push_back(nums);
            return;
        }
        // 当前位置保持不变,接着排下一个
        backtrack(nums, cur + 1, ans);
        // 换后面的某一个到当前位置
        for (int i = cur + 1; i < nums.size(); ++i) {
            swap(nums[cur], nums[i]);
            backtrack(nums, cur + 1, ans);
            swap(nums[cur], nums[i]);
        }
    }
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> ans;

        backtrack(nums, 0, ans);

        return ans;
    }
};

python

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        ans = []

        def backtrack(cur: int) -> None:
            if cur == n:
                ans.append(nums[:])
                return
            # 当前位置保持不变,接着排下一个
            backtrack(cur + 1)
            # 换后面的某一个到当前位置
            for i in range(cur + 1, n):
                nums[cur], nums[i] = nums[i], nums[cur]
                backtrack(cur + 1)
                nums[cur], nums[i] = nums[i], nums[cur]

        backtrack(0)
        return ans
        

go

func permute(nums []int) [][]int {
    n := len(nums)
    var ans [][]int

    var backtrack func(cur int)
    backtrack = func(cur int) {
        if cur == n {
            ans = append(ans, append([]int{}, nums...))
            return
        }
        // 当前位置保持不变,接着排下一个
        backtrack(cur + 1)
        // 换后面的某一个到当前位置
        for i := cur + 1; i < n; i++ {
            nums[cur], nums[i] = nums[i], nums[cur]
            backtrack(cur + 1)
            nums[cur], nums[i] = nums[i], nums[cur]
        }
    }

    backtrack(0)

    return ans
}

rust

impl Solution {
    pub fn permute(mut nums: Vec<i32>) -> Vec<Vec<i32>> {
        let mut ans = Vec::new();

        Solution::backtrack(&mut nums, 0, &mut ans);

        ans
    }

    fn backtrack(nums: &mut Vec<i32>, cur: usize, ans: &mut Vec<Vec<i32>>) {
        if cur == nums.len() {
            ans.push(nums.clone());
            return;
        }
        // 当前位置保持不变,接着排下一个
        Solution::backtrack(nums, cur + 1, ans);
        // 换后面的某一个到当前位置
        (cur + 1..nums.len()).for_each(|i| {
            nums.swap(cur, i);
            Solution::backtrack(nums, cur + 1, ans);
            nums.swap(cur, i);
        });
    }
}

在这里插入图片描述


原题传送门:https://leetcode-cn.com/problems/VvJkup/

原题传送门:https://leetcode-cn.com/problems/permutations/


非常感谢你阅读本文~
放弃不难,但坚持一定很酷~
希望我们大家都能每天进步一点点~
本文由 二当家的白帽子:https://developer.aliyun.com/profile/sqd6avc7qgj7y 博客原创~

相关文章
|
6月前
|
机器学习/深度学习 算法 数据挖掘
没发论文的注意啦!重磅更新!GWO-BP-AdaBoost预测!灰狼优化、人工神经网络与AdaBoost集成学习算法预测研究(Matlab代码实现)
没发论文的注意啦!重磅更新!GWO-BP-AdaBoost预测!灰狼优化、人工神经网络与AdaBoost集成学习算法预测研究(Matlab代码实现)
214 0
|
9月前
|
存储 监控 算法
基于 C++ 哈希表算法实现局域网监控电脑屏幕的数据加速机制研究
企业网络安全与办公管理需求日益复杂的学术语境下,局域网监控电脑屏幕作为保障信息安全、规范员工操作的重要手段,已然成为网络安全领域的关键研究对象。其作用类似网络空间中的 “电子眼”,实时捕获每台电脑屏幕上的操作动态。然而,面对海量监控数据,实现高效数据存储与快速检索,已成为提升监控系统性能的核心挑战。本文聚焦于 C++ 语言中的哈希表算法,深入探究其如何成为局域网监控电脑屏幕数据处理的 “加速引擎”,并通过详尽的代码示例,展现其强大功能与应用价值。
206 2
|
5月前
|
机器学习/深度学习 运维 算法
【微电网多目标优化调度】多目标学习者行为优化算法MOLPB求解微电网多目标优化调度研究(Matlab代码实现)
【微电网多目标优化调度】多目标学习者行为优化算法MOLPB求解微电网多目标优化调度研究(Matlab代码实现)
287 1
|
11月前
|
存储 算法 数据处理
公司局域网管理中的哈希表查找优化 C++ 算法探究
在数字化办公环境中,公司局域网管理至关重要。哈希表作为一种高效的数据结构,通过哈希函数将关键值(如IP地址、账号)映射到数组索引,实现快速的插入、删除与查找操作。例如,在员工登录验证和设备信息管理中,哈希表能显著提升效率,避免传统线性查找的低效问题。本文以C++为例,展示了哈希表在局域网管理中的具体应用,包括设备MAC地址与IP分配的存储与查询,并探讨了优化哈希函数和扩容策略,确保网络管理高效准确。
|
9月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
234 17
|
7月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
205 0
|
11月前
|
算法 数据可视化 开发者
为什么要学习数据结构与算法
今天,我向大家介绍一门非常重要的课程——《数据结构与算法》。这门课不仅是计算机学科的核心,更是每一位开发者从“小白”迈向“高手”的必经之路。
为什么要学习数据结构与算法
|
8月前
|
机器学习/深度学习 存储 算法
基于 C++ 布隆过滤器算法的局域网上网行为控制:URL 访问过滤的高效实现研究
本文探讨了一种基于布隆过滤器的局域网上网行为控制方法,旨在解决传统黑白名单机制在处理海量URL数据时存储与查询效率低的问题。通过C++实现URL访问过滤功能,实验表明该方法可将内存占用降至传统方案的八分之一,查询速度提升约40%,假阳性率可控。研究为优化企业网络管理提供了新思路,并提出结合机器学习、改进哈希函数及分布式协同等未来优化方向。
254 0
|
10月前
|
存储 监控 算法
基于 C++ 哈希表算法的局域网如何监控电脑技术解析
当代数字化办公与生活环境中,局域网的广泛应用极大地提升了信息交互的效率与便捷性。然而,出于网络安全管理、资源合理分配以及合规性要求等多方面的考量,对局域网内计算机进行有效监控成为一项至关重要的任务。实现局域网内计算机监控,涉及多种数据结构与算法的运用。本文聚焦于 C++ 编程语言中的哈希表算法,深入探讨其在局域网计算机监控场景中的应用,并通过详尽的代码示例进行阐释。
209 4
|
11月前
|
存储 算法 安全
企业员工数据泄露防范策略:基于 C++ 语言的布隆过滤器算法剖析[如何防止员工泄密]
企业运营过程中,防范员工泄密是信息安全领域的核心议题。员工泄密可能致使企业核心数据、商业机密等关键资产的流失,进而给企业造成严重损失。为应对这一挑战,借助恰当的数据结构与算法成为强化信息防护的有效路径。本文专注于 C++ 语言中的布隆过滤器算法,深入探究其在防范员工泄密场景中的应用。
248 8