字典序法实现全排列

        参考:http://yangyou230.iteye.com/blog/1307884        http://ashaochangfu.blog.163.com/blog/static/10425173020115217189184/

        全排列的生成算法就是对于给定的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来

        现在说如果有一个字符串,那么对于这个字符串中的所有单个的字符,这个字符串就是这些字符的一个排列喽!那么,我们现在的目标是把这个字符串中的字符的所有可能的排列列举出来。

        用字典序法得到全排列的思路大概是这样的:我们需要有一个初始的排列状态,对于这个排列,用字典序法转换就得到下一个排列。我们把初始状态设置为字符从小到大地排,不断地用字典序法得到它的下一个排列,直到最后一个排列为止,而最后一个排列就是字符从大到小地排喽。比如说5个数字的所有的排列中最前面的是12345,最后面的是54321。
        字典序法的描述如下:

        设P是1~n的一个全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn
      1)从排列的右端开始,找出第一个比右边字符小的字符的序号j(j从左端开始计算),即 j=max{i|pi<pi+1}
      2)在pj的右边的字符中,找出所有比pj大的字符中最小的字符pk,即
      k=max{i|pi>pj}(右边的字符从右至左是递增的,因此k是所有大于pj的字符中序号最大者)
      3)对换pj,pk
      4)再将pj+1......pk-1pkpk+1pn倒转得到排列p''=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,这就是排列p的下一个下一个排列。
      例如839647521是数字1~9的一个排列。从它生成下一个排列的步骤如下:
      自右至左找出排列中第一个比右边数字小的数字4 839647521
      在该数字后的数字中找出比4大的数中最小的一个5 839647521
      将5与4交换 839657421
      将7421倒转 839651247
      所以839647521的下一个排列是839651247。
      839651247的下一个排列是839651274。

        代码如下:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;

public class Test {

	public static void main(String[] args) {

		 long a=System.currentTimeMillis();
		
         String s="1587469320";
         char[] arr=s.toCharArray();
         Arrays.sort(arr);
         List<String> result=new ArrayList<String>();
         result.add(String.valueOf(arr));
        while(lowPos(arr)!=-1){
        	int i=lowPos(arr);
            int j=minInMaxThanPos(arr, i);
            swap(arr, i, j);
            reverseAfterI(arr, i);
            result.add(String.valueOf(arr));
         }
        System.out.println(result.size());
        long b=System.currentTimeMillis();
        System.out.println("用时:"+String.valueOf(b-a));
       // for(String e:result)
        	//System.out.println(e);                 
	}
	
	//从排列的右端开始,找出第一个比它的右边的字符(紧挨着它的)小的字符的序号
	//比如在"839647521" 自右至左找出排列中第一个比右边数字小的数字是'4' ,返回它的下标4
	public static int lowPos(char[] arr){
		
		int i;
		for(i=arr.length-1;i>=1;i--)
			if(arr[i-1]<arr[i])
				break;
		return i-1;
		
	}
	
	//在下标index的右边的字符中,找出所有比arr[index]大的字符中的最小的字符的下标
	//比如在"839647521" index=4时,arr[4]='4',
	// 在'4'右边所有大于'4'的字符中的最小者是'5',返回'5'的下标6
	public static int minInMaxThanPos(char[] arr,int index){
		
		Hashtable< Character,Integer> ht=new Hashtable< Character,Integer>();
		ArrayList<Character> tmp =new ArrayList<Character>();
		int j=0;
		for(int i=index+1;i<arr.length;i++)
			if(arr[i]>arr[index])
				{
				  ht.put(arr[i],i);
				  tmp.add(arr[i]);
				 
				}
		if( ht.isEmpty())
			return -1;
		
		Collections.sort(tmp);
		return  ht.get(tmp.get(0));
		
	}
	//交换
	public static void swap(char[] arr,int i,int j){
		
		char tmp=arr[i];
		arr[i]=arr[j];
		arr[j]=tmp;
	}
	// arr 在 index之后的元素反转
	//比如在"839647521" 在index=6之后的元素反转是:"839647512"
	public static void reverseAfterI(char[] arr,int index){
		
		int j=arr.length-1;
		//arr 在 index之后的元素的长度
		int len=arr.length-1-(index+1)+1;
		for(int i=index+1;i<=index+len/2;i++)
			swap(arr,i,j--);
	}
}

        好家伙,10个数字的全排列在我这台破机子上运行了14 秒钟,总共有3628800 个。把 s 改成 “132” ,运行结果:

result.size()=6
用时:0
123
132
213
231
312
321
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值