编程之法:面试和算法心得 读书笔记(三) 字符串的全排列

全排列

递归

  首先来说递归方法是比较容易想到的。

  比如 “abc”
  首先作为字符串的第一位可以 是 ‘a’,‘b’,‘c’。剩下的字符串任然进行全排列。

第一个字符剩下的字符
1abc的全排列
2bac的全排列
3cab的全排列
#include <stdio.h>
#include <string.h>

void swap(char * ch1, char * ch2){
    char t = *ch1;
    *ch1 = *ch2;
    *ch2 = t;
}


//递归全排列
void CalcAllPermutation(char * str,int begin, int end){
    if(end <= 0){
        return;
    }
    if(begin == end){
        printf("%s\n",str);
    }else{
        for(int i = begin;i <= end;i++){
            swap(str+begin,str+i);
            CalcAllPermutation(str,begin+1,end);
            swap(str+begin,str+i);
        }
    }
}



int main(int argc, char * argv[]){
    char str[] = "abc";
    printf("递归全排列:\n");
    CalcAllPermutation(str,0,strlen(str)-1);
    return 0;
}

非递归

  非递归方式,简而言之就是:
  比如 “123”
  如果看成数字最小的就是123,最大的就是321。字符串也有大小,称为字典序。这样我们就可以设计一个函数,获取当前字符串的下一个比它大的字符串。还是以 “123”为例子, f ( &quot; 123 &quot; ) = “ 213 ” f(&quot;123&quot;) = “213” f("123")=213, f ( &quot; 213 &quot; ) = “ 231 ” f(&quot;213&quot;) = “231” f("213")=231, f ( &quot; 231 &quot; ) = &quot; 312 &quot; f(&quot;231&quot;) = &quot;312&quot; f("231")="312" f ( &quot; 321 &quot; ) = “ ” f(&quot;321&quot;) = “” f("321")= 结束

次数
0123
1132
2213
3231
4312
5321

  那么这个函数如何设计呢?

1

  找到最右边的一个需要变大的元素。

当前字符串需要变大的元素备注
12322->3
13211->2
21311->2
23122->3

   其实我们就是要从又往左扫描字符串str 找到第一个 s t r [ i ] &lt; s t r [ i + 1 ] str[i] &lt; str[i+1] str[i]<str[i+1]。如果找不到就说明已经是最大的字典序。也就是全排列结束。

2

  接下来就是要找i右边的字符串中的比str[i]大的所有元素中最小的一个。str[j],然后和str[i]互换位置,并且把i右边的字符串变成最小字典序排列。得一点一点变大是吧,不能漏。然后我用数字来解释, 199 + 1 = 200 199+1=200 199+1=200,不看百位99正好就是最大的,00正好就是最小的。所以我们需要,从右往左找到第一个比第一步找到的元素大的元素。和str[i]互换并且把i右边的字符串倒置。应为肯定是要先到最大字典序才会需要“进位”。而且是把字符串中最小的一个元素换成了一个更小的元素。所以当前任然是最大字典序,倒置就是最小字典序。

当前字符串需要变大的元素第二步找到的元素交换倒置
12323132132
13212231213
21313231231
23123321312
#include <stdio.h>
#include <string.h>

void swap(char * ch1, char * ch2){
    char t = *ch1;
    *ch1 = *ch2;
    *ch2 = t;
}



//翻转字符串
void reverse(char * buffer,int len)
{
    char t;
    for(int i = 0;i<len/2;i++)
    {
        t = *(buffer+i);
        *(buffer+i) = *(buffer+len-i-1);
        *(buffer+len-i-1) = t;
    }
}

//非递归全排列 要求初始字符串字典序最小排列
int _CalcAllPermutation(char * str){
    int i;
    int len = strlen(str);
    //找最后一个 可以升序的位置
    for(i = len - 2; (i>=0) && str[i+1] <= str[i];i--);
    //全排列完成
    if(i < 0){
        return 0;
    }
    int j;
    //找到i右边最后一个比 str[i]大的元素  应为此时 i 右边一定是字典序最大排列
    for(j = len-1; (j > i) && str[j] <= str[i]; j--);
    swap(str+i,str+j);
    reverse(str+i+1,len-i-1);
    printf("%s\n",str);
    return 1;
}

void CalcAllPermutation(char * str){
    printf("%s\n",str);
    while( _CalcAllPermutation(str) );
}


int main(int argc, char * argv[]){
    char str[] = "abc";
    printf("非递归全排列:\n");
    CalcAllPermutation(str2);
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值