排列和组合

人生的第一次面试,一家做算法的小公司,面试官人挺好,但我发挥的太差了……
前面几到和数据库有关的业务题,就先不说了,哎。
有一道看似很简单的算法题,竟然做不出来……


看到这题,我首先想到了剑指offer的一道题目:

  • 求字符的排列
  • 题目描述
    输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
  1. 第一步交换第一个字符和剩余所有字符;
  2. 第二步固定第一个字符,求剩余字符的全排列;(递归)
class Solution {
public:
    vector<string> Permutation(string str) {
        vector<string>ans;
        if (str.size()==0)return ans;
        dp(ans,str,0);          //   & *  ?? 
        sort(ans.begin(),ans.end()) ;
        return ans;
        
    }
    void dp(vector<string>&ans,string str,int begin) 
    {
    	if (begin==str.size())
    	{
    		ans.push_back(str);   
            return ;
		}
		
		for (int i =begin;i<str.size();i++)
		{
            if (i!=begin && str[i]==str[begin])continue;  //考虑重复!!
			swap(str[begin],str[i]);
			
			dp(ans,str,begin+1);
			
			swap(str[begin],str[i]);
			
		}
		
	}
};
  1. 组合
    参考:
    https://blog.csdn.net/Hackbuteer1/article/details/7462447
class zh{
	public:
		vector<string>zuhe(string str)
		{
			vector<string>ans;
			vector<char>tmp;
			if (str.size()==0)return ans;
			
			for (int i=1;i<=str.size();i++)
			{
				dp(ans,tmp,str,0,i);
			}
			return ans;
			
		}
		void dp(vector<string>&ans,vector<char>&tmp,string str,int begin,int num)  // num : the number of combion 
		{

			if (num==0 )
			{
				string s = "";
				for (int i=0;i<tmp.size();i++)
				{
					s+=tmp[i];
				}

				ans.push_back(s);
				return;
			}
			
			if (begin==str.size())return ;
			
			tmp.push_back(str[begin]);
			dp(ans,tmp,str,begin+1,num-1);
			tmp.pop_back();
			dp(ans,tmp,str,begin+1,num);
			
		}
}; 

位运算,所有1,0的组合,即为所有字符的组合

class wei{
	public:
		vector<string>zuhe(string str)
		{
			vector<string>ans;
			set<string>se;
			if (str.size()==0)return ans;
			
			for (int i=1;i<(1<<str.size());i++)
			{
				mj(se,i,str);         // 2 ** (n-1) 
			}
			
			for (set<string>::iterator it= se.begin();it!=se.end();it++ )
			{
				ans.push_back(*it);
			}
			
			return ans;
			
		}
		void mj(set<string>& ans, int n,string str)
		{
			string tmp = "";
			for (int i=0;i<str.size();i++)
			{
				if ((1<<i)&n)
				{
					tmp+=str[i];
//					cout<<"--"<
				}
			}
			ans.insert(tmp);
		}
};

leetcode 一道组合题目,规定了返回组合数的长度。

Input: n = 4, k = 2
Output:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

目前只是在之前位运算的基础上加了一个判断条件,复杂度依然为O(n**2),后面再优化吧

class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        vector<vector<int> >ans;
        for (int i=1;i<(1<<n);i++)
        {
            vector<int>v;
            for (int j=0;j<n;j++)
            {
                if ((1<<j)&i)
                {
                    v.push_back(j+1);
                }
            }
            if (v.size()==k)
            {
                ans.push_back(v);
            }
        }
        return ans;
    }
};

对于结构体

总结起来,排列,从第一个数开始,后面每个数和其交换,固定第一个数,递归的作用于后面的子序列;终止条件,begin==str.size()-1;
组合,外层的一个for循环决定要组合多少个数;对于每个固定的数,从0开始,判断当前数是否加入进去。 终止条件,number = 0 , 注意 begin 不要越界。

#include
#include
#include
using namespace std;


struct node{
	char name;
	int number;
	 node(char s,int n):name(s),number(n){
	}
};

bool cmp(struct node a,struct node b)
{
	return a.number<b.number;
}

class  solve_bt{
	public:
		vector<vector<node> >pl(vector<node>str)
		{
			vector<vector<node> >ans;
			
			if (str.size()==0)return ans;
			
			dg(ans,str,0);
			
			return ans;
		}
		
		void dg(vector<vector<node> >&ans,vector<node>str,int begin)
		{
			if (begin==str.size()-1)
			{
//				sort(str.begin(),str.end(),cmp);
				ans.push_back(str);
				return ;
			}
			
			for (int i=begin;i<str.size();i++)
			{
				if (i!=begin && str[i].name==str[begin].name)continue;  
				
				swap(str[begin],str[i]);
				dg(ans,str,begin+1)	;
				swap(str[begin],str[i]);
			}
			
		}
		
};

class solve_zh{
	
	public:
		vector<vector<node> >zh(vector<node>str)
		{
			vector<vector<node> >ans;
			
			if (str.size()==0)return ans;
			
			vector<node>tmp;
			for (int i=1;i<=str.size();i++)
			{
				dg(ans,tmp,str,0,i);
			}
			return ans;
			
		}
		void dg(vector<vector<node> >&ans,vector<node>&tmp,vector<node>str,int begin,int number)
		{
			if (number==0)
			{
				sort(tmp.begin(),tmp.end(),cmp);
				
//				for (int i=0;i
//				cout<
				ans.push_back(tmp);
				return ;
			}
			
			if (begin==str.size())return ;
			
			tmp.push_back(str[begin]);
			dg(ans,tmp,str,begin+1,number-1);
			tmp.pop_back();
			dg(ans,tmp,str,begin+1,number);
			
		}
};

int main()
{
	node a('a',2);
	node b('b',1);
	node c('c',3);
	vector<node>str;
	str.push_back(a);
	str.push_back(b);
	str.push_back(c);
	
	swap(str[0],str[1]);
	
	for (int i=0;i<str.size();i++)
	{
		cout<<str[i].name<<endl;
	}
	
	cout<<"---"<<endl;
	
//	solve_bt bt;
//	vector >ans= bt.pl(str);

	solve_zh z;
	vector<vector<node> >ans = z.zh(str);
	
	for (int i=0;i<ans.size();i++)
	{
		for (int j=0;j<ans[i].size();j++)
		{
			cout<<ans[i][j].name;
		}
		cout<<endl;
	}
	
	return 0; 
} 

----------------更------------
关于,排列,如果有重复元素,我上面的解法应该是错的 ,参考:
https://www.cnblogs.com/grandyang/p/4359825.html
https://leetcode.com/problems/permutations-ii/description/

暂时还是用 set吧……

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        set<vector<int> >ans;
        
        int n = nums.size();
        
        // if (n==0)return ans;
        
        solve(ans,nums,0,n);
        return vector<vector<int> >(ans.begin(),ans.end());
        
        
    }
    
    void solve(set<vector<int> >&ans,vector<int>&nums,int cur,int n)
    {
        if (cur==n)
        {
            // ans.push_back(nums);
            ans.insert(nums);
            return ;
        }
        
        for (int i=cur;i<n;i++)
        {
            if (i!=cur && nums[i]==nums[cur])continue;
            
            swap(nums[cur],nums[i]);
            solve(ans,nums,cur+1,n);
            swap(nums[cur],nums[i]);
            
        }
        return;
        
    }
    
};

关于组合,还有另外一种dfs的做法,形式上和排序相似,注意,push(i) 以及 solve(i+1)的做法:

class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        
        vector<vector<int> >ans;
        
        vector<int>tmp;
        
        if (n<=0|| k<=0)return ans;
        
        solve(ans,tmp,1,k,n);
        
        return ans;
        
    }
    
    void solve(vector<vector<int> >& ans,vector<int>& tmp,int cur,int k,int n)
    {
        
        if (tmp.size()==k)
        {
            ans.push_back(tmp);
            return ;
        }
        
        for (int i=cur;i<=n;i++)
        {
        // if (cur>n)return;
            tmp.push_back(i);
            
            solve(ans,tmp,i+1,k,n);
            
            tmp.pop_back();
            
            // solve(ans,tmp,cur+1,k,n);
            
        }
        return;
        
    }
-------------------
剪枝的用法: 每次能用的数 和剩余的k有关
void solve(vector<vector<int> >& ans,vector<int>& tmp,int cur,int k,int n)
    {
        
        if (k==0)
        {
            ans.push_back(tmp);
            return ;
        }
        
        for (int i=cur;i<=n-k+1;i++)
        {
        // if (cur>n)return;
            tmp.push_back(i);
            
            solve(ans,tmp,i+1,k-1,n);
            
            tmp.pop_back();
            
            // solve(ans,tmp,cur+1,k,n);
            
        }
        return;
        
    }
    
    
    
};

leetcode上一道算是组合的题吧,注意题中要求的是,组合出一组数(改组合数是可以重复用的),其和为某值。
https://leetcode.com/problems/combination-sum/

class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int> >ans;
        vector<int>tmp;
        
        // int n = candidates.size();
        // if (n<=0)return ans;
        
        // for (int i=1;i<=n;i++)
        // {
        solve(ans,tmp,candidates,0,target);
        // }
        
        return ans;
        
        
    }
    
    void solve(vector<vector<int> >&ans,vector<int>&tmp,vector<int>&candidates,int cur,int target)
    {
        // if (target<0)return;
        if (target==0)
        {
          ans.push_back(tmp);
        }
        
        else if (target>0)
        {
        for (int j=cur;j<candidates.size();j++)
        {
            if (candidates[j]<=target)
            {
                
            tmp.push_back(candidates[j]);
            solve(ans,tmp,candidates,j,target-candidates[j]);
            tmp.pop_back();
                
            }
        }
        
//         if (cur>=n)return;
        
//         tmp.push_back(candidates[cur]);
        
//         solve(ans,tmp,candidates,cur+1,k-1,n,target);
        
//         tmp.pop_back();
        
//         solve(ans,tmp,candidates,cur+1,k,n,target);
        
        }
        
    }
    
};

https://leetcode.com/problems/combination-sum-ii/
组合二。有两个地方,一个是不能重复利用数组的数,那就从 i+1 递归;
二是数组中有重复的数,那可以先排序,然后 在for循环的开始,判断是否和之前重复

class Solution {
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        
        vector<vector<int> >ans;
        
        vector<int>tmp;
        
        sort(candidates.begin(),candidates.end());
        
        solve(ans,tmp,candidates,0,target);
        
        return ans;
        
    }
    
    
    void solve(vector<vector<int>>&ans,vector<int>&tmp,vector<int>& candidates,int cur,int target)
    {
        if (target==0)
        {
            ans.push_back(tmp);
        }
        
        else if (target>0)
        {
            for (int i=cur;i< candidates.size();i++)
            {
                if (i>cur && candidates[i]==candidates[i-1])continue;
                
                tmp.push_back(candidates[i]);
                solve(ans,tmp,candidates,i+1,target-candidates[i]);
                tmp.pop_back();
            }
            
            
        }
        
    }
};

你可能感兴趣的:(数据结构和刷题)