28-字符串全排列问题

问题描述

打印给定字符串的所有排列情况。
例如 输入字符串‘abc’,则打印出它的全排列形式:
abc acb bac bca cab cba
【注意】写算法时要考虑到有相同元素的情况:
输入字符串‘aab’,它的全排列打印应该为 aab aba baa
所以,设计的算法应该对没有重复元素有重复元素这两种情况有区分

方法一:递归实现—通俗易懂

1)把字符串分成两部分:一部分是字符串第一个字符,另一部分是第一个字符以后的所有字符,接下来求第一个字符以后的所有字符的排列
2)拿第一个字符和它后面的字符逐个交换

递归代码—-推荐此法

/***************************************
Author:tmw
date:2018-7-2
***************************************/
#include <stdio.h>
#include <stdlib.h>

void char_all_permutate( char* str )
{
    /**入参检查**/
    if( str == NULL )
        return;

    permutate( str,str );
}

/**
* 递归全排列
* @param *str
* @param *begin_p  -- 记录排列挪动的位置
*/
#define swap( a,b,t ) (t=a,a=b,b=t)
void permutate( char* str, char* begin_p )
{
    if( *begin_p == '\0' ) /**begin_p是指针**/
        printf("%s\n",str);
    else
    {
        char* p;
        for( p=begin_p; p != '\0'; p++ )
        {
            char temp;
            swap( *begin_p,*p,temp );
            permutate(str,begin_p+1);
            swap( *begin_p,*p,temp );//还原之前的状态
        }
    }
}

方法二:递归实现

先确定一个数作为排列的第一个字符,然后对剩余的字符进行全排列,经过层层递归,从右到左慢慢覆盖所有的全排列情况。

递归代码

/************************
Author:tmw
date:2017-11-9
************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
/**递归算法实现**/
//#define swap(x,y,t) (t=x,x=y,y=t)---宏定义方法
void swap( char* array , int i , int j ) //---函数实参传递方法
{
    char temp;
    temp = array[i];
    array[i] = array[j];
    array[j] = temp;
}
bool can_swap( char *array , int i , int left );
void char_all_permutate(char array[] , int left , int right )
{
    int i;
    if( left == right )
        printf("\t %s\n",array);

    for( i = left ; i <= right ; i++ )
    {
        if(can_swap(array,left,i))
        {
            swap(array,i,left);
            //对i位置以后的元素进行全排序,如此递归
            char_all_permutate(array,left+1,right);
            //返回上一层的同时恢复上一层序列排序,以防i叠加后,进入第一个swap时,又和之前相同的元素调换了,打印相同交换结果
            //---真正实现与i位置元素轮流调换的功能
            swap(array,i,left);
        }
    }
}
//以上子函数虽能实现对字符数组的全排列,但是如果排列元素中有重复的元素,此全排列算法会失效
//因为此算法没有对元素判重区分后再交换,因此,优化此算法的点在于,优化swap,对swap增加一个元素判重的判定

//元素非重复,返回true,可交换
bool can_swap( char *array , int left , int i )
{
    //由于i起始位置是left,因此在判断待交换的两个元素是否需要交换时应避免i=left的情况
    int kk = left;
    for( kk = left ; kk < i ; kk++)
        if( array[kk] == array[i] && i != kk )
            return false;
    return true;
}

方法二:字典序全排列

字典序排列算法

给定两个偏序集A和B,(a,b)和(a′,b′)属于笛卡尔积A×B,则字典序定义为(a,b) ≤ (a′,b′) 当且仅当 a小于a′ 或 (a=a′ 且b≤b′).
1)如果给定的两个等长字符串,从起点开始将它们对应的字符逐个比较,则先出现较小字符的字符串字典序小
2)如果给定的是两个非等长字符串,如果字符一直相等,则较短那个字符串的字典序小

例如:“abc”、“abcd”、“acde”、“adbc”
字典序从小到大排列为:”abc”、“abcd”、“acde”、“adbc”

所以,要求出给定任意字符串的全排列,则采用字典序排序算法如下:

算法起点:找到字典序最小的排列1~n,如“12345”
step1:从右至左找寻字符串中第一次出现递减序列的首位字符a[i];
step2:寻找i位置往右的字符中,比a[i]大的所有字符里面最小的字符,记为b[j];
step3:交换a[i]与b[j];
step4:把第i+1位到最后部分的字符翻转,执行此步骤后,i+1到n的字符变成升序排列
算法终点:找到字典序最大的排列n~1,如“54321”,算法结束

字典序排列代码实现

/**字典序排列算法实现**/
/************************
Author:tmw
date:2017-11-12
************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#define swap(x,y,t) (t=x,x=y,y=t)
//算法起点:普通快排找出已给字符串的字典序最小排列
int first_ele_fast_sort( char* array , int low , int high )//注意是针对字符串的快排  char
{
    int target = 0;
    target = array[low];//偷个懒,直接用数组中的第一个元素作为中轴元素

    while( low < high )
    {
        while( low < high && array[high] >= target  )
            high--;
        array[low] = array[high];//此时high位置上的元素为待处理元素

        while( low < high && array[low] <= target   )
            low++;
        array[high] = array[low];//将大于target值的元素放到待处理的high位置上,那么此时,low位置变为待处理
    }
    array[low] = target; //以上,省去交换,变为“挖坑赋值”,降低时间复杂度
    return low;
}
char* fast_sort( char* array , int low , int high )
{
    if( low < high )
    {
        int index;
        index = first_ele_fast_sort(array,low,high);
        fast_sort(array,low,index-1);
        fast_sort(array,index+1,high);
    }
    return array;
}

void reverse( char* array , int start , int end )
{
    while( start < end )
    {
        char temp;
        swap(array[start],array[end],temp);
        start++;
        end--;
    }
}

//字典序排列算法----找到比当前排列字典序大1的新排列
bool dictionary_sort( char* array , int array_len )
{
    //step1:从右至左找寻字符串中第一次出现递减序列的首位字符a[i]
    int i = 0;
    int j = 0;
    for( i = array_len - 2 ; (i >= 0) && array[i] >= array[i+1] ; i-- );//此时i位置上的元素array[i]即为目标元素a

    if( i < 0 )//说明此时已经是最大字典序列
        return false;

    //step2:寻找i位置往右的字符中,比a[i]大的所有字符里面最小的字符,记为b[j];
    for( j = array_len-1 ; ( j > i ) && array[j] <= array[i] ; j-- );
    //for循环后,就找到了第二个目标元素b---array[j]

    //step3:交换a[i]与b[j]
    char temp;
    swap(array[j],array[i],temp);

    //step4:把第i+1位到最后部分的字符翻转
    reverse(array,i+1,array_len-1);
    return true;
}

/**字典序全排列--找出给定字符串按字典序从小到大找出全排列**/
void dictionary_full_sort(char* array , int array_len )
{
    int i = 1;
    array = fast_sort(array,0,array_len-1);//先快排,找出最小字典序列
    printf("\tcase #%d \t%s\n",i,array);
    while(dictionary_sort(array,array_len))
        printf("\tcase #%d \t%s\n",++i,array);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值