leetcode-46-permutations 搜索

本文介绍了解决LeetCode 46题——全排列的多种方法,包括深度优先搜索(DFS)策略、利用哈希表进行剪枝优化、采用STL库next_permutation函数等,并探讨了重复元素情况下的解决方案。

问题

题目:[leetcode46]

思路

这个题目要说思路也不难,dfs就好了。每一层试探所有可能的元素,只要有一个满足条件,从这个元素继续dfs就行了。但是也有需要注意的点。

  • 剪枝的条件:当前层不能试探上一层试探过的元素。代码实现:建立哈希表,将当前层试探过的哈希表传递给下一层。
  • 当前层试探注意点:当前曾上一次的试探不能影响这一次的试探。因为每一层本质是只能有一个元素。所以,每一层的不同试探逻辑上是相互独立的。代码实现:由于每一次试探在逻辑上来说是相互独立的。所以,每次试探的哈希表都是上一层传递过来的,且一样。

搜索树如下:
这里写图片描述

代码(visit记录元素是否在排列中)

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        std::vector< std::vector<int> > ret;
        int sz = nums.size();
        if( !sz )
            return ret;

        std::map< int, bool > visit;
        std::vector<int> arr( sz, int() );

        dfs( 0, arr, visit, nums, ret );

        return ret;

    }
private:
    void dfs( int depth, std::vector<int>& arr, std::map< int, bool >& visit, const std::vector<int>& nums, std::vector< std::vector<int> >& ret )
    {
        int sz = nums.size();
        for( int i = 0; i < sz; ++i )
        {
            std::map<int, bool> _visit = visit; // 每一层不同的试探逻辑上相互独立,需要获得上一层哈希表
            if( _visit.find( nums[i] ) == _visit.end() )
            {
                arr[depth] = nums[i];
                _visit[ nums[i] ] = true;

                if( depth == sz - 1 )
                    ret.push_back( arr );
                else
                    dfs( depth + 1, arr, _visit, nums, ret );
            }
        }
    }    
};

测试代码实现:

/*************************************************************************
 > File Name: main.cpp
 > Author: kang
 > Mail:likang@tju.edu.cn 
 > Created Time: 2016年12月17日 星期六 20时53分55秒
 ************************************************************************/
#include <iostream>
#include <vector>
#include <map>

class Solution {
public:
    std::vector<std::vector<int> > permute( std::vector<int>& nums ) {
        std::vector< std::vector<int> > ret;
        int sz = nums.size();
        if( !sz )
            return ret;
        std::vector<int> arr( sz, int() );
        std::map< int, bool > visit;

        dfs( 0, arr, visit, nums, ret );

        return ret;   
    }
private:
    void dfs( int depth, std::vector<int>& arr, std::map< int, bool >& visit, const std::vector<int>& nums, std::vector< std::vector<int> >& ret  ){
        int sz = nums.size();
        for( int i = 0; i < sz; ++i )
        {
            std::map< int, bool > _visit = visit;
            if( _visit.find( nums[i] ) == _visit.end() )
            {
                arr[depth] = nums[i];
                _visit[ nums[i] ] = true;

                if( depth == sz - 1 )
                    ret.push_back( arr );
                else
                    dfs( depth + 1, arr, _visit, nums, ret );

            }
        }


    }
};

void show( const std::vector<int>& nums ){
    int sz = nums.size();
    for( int i = 0; i < sz; ++i )
    {
        std::cout << nums[i] << " ";
    }
    std::cout << std::endl;
}

int main( void )
{
    int nums_arr[] = {1, 2, 3};
    std::vector<int> nums( nums_arr, nums_arr + 3 );
    Solution s;
    std::vector< std::vector<int> > ret = s.permute( nums );

    int sz = ret.size();
    for( int i = 0; i < sz; ++i )
    {
        show( ret[i] );
    }

    return 0;
}

这是我做的第一道搜索题目,我补充一下对于上面的这道题目。如何生成所有的序列,没有排列的概念。元素也可以重复。这应该是最完整的搜索树。
思路也很简单,就是在每个位置试探所有的元素,也不剪纸。

    void dfs1( int depth, std::vector<int>& arr, const std::vector<int>& nums, std::vector< std::vector<int> >& ret  ){
        int sz = nums.size();
        for( int i = 0; i < sz; ++i )
        {
            arr[depth] = nums[i];
            if( depth == sz - 1 )
                ret.push_back( arr );
            else
                dfs1( depth + 1, arr, nums, ret );
        }
    }

用STL库的next_permutation实现,获取当前排列的下一个排列。返回值为true,如果存在下一个排列。

代码1(STL套路)

#include <algorithm>
class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        std::vector< std::vector<int> > ret;
        std::sort( nums.begin(), nums.end() ); // 先排序 - 第一个排列

        do ret.push_back( nums );
        while( std::next_permutation( nums.begin(), nums.end() ) );

        return ret;
    }
};

思路

当我做下一道题目的时候,发现了这种办法的问题。比如[1,1,2]这个序列,当序列中存在重复元素的时候,这种办法就不行了。因为visit数组记录的是这个元素之前是否出现过对于不重复的数组来说,元素出现过就代表着这个位置已经使用了

所以,在求全排列的时候,每一层试探的时候,本质上看的是这个位置,之前是否已经在排列中了,如果不在,可以把这个位置的元素加进来

所以,visit所代表的含义可以是,这个位置的元素是否已经在排列当中。

代码2(visit记录位置是否在排列中)

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        std::vector< std::vector<int> > ret;
        std::vector< int > arr( nums.size(), int() );
        std::set<int> visit;

        dfs( 0, arr, visit, nums, ret );
        return ret;
    }
private:
    void dfs( int depth, std::vector<int>& arr, std::set<int>& visit,
              const std::vector<int>& nums, std::vector< std::vector<int> >& ret ){

                  int sz = nums.size();
                  for( int i = 0; i < sz; ++i )
                  {
                      std::set<int> __visit = visit;
                        if( __visit.find( i ) == __visit.end() )
                        {
                            arr[depth] = nums[i];
                            __visit.insert( i ); // insert the location of nums[i]

                            if( sz-1 == depth )
                                ret.push_back( arr );
                            else
                                dfs( depth+1, arr, __visit, nums, ret );
                        }

                  }


              }    
};

思路2

把每个数与他后面的数交换,即可。
注意接口设计,设计n的目的是为了避免当给出char* p = “abc”;这种形式的情形。通用。

代码2

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        int sz = nums.size();
        vector<vector<int>> ret;
        permute( nums, 0, sz, ret );
        return ret;
    }
private:
    void permute(vector<int>& nums, int depth, int n, vector<vector<int>>& ret){
        if(depth == n-1) ret.push_back(nums);
        else{
            for(int i = depth; i < n; ++i){
                swap(nums[depth], nums[i]);
                permute(nums, depth+1, n, ret);
                swap(nums[depth], nums[i]);
            }
        }
    }
private:
    void swap(int& a, int& b){
        int t = a;
        a = b;
        b = t;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值