LeetCode 46. Permutations

问题链接

LeetCode 46. Permutations

题目解析

求解一个无重复元素序列的全排列。

解题思路

什么是全排列?理解一下题意,简单来讲,就是求解序列中元素的所有排列方法,一共有A(n, n)=n!种排列。可以采用递归、非递归、插空等方法求解。

求解全排列参考链接:数组的全排列

注意:本题中已经说明没有重复元素,对于有重复元素的情况,将在47. Permutations II在给大家说明。

DFS求解全排列

采用DFS,大致算法如下:

  • 任意取一个元素放在第一个位置,则有n种选择;
  • 再剩下的n-1个元素中再取一个元素放在第二个位置则有n-1种选择,此时可以看做对n-1个元素进行全排列;
  • 重复第二步,直到对最后一个元素进行全排列,即最后一个元素放在最后一个位置,全排列结束。

此算法容易理解,但是需要耗费大量的栈空间。每次通过交换nums[index]和nums[i],代表当前选择了nums[i],然后进行下一次选择(DFS),参考代码如下:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector< vector<int> > res;
        DFS(nums, 0, res);
        return res;
    }
    void DFS(vector<int> &nums, int index, vector< vector<int> > &res) {
        if(index >= nums.size()) res.push_back(nums);
        for(int i = index; i < nums.size(); i++) {
            swap(nums[index], nums[i]);
            DFS(nums, index+1, res);
            swap(nums[index], nums[i]);
        }
    }
};

非递归求解全排列

还记得LeetCode 31. Next Permutation吗?这道题中求的是序列的下一个序列,本题中其实就是求所有的“下一个序列”。试想一下,如果我不断地求下一个排列,最终我就可以得到所有的排列。

为了避免重复,先将数组排个序,当数组完全逆序时退出循环。关于如何求解全排列,在LeetCode 31. Next Permutation也有具体的过程,这里就不多说了。

直接借用库函数,投机取巧之法:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector< vector<int> > res;
        sort(nums.begin(), nums.end());
        do {
            res.push_back(nums);
        }while(next_permutation(nums.begin(), nums.end()));
        return res;
    }
};

具体实现全排列,参考代码如下:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector< vector<int> > res;
        sort(nums.begin(), nums.end());
        while(1) {
            res.push_back(nums);
            int n = nums.size(), i = n-2, j = n-1;
            while(i >= 0 && nums[i] >= nums[i+1]) i--;
            if(i >= 0) {
                while(nums[i] >= nums[j]) j--;
                swap(nums[i], nums[j]);
            }
            else break;
            reverse(nums.begin()+i+1, nums.end());
        }
        return res;
    }
};

插空法求解全排列

参考大神的帖子:Permutations。了解到这么一种巧妙的方法。

  • 当n=1时,数组中只有一个数a1,其全排列只有一种,即为a1;
  • 当n=2时,数组中此时有a1a2,其全排列有两种,a1a2和a2a1,那么此时我们考虑和上面那种情况的关系,我们发现,其实就是在a1的前后两个位置分别加入了a2;
  • 当n=3时,数组中有a1a2a3,此时全排列有六种,分别为a1a2a3, a1a3a2, a2a1a3, a2a3a1, a3a1a2和 a3a2a1。那么根据上面的结论,实际上是在a1a2和a2a1的基础上在不同的位置上加入a3而得到的。_ a1 _ a2 _ : a3a1a2, a1a3a2, a1a2a3。_ a2 _ a1 _ : a3a2a1, a2a3a1, a2a1a3。

以此类推,可以求得任意n元素的全排列。代码中每次递归取出原数组第一个元素并删除之,利用words保存当前所有排列,然后每个排列中在各个位置插入该元素,参考代码如下:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        if(nums.empty()) return vector< vector<int> >(1, vector<int>());
        
        vector< vector<int> > res;
        int first = nums[0];
        nums.erase(nums.begin());
        vector< vector<int> > words = permute(nums);
        for(auto &a : words) {
            for(int i = 0; i <= a.size(); ++i) {
                a.insert(a.begin() + i, first);
                res.push_back(a);
                a.erase(a.begin() + i);
            }
        }   
        return res;
    }
};

相似题目

LeetCode 31. Next Permutation


LeetCode All in One题解汇总(持续更新中...)

本文版权归作者AlvinZH和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.


转载于:https://www.cnblogs.com/AlvinZH/p/8673493.html

(1)普通用户端(全平台) 音乐播放核心体验: 个性化首页:基于 “听歌历史 + 收藏偏好” 展示 “推荐歌单(每日 30 首)、新歌速递、相似曲风推荐”,支持按 “场景(通勤 / 学习 / 运动)” 切换推荐维度。 播放页功能:支持 “无损音质切换、倍速播放(0.5x-2.0x)、定时关闭、歌词逐句滚动”,提供 “沉浸式全屏模式”(隐藏冗余控件,突出歌词与专辑封面)。 多端同步:自动同步 “播放进度、收藏列表、歌单” 至所有登录设备(如手机暂停后,电脑端打开可继续播放)。 音乐发现与管理: 智能搜索:支持 “歌曲名 / 歌手 / 歌词片段” 搜索,提供 “模糊匹配(如输入‘晴天’联想‘周杰伦 - 晴天’)、热门搜索词推荐”,结果按 “热度 / 匹配度” 排序。 歌单管理:创建 “公开 / 私有 / 加密” 歌单,支持 “批量添加歌曲、拖拽排序、一键分享到社交平台”,系统自动生成 “歌单封面(基于歌曲风格配色)”。 音乐分类浏览:按 “曲风(流行 / 摇滚 / 古典)、语言(国语 / 英语 / 日语)、年代(80 后经典 / 2023 新歌)” 分层浏览,每个分类页展示 “TOP50 榜单”。 社交互动功能: 动态广场:查看 “关注的用户 / 音乐人发布的动态(如‘分享新歌感受’)、好友正在听的歌曲”,支持 “点赞 / 评论 / 转发”,可直接点击动态中的歌曲播放。 听歌排行:个人页展示 “本周听歌 TOP10、累计听歌时长”,平台定期生成 “全球 / 好友榜”(如 “好友中你本周听歌时长排名第 3”)。 音乐圈:加入 “特定曲风圈子(如‘古典音乐爱好者’)”,参与 “话题讨论(如‘你心中最经典的钢琴曲’)、线上歌单共创”。 (2)音乐人端(创作者中心) 作品管理: 音乐上传:支持 “无损音频(FLAC/WAV)+ 歌词文件(LRC)+ 专辑封面” 上传,填写 “歌曲信息
### LeetCode46题 Python 实现 以下是针对LeetCode46题——Permutations的Python解决方案。该问题的核心在于生成给定列表的所有可能排列组合。 #### 方法一:回溯法 (Backtracking) 通过递归的方式构建每一种排列的可能性,使用`set()`或者布尔数组来标记已经使用的元素,从而避免重复计算。 ```python class Solution: def permute(self, nums: list[int]) -> list[list[int]]: result = [] def backtrack(path, remaining): if not remaining: # 如果剩余元素为空,则当前路径即为一个完整的排列 result.append(path[:]) return for i in range(len(remaining)): current_num = remaining[i] next_remaining = remaining[:i] + remaining[i+1:] path.append(current_num) # 将当前数字加入路径 backtrack(path, next_remaining) # 继续处理剩下的数字 path.pop() # 回溯,移除最后一个数字 backtrack([], nums) return result ``` 此方法的时间复杂度为O(N!),其中N为输入列表长度[^1]。 --- #### 方法二:插入法 (Insertion Method) 另一种思路是从空列表开始逐步扩展,每次将新数字插入已有排列的不同位置形成新的排列集合。 ```python class Solution: def permute(self, nums: list[int]) -> list[list[int]]: permutations = [[]] for num in nums: new_permutations = [] for permutation in permutations: for i in range(len(permutation) + 1): new_permutation = permutation[:i] + [num] + permutation[i:] new_permutations.append(new_permutation) permutations = new_permutations return permutations ``` 这种方法同样具有时间复杂度O(N!),但在某些场景下更直观易懂[^2]。 --- #### 方法三:DFS (Depth First Search) 利用深度优先搜索的思想,在每一层递归中选取尚未被固定的元素作为当前位置候选者,并继续向下探索直至完成整个序列的选择过程。 ```python class Solution: def permute(self, nums: list[int]) -> list[list[int]]: def dfs(ans, nums, path): if len(nums) == 0: ans.append(path) return for i in range(len(nums)): dfs(ans, nums[:i] + nums[i+1:], path + [nums[i]]) answer = [] dfs(answer, nums, []) return answer ``` 这种方式简洁明了,易于理解和实现[^3]。 --- ### 总结 以上三种算法均能有效解决LeetCode46题的要求,具体选择取决于个人偏好以及实际应用场景下的性能考量因素。无论是采用显式的状态维护还是隐含的状态传递机制,这些方案都能够满足题目对于不重复全排列生成的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值