全排列算法学习

递归方式实现全排列:图片资源来自于网络(递归顺序类似树的后序遍历)
在这里插入图片描述
代码实现

	/*****************
	 * 将字符串的组成元素进行全排列 后输出
	 * @param str 输入的字符串
	 * @return 所有的全排序序列
	 */
    public ArrayList<String> Permutation(String str) {
    	//输出的结果集
    	ArrayList<String> result=new ArrayList<>();
    	//非空判断
    	if(str==null||str.length()==0)
    	{
    		return result;
    	}
    	//调用递归全排序算法
    	allRangeRecu(str.toCharArray(), 0, str.length()-1, result);
    	//字典序排序
		 Collections.sort(result);
		 //返回结果集
		 return result;
    }
    /*************
     * 对数组中的元素进行全排序 排序序列保存在ArrayList中
     * @param array 进行全排序的元素序列
     * @param start 序列开始位置
     * @param end 序列结束位置
     */
    private void allRangeRecu(char[] array,int start,int end,ArrayList<String> result)
    {
    	if(start==end)//如果只剩下一个元素 则固定此序列
    	{
    		String curOrder=String.valueOf(array);//保存当前的序列
    		if(!result.contains(curOrder))//去重 只有当前的字符串不存在结果集中时才添加
    		{
    			result.add(curOrder);
    		}
    	}
    	else 
    	{
    		for(int i=start;i<=end;i++)
        	{
        		char temp=array[i];
        		array[i]=array[start];
        		array[start]=temp;
        		
        		allRangeRecu(array, start+1, end, result);
        		
        		temp=array[i];
        		array[i]=array[start];
        		array[start]=temp;
        	}
		}
    }

非递归方式实现全排序
非递归求解的过程就是一个找序列的下一序列的过程
这对初始的序列有要求,需要满足是按照升序或者降序排列的。
方法的基本流程为:

  • 首先,将所有的元素进行排序(假设初始按照升序排列)。
  • 然后,当所有的元素没有完全变成另一种排序方式时,执行循环。
  • 从数组尾节点位置向前寻找,找到相邻两个位置中前一位置元素小于后一元素的位置,记录前一位置为leftIndex。如果没找到,则说明序列已经变成降序,查找序列完成。
  • 从leftIndex+1开始,向后寻找最后一个大于array[leftIndex]的值,记录此位置为rightIndex。
  • 交换两个位置的元素
  • 将原数组从leftIndex+1位置开始,转置后面的数组。
  • 将转置后的结果添加到结果集中。重新开始循环。
    算法过程如图:
    在这里插入图片描述
    最后为什么要进行转置?
    为了保证下一个排序是恰好比当前排列大的一个序列。每次两个元素交换后,都会将一个较大的元素移到前面(leftIndex位置)去,而从leftIndex+1的位置向后是一个递减的序列,因为移动到前面去一个大的值,高位变大,低位要调整为最小的(升序排列),才能保证当前的序列恰好比之前的序列大。
    代码实现如下
/***********************
 * 使用非递归方式对字符串进行字典序排序
 * @param array 需要进行排序的数组
 * @param start 数组开始位置
 * @param end  数组结束位置
 * @param result 排序的结果集
 */
    public void allRange(char[] array,int start,int end,ArrayList<String> result)
    {
    	//将数组排序为有序的
    	Arrays.sort(array);
    	result.add(String.valueOf(array));
    	//循环查找所有元素的下一元素
    	//这是一个从123到321的过程
    	while(true)
    	{
    		int leftIndex=end;//从尾部向前找第一个array[i-1]<array[i]的位置
    		while(leftIndex>=1&&array[leftIndex-1]>=array[leftIndex])
    		{
    			//从右边数第一个小右侧元素的下标加1
    			leftIndex--;
    		}
    		//序列已经变成降序排列时 退出循环
    		if(leftIndex==0)
    		{
    			break;
    		}
    		leftIndex--;
    		int rightIndex=leftIndex+1;//查找右侧交换元素
    		//从查找到的左侧元素的下一位开始 
    		//找到最后一位大于array[leftIndex]的位置
    		while(rightIndex<=end&&array[rightIndex]>array[leftIndex])
    		{
    			rightIndex++;
    		}
    		rightIndex--;
    		//交换两位置的元素
    		char temp=array[leftIndex];
    		array[leftIndex]=array[rightIndex];
    		array[rightIndex]=temp;
    		//转置出前缀之外的数组
    		reverseArray(array, leftIndex+1, end);
    		//结果添加到数组中
    		result.add(String.valueOf(array));
    	}
    }
    /****************
     * 转置数组内指定区域内的元素
     * @param array 需要进行转置的数组
     * @param start 需要进行转置数组的开始位置
     * @param end 结束位置
     */
    private void reverseArray(char[] array,int start,int end)
    {
    	if(start>=end)
    	{
    		return;
    	}
    	while(start<end)
    	{
    		char temp=array[start];
    		array[start]=array[end];
    		array[end]=temp;
    		start++;
    		end--;
    	}
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值