zoukankan      html  css  js  c++  java
  • LeetCode Notes_#46_全排列

    LeetCode Notes_#46_全排列

    Contents

    题目

    给定一个 没有重复数字的序列,返回其所有可能的全排列。
    示例:

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

    思路分析

    这题的思路是回溯法,或者说DFS深度优先遍历。因为全排列本身就是一种遍历。遍历的就是若干个数字不同顺序的所有可能性。
    这个问题可以被抽象为一个决策问题,比如对于123三个数字进行排序,相当于做3次决策,可以画出一个决策树

    在这棵树的每个节点位置,有两个属性:

    1. 选择列表:可以选择没有之前没选择过的所有数字作为下一个数字。
    2. 路径:从根节点到当前节点,做出的所有选择组成一条路径,也就是一个全排列。

    那么求所有全排列的问题可以转化为遍历这一棵决策树的问题,并且是深度优先遍历。所谓深度优先遍历,有一个很形象的描述:“不撞南墙不回头”,从根节点开始,我们一路向下遍历,直到遇到最底层,不能再往下遍历了,就回头(即结束递归,开始回溯)。

    这个问题与普通的树的DFS遍历又有所不同:

    1. 普通的DFS遍历是按照深度优先的顺序输出一个个的节点,但是这题需要的是输出一条条路径。所以我们维护了一个track变量来记录路径。
    2. 由于track变量是作为dfs方法的参数传入,而且track是一个引用变量,所以实际上是所有递归调用函数所共用的。所以我们在保存结果之后,需要进行一步撤销选择的操作,在树中,这一步操作体现为,从当前节点返回到上一层。

    下图体现了这一题的整个流程,可以参考。

    解答

    class Solution {
        List<List<Integer>> res = new LinkedList<>();
        public List<List<Integer>> permute(int[] nums) {
            LinkedList<Integer> track = new LinkedList<>();
            dfs(nums, track);
            return res;
        }
    
        private void dfs(int[] nums, LinkedList<Integer> track){
            //终止条件,当前路径的长度等于数组长度,说明已经是一个全排列,或者说在树中已经遍历到最底层了
            if(track.size() == nums.length){
                //为什么需要使用new?
                //因为这里的track是一个LinkedList的引用变量,自始至终引用的都是同一个内存空间的内容
                //得到一个结果后,需要将其复制到另一块内存空间保存,否则在“撤销选择”的过程中,这个track内容就变化了
                res.add(new LinkedList(track));
                return;
            }
            for(int i = 0;i <= nums.length - 1;i++){
                //可以选择之前没有选过的所有节点
                if(track.contains(nums[i])) continue;
                track.add(nums[i]);
                //开启递归调用,一条路走到底
                dfs(nums, track);
                //撤销选择,回溯到上一层
                track.removeLast();
            }
           
        }
    }

    复杂度分析

    分析过程比较复杂,参考 回溯算法入门级详解 + 练习
    时间复杂度:O(n*n!)
    空间复杂度:O(n*n!)

    参考

  • 相关阅读:
    数据驱动编程法
    23个设计模式的简明教程
    分享一篇文章C语言字节对齐问题(适用于C++)转载至http://blog.csdn.net/21aspnet/article/details/6729724
    关于C++类中访问权限的若干疑问(虚函数访问权限)
    利用C# 反射设计支持可扩展插件的应用程序
    隐藏控制台console application窗口
    Intellij IDEA社区版上新建项目或模块没有Spring Initializr选项解决办法
    mac jmeter 界面乱码
    windows 查看端口被占用进程
    php static 变量声明
  • 原文地址:https://www.cnblogs.com/Howfars/p/13617959.html
Copyright © 2011-2022 走看看