17.电话号码的字母组合

题目描述

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:

输入:digits = ""
输出:[]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

学到的方法(回溯)

这道题需要用到回溯的算法思想,因为digits中有不等的数字个数,需要对每个数字对应的字母分别进行排列组合,而回溯的好处就是不断地尝试各种路径,直到该条路径走不通时,再返回上一层,寻找下一条路径。这种方法就解决了多层循环的问题,因为不知道有几个数位,也就无法确定循环的层数,而通过回溯,就可以动态的根据位数,递归的调用自己,直到所有位数对应的字母组合成一个完整的组合,结束此次调用,回溯到上一层循环。由于这题是找字母组合,所以不存在回溯路径走不通的情况,每一条路径都可以从头走到尾,因此只需要穷举所有可能的组合即可。

代码 

char phoneMap[11][5] = {"\0", "\0", "abc\0", "def\0", "ghi\0", "jkl\0", "mno\0", "pqrs\0", "tuv\0", "wxyz\0"};

char* digits_tmp;        //数字数组的临时拷贝
int digits_size;         //数字数组的元素个数

char** combinations;     //总的组合数组
int combinations_size;   //总组合的个数

char* combination;       //单个的组合
int combination_size;    //单个组合的大小
//回溯函数
void backtrace(int index){
//如果回溯时传入的下标等于数字数组的元素个数,表明该组合已经完整,将这个组合加入到总的组合中
    if(index==digits_size){
        char* temp=malloc(sizeof(char)*(combination_size+1));
        memcpy(temp,combination,sizeof(char)*(combination_size+1));
        combinations[combinations_size++]=temp;
    }
//否则表示该组合尚未完整,仍需向后递归,直到组合完整
    else{
        char digit=digits_tmp[index];
        char* letters=phoneMap[digit-'0'];
        int len=strlen(letters);
        for(int i=0;i<len;i++){
            combination[combination_size++]=letters[i];
            combination[combination_size]=0;
            backtrace(index+1);
            combination[--combination_size]=0;
        }
    }
}
char ** letterCombinations(char * digits, int* returnSize){
    digits_tmp=digits;   //将digits实参保存在全局变量中,用于回溯
    combination_size=combinations_size=0;  //初始化为0
    digits_size=strlen(digits);  //记录字符数组大小
    //数字数组为空则返回一个空数组
    if(digits_size==0){
        *returnSize=0;
        return combinations;
    }
    //根据digits_size估算要分配给combinations的大小
    int size=1;
    for(int i=0;i<digits_size;i++){
        size*=4;
    }
    //为combination和combinations分配内存空间
    combination=malloc(sizeof(char)*(digits_size+1));
    combinations=malloc(sizeof(char*)*size);
    //从0开始回溯
    backtrace(0);
    *returnSize=combinations_size;
    return combinations;
}

回溯过程

以digits=['2','3']为例,初始时,combinations_size和combination_size=0,且digits_size=2;

于是为combinations动态开辟4^2=16份sizeof(char*)字节的大小,足以容纳所有的排列组合情况;

为combination动态开辟digits_size+1个char的空间,用来存放字符串"ad" ,"ae"等组合,之所以要+1,是为了存放该字符串的结束标志'\0';

第一次回溯时,调用backtrace(0),进入backtrace()函数后,由于0!=2,执行else语句的内容,digit='2',letters="abc",len=3,combination[0]='a',combination[1]=0,combination_size=1,

递归调用backtrace(1),进入回溯函数后,digit='3',letters="def",len=3,combination[1]='d',combination[2]=0,combination_size=2,

递归调用backtrace(2),进入回溯函数后,2==2,执行if语句的内容,将combination数组的内容赋值给临时拷贝数组tmp中,并把该临时拷贝数组的内容加入到combinations数组中,并将combinations_size++,此时"ad"被放入combinations,接下来backtrace(2)函数执行结束,根据函数调用栈中记录的内容,返回到backtrace(2)的后一句代码处,先--combination_size,则combination_size=1,接着给combination[1]=0,把之前存放的'd'的值覆盖。

对于backtrace(1),将"ad"加入到组合序列后,相当于执行完成了一趟for循环,接着进行下一次循环,如上述过程,分别加入"ae","af"后,backtrace(1)的执行才算结束,于是返回到调用该函数的位置,转而继续执行该句代码的后一句内容。先--combination_size,则combination_size=0,接着令combination[0]=0,把之前存放的'a'的值覆盖,这句代码执行完后,对backtrace(0)函数而言,相当于执行完成了一趟for循环,接着进入下一趟循环,后序依次将"bd","be","bf","cd","ce","cf"加入到组合序列combinations中,至此回溯函数的执行才结束,函数调用栈为空。

总结

这道题让我粗略的了解了回溯函数的工作原理,所谓回溯就是不断地尝试,直到走不通时再返回上层函数,换一条新的路径继续探索。在循环嵌套层数太多或者无法确定时,可以考虑并尝试使用这种思想解决问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值