排列汇总

排列汇总

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

组合

1.位计算来实现所需的组合:

。即。将每一个组合与一个二进制数相应起来。枚举二进制的同一时候,枚举每一个组合。如字符串:abcde,则有


00000———null


00001———a


00010 ——–b


00011———ab


00100———c


… …

11111——–abcde


给出程序例如以下所看到的:

#include <stdio.h>
#include <string.h>

void printCombination(char* str, int i);
void combination(char* str)
{
	int len = strlen(str);
	//共同拥有(1<<len)个组合,当中有一次什么都不打印外
	for(int i=0; i< (1<<len); ++i)
	{
		printCombination(str, i);
		printf("\n");
	}
}

void printCombination(char* str, int i)
{
	int len = strlen(str);
	for(int j=0; j<len; ++j)
	{
		//看s中哪些位为
		int s = i&(1<<j);
		if(s)
			printf("%c", str[j]);
	}
}	

int main()
{
	char str[] = "abc";
	combination(str);
}

2.递归的思路解决

#include <stdio.h>
#include <string.h>

void printCombination(char* str, int len, int m, int* arr, const int M);
void combination(char* str)
{
	int len = strlen(str);
	int* arr = new int[len];
	for(int i=1; i<=len; ++i)
	{
		printCombination(str, len, i, arr, i);
	}
	delete[] arr;
}
//从n中选m个数进行组合
/**************************************
a. 首先从n个数中选取编号最大的数,然后在剩下的n-1个数里面选取m-1个数,
直到从n-(m-1)个数中选取个数为止。
b. 从n个数中选取编号次小的一个数,继续运行步。直到当前可选编号最大的数为m。
******************************************/
void printCombination(char* str, int len, int m, int* arr, const int M)
{
	for(int i=len; i>=m;--i)
	{
		//依次选择编号最大的数,次大的数....
		arr[m-1] = i-1;
		if(m>1)
		{
			//选择的数目大于。从剩余的i-1个数中选取m-1个数的组合
			printCombination(str,i-1,m-1,arr,M);
		}
		else
		{
			//打印M个数字
			for(int j=M-1; j>=0; --j)
			{
				printf("%c ", str[arr[j]]);
			}
			printf("\n");
		}
	}
}	

int main()
{
	char str[] = "abcd";
	combination(str);
	return 0;
}

3.非递归实现

首先,初始化一个n个元素的数组(所有由0。1组成),将前m个初始化为1。后面的为0。这时候就能够输出第一个组合了。

为1的元素的下标所相应的数。

算法開始:从前往后找,找到第一个10组合,将其反转成01。然后将其前面的所有1,所有往左边推。即保证其前面的1都在最左边。然后就能够依照这个01序列来输出一个组合结果了。
而假设找不到10组合。就表示说全部情况都输出了,为什么?你想,(以n=5,m=3为例)一開始是11100,最后就是00111。已经没有10组合了。

这样的将问题转换为01序列(也就是真假序列)的想法值得我们考虑和借鉴。
比如求5中选3的组合:
1 1 1 0 0 //1,2,3
1 1 0 1 0 //1,2,4
1 0 1 1 0 //1,3,4
0 1 1 1 0 //2,3,4
1 1 0 0 1 //1,2,5
1 0 1 0 1 //1,3,5
0 1 1 0 1 //2,3,5
1 0 0 1 1 //1,4,5
0 1 0 1 1 //2,4,5
0 0 1 1 1 //3,4,5

实现代码例如以下:

#include <stdio.h>   
#include <stdlib.h>   
#include <string.h>   
   
int l=0;   
//function definition   
void composition(const char [],int,int);    
void printCompostion(const char[],const bool[],int);   
  
//function implementation   
void printCompostion(const char source[],const bool comp[],int n){   
	int i;   
    for (i=0;i<n;i++)    
		if (comp[i]==true) printf ("%c-",source[i]);   
    printf ("\n");   
   
}   
   
void compostion(const char* source,int n,int m){   
       
    bool * comp = (bool*)malloc(n*sizeof(bool));   
       
	int i;   
	for (i=0;i<m;i++) comp[i]=true;   
    for (i=m;i<n;i++) comp[i]=false;   
	   
    printCompostion(source,comp,n);   
	    l++;   
   
	    while(true){   
	           
			for (i=0;i<n-1;i++)    
	            if (comp[i]==true&&comp[i+1]==false) break;   
	           
			if (i==n-1) return;  //all the compostion is found out   
           
	        comp[i]=false;   
	        comp[i+1]=true;   
           
			int p=0;   
			while (p<i){   
	            while (comp[p]==true) p++;   
				while (comp[i]==false) i--;   
				if (p<i) {   
					comp[p]=true;   
					comp[i]=false;   
	            }   
			}   
	        printCompostion(source,comp,n);   
       l++;   
    }   
}   
  
  
//test function   
void testCompostion(){   

	char* testcase = "abcdefghijklmno";   
	int n=strlen(testcase);   
    int m=7;   
	compostion(testcase,n,m);   
}   	   
//main function   
void main(){   
	testCompostion();   
	printf ("total=%d\n",l);   
}   

全排列:

1. 递归

分治算法:这个算法利用了分而治之的思想。

我们先从2个数開始,比方说4,5。他们的全排列仅仅有两个45和54。假设在前面加个3,那么全排列就是345,354,也就是3(54),括号表示里面的数的全排列。当然还有4(35),5(34)…写到这里,各位看官应该已经看出点门道了吧。

三个数的全排列,能够分为三次计算。第一次计算3和(45)的全排列。第二次计算4和(35)的全排列…..也就是说,将序列第一个元素分别与它以及其后的每个元素代换,得到三个序列,然后对这些序列的除首元素外的子序列进行全排列。

思想事实上还是挺简单的:
代码实现例如以下:

#include <stdio.h>   
#include <string.h>   
#include <stdlib.h>   
   
#define LENGTH 27   
   
int n=0;   
   
void permute(int[],int,int);   
void swapint(int &a,int &b);   
void printIntArray (int[],int);   

//Function Implementation   
   
void swapint(int &a,int &b){   
	int temp;   
    temp = a;   
    a = b;   
    b = temp;   
}   
   
void printIntArray(int target[],int length){   
    int i;   
    for (i=0;i<length;i++) printf ("%d",target[i]);   
    printf ("\n");   
}   
   

void permute(int target[],int begin,int end){   
       
    if (begin==end) {   
        printIntArray(target,end+1);   
        n++;   
        return;   
    }   
    int i;   
    for (i=begin;i<=end;i++){   
       
        swapint(target[begin],target[i]);   
        permute(target,begin+1,end);   
        swapint(target[begin],target[i]);   
   
       
    }   
}   
   
//test Functions   
void testPermute(){   
    int len;   
    scanf ("%d",&len);   
       
    int *testcase =(int *)malloc(len*sizeof(int));   
       
    int i;   
    for (i=0;i<len;i++) testcase[i]=i+1;   
    permute(testcase,0,len-1);   
   
}   
   
//Main Function   
void main(){   
    testPermute();   
    printf ("n=%d",n);   
}  

2. 字典序

有时候递归的效率使得我们不得不考虑除此之外的其它实现,非常多把递归算法转换到非递归形式的算法是比較难的,这个时候我们不要忘记了标准模板库已经实现的那些算法。这让我们非常轻松。STL有一个函数next_permutation(),它的作用是假设对于一个序列。存在依照字典排序后这个排列的下一个排列,那么就返回true且产生这个排列。否则返回false。注意,为了产生全排列,这个序列要是有序的,也就是说要调用一次sort。

直接用STL上的

#include "iostream"
#include "algorithm"
using namespace std;

void permutation(char* str,int length)
{
	sort(str,str+length);	//必须得先排序
	do
	{
		for(int i=0;i<length;i++)
			cout<<str[i];
		cout<<endl;
	}while(next_permutation(str,str+length));

}
int main(void)
{
	char str[] = "acb";
	cout<<str<<"全部全排列的结果为:"<<endl;
	permutation(str,3);
	system("pause");
	return 0;
}

当中next_permutation()的实现例如以下:

template<class BidirectionlIterator>
bool next_permutation(BidirectionalIterator firt, 
					  BidirectionalIterator last)

{
	if(first == last) return false;	//空区间
	BidirectionalIterator i = first;
	++i;
	if(i == last) return false;	//仅仅有一个元素
	i = last;	//i指向尾端
	--i;

	for(;;){
		BidrectionalIterator ii = i;
		--i;
		//以上,锁定一组(两个)相邻元素
		if(*i < *ii)	//假设前一个元素小于后一个元素
		{
			BidrectionalIterator j = last;	//令j指向尾端
			while(!(*i < *--j));	//由尾端往前找,直到遇上比*i大的元素
			iter_swap(i,j);			//交换i,j
			reverse(ii, last);		//将ii之后的元素所有逆向重排
			return true;
		}
		if(i == first)				//进行到最前面了
		{
			reverse(first, last);	//所有逆向重置
			return false;		
		}
	}
}

自己设计函数next_permutation():

#include <stdio.h>
#include <algorithm>
#include <string.h>

using namespace std;
//反转数组
void reverse(char* str, int first, int last)
{
	if(str == NULL || *str == '\0')
		return;

	while(first < last)
	{
		char ch = str[first];
		str[first] = str[last];
		str[last] = ch;
		first++;
		last--;
	}
}
//打印数组
void printfString(char* str)
{
	for(int i=0; i<strlen(str); ++i)
	{
		printf("%c", str[i]);
	}
	printf("\n");
}
//找到下一个数组
bool next_permutation(char* str)
{
	if(str == NULL || *str=='\0')
		return false;

	int len = strlen(str);
	int i = len - 2;
	int ii = i+1;
	int j = len - 1;

	//从后端開始找到第一对str[i]<str[j]的数字
	while(str[i] > str[ii])
	{
		--i;
		--ii;
		//假设i<0,则表示已经所有排列
		if(i<0)
		{
			//所有翻转
			reverse(str, i+1, len-1);
			return false;
		}
	}
	//从后面找到第一个大于str[i]的数字
	while(str[j] < str[i])
	{
		--j;
	}
	
	//交换
	char ch = str[i];
	str[i] = str[j];
	str[j] = ch;
	//翻转j之后的数组
	reverse(str, ii, len-1);
	
	return true;
		
}



int main()
{
	char str[] = "cab";
	int len = strlen(str);
	//必须先排序
	sort(str, str + len);
	printfString(str);
	//打印全排列
	while(next_permutation(str))
	{
		printfString(str);
	}

	return 0;
}

參见:

http://blog.csdn.net/hackbuteer1/article/details/7462447

http://xiaomage.blog.51cto.com/293990/74094

http://blog.csdn.net/hackbuteer1/article/details/6657435

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/117766.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • flutter开发app_flutter项目

    flutter开发app_flutter项目前段时间Flutter很火,所以在闲暇之余做了一个助学通的Flutter移动端应用,现在开源出来,希望对想要学习Flutter的朋友有所帮助。我大致做个项目介绍:学生签到系统:分java服务端提供

    2022年8月5日
    7
  • 动态迁移_动作迁移

    动态迁移_动作迁移概念在虚拟化环境中的迁移,又分为动态迁移,静态迁移,也有人称之为冷迁移和热迁移,或者离线迁移在线迁移;静态迁移和动态迁移的区别就是静态迁移明显有一段时间客户机的服务不可用,而动态迁移则没有明显的服务暂停时间,静态迁移有两种1,是关闭客户机将其硬板镜像复制到另一台宿主机系统,然后回复启动起来,这种迁移不保留工作负载,2是,两台客户机公用一个存储系统,关闭一台客户机,防止其内存到另一台宿主机,这样做的

    2025年7月26日
    4
  • Matlab中axis函数使用

    Matlab中axis函数使用目录一.语法1.输入参数2.输出参数二.说明三.示例1.设置坐标轴范围2.使用半自动坐标轴范围3.设置多个坐标轴的坐标轴范围4.显示绘图而不显示坐标区背景5.使用紧凑的坐标轴范围并返回值6.更改坐标系的方向7.添加新绘图时保留当前的坐标轴范围axis函数是设置坐标轴范围和纵横比。一.语法axis(limits)axisstyleaxismodeaxisydirectionaxisvisibility

    2022年5月31日
    98
  • 职称计算机模块intern,职称计算机考试模块试题.pdf

    职称计算机模块intern,职称计算机考试模块试题.pdf职称计算机考试模块试题职称计算机考试WORD模块试题2008-02-0819:12职称计算机考试,找点题目看看,也不知道是不是就考这些,顺便给大家分享分享.1、新建一word文档,将Windows剪贴板上的内容粘贴到该Word文档中。2、保存当前文档的版本(不输入版本的备注),并设置关闭文档时自动保存版本。3、请用文档结构图显示当前文档,并设置为蓝底白字。4、请将WORD…

    2022年6月2日
    32
  • CreateFile函数

    CreateFile函数在 include include 的头文件里 HANDLECreate LPCTSTRlpFil 要打开的文件名 nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp DWORDdwDesir 文件的操作属性 nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp DWORDdwShare 文件共享属性 nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp LPSECU

    2025年9月25日
    3
  • centos7 top命令_linux tee命令

    centos7 top命令_linux tee命令top命令Linuxtop命令用于实时显示process的动态。top参数详解第一行,任务队列信息**系统当前时间:**13:52:56**系统开机后到现在的总运行时间:**up66

    2022年7月30日
    12

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号