【算法学习】剑指 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 博客原创~

相关文章
|
1天前
|
存储 编译器 Go
Go语言学习12-数据的使用
【5月更文挑战第5天】本篇 Huazie 向大家介绍 Go 语言数据的使用,包含赋值语句、常量与变量、可比性与有序性
32 6
Go语言学习12-数据的使用
|
4天前
|
Java Go
Go语言学习11-数据初始化
【5月更文挑战第3天】本篇带大家通过内建函数 new 和 make 了解Go语言的数据初始化过程
17 1
Go语言学习11-数据初始化
|
4天前
|
小程序 程序员 开发者
Python学习心得——小白的成长之路
Python学习心得——小白的成长之路
11 0
|
5天前
|
网络安全 Python
网安之python基础学习练习(2-3)
本篇博文是关于网络安全课程中Python编程的学习实践总结。分享关于两个练习题目及其解决方案。第一个题目要求用户输入姓名并选择一项武技,使用for循环和if判断实现。第二个题目是删除列表中特定值(如&#39;cat&#39;)的所有元素,作者展示了两种方法,包括列表推导式和常规循环删除。接下来,文章还介绍了如何编写一个函数,随机生成一副扑克牌(除大小王),并返回一张随机抽取的牌。
|
5天前
|
存储 网络安全 索引
网安之python基础学习练习(1)
本篇博文是关于网络安全课程中Python编程学习的总结,主要内容包括:1) 常见数据类型的回顾和应用,如数字(整数、浮点数、复数)、字符串、列表、元组、集合、字典和布尔类型;2) 数据类型的实例操作,展示如何创建和使用这些类型;3) 数值类型之间的加、减、乘、除和模运算;4) 列表和元组的索引访问;5) 字典的修改,如查看键和值,以及更新值。文章强调了基础知识的重要性,并以“自满必定失败,骄傲必定后悔”作为每日一言。
|
5天前
|
JavaScript 前端开发 Go
Go语言的入门学习
【4月更文挑战第7天】Go语言,通常称为Golang,是由Google设计并开发的一种编程语言,它于2009年公开发布。Go的设计团队主要包括Robert Griesemer、Rob Pike和Ken Thompson,这三位都是计算机科学和软件工程领域的杰出人物。
14 1
|
6天前
|
Go
|
7天前
|
机器学习/深度学习 数据挖掘 程序员
Python学习难度的具体标准
Python学习难度因个人编程背景、目标、资源和学习能力而异。对有编程经验者来说,Python的简单语法使其易上手;而对于新手,理解基础概念可能需更多时间。不同应用领域(如Web开发、数据分析)的学习曲线也不同。丰富的学习资源适应各种水平,但选择合适资源很重要。成功学习Python需要逻辑思维、问题解决能力及毅力。总的来说,Python学习难度因人而异,需结合自身条件评估。
19 0
|
7天前
|
存储 索引 Python
python数据结构知识学习
【5月更文挑战第6天】Python提供四种核心数据结构:列表(List)——可变有序集合,支持索引和切片;元组(Tuple)——不可变有序集合;字典(Dictionary)——键值对结构,通过键访问值;集合(Set)——无序不重复元素集合,支持数学运算。此外,Python允许自定义数据结构,如链表、树、图,以适应不同问题需求。
17 0
|
11天前
|
XML 前端开发 Oracle
16:JSP简介、注释与Scriptlet、Page指令元素、Include操作、内置对象、四种属性-Java Web
16:JSP简介、注释与Scriptlet、Page指令元素、Include操作、内置对象、四种属性-Java Web
13 2