Leetcode -- Letter Combinations of a Phone Number

本文详细介绍了如何使用深度搜索(DFS)和广度搜索(BFS)解决给定数字字符串对应可能的字母组合问题。通过构建哈希表映射数字到字母,采用递归实现DFS或使用队列实现BFS,最终获得所有可能的字母组合。DFS和BFS在处理此类问题时各有特点,适用于不同场景。

问题链接:https://oj.leetcode.com/problems/letter-combinations-of-a-phone-number/


问题描述:Given a digit string, return all possible letter combinations that the number could represent.

关于数字和字母的mapping请参考链接内的图


API:public List<String> letterCombinations(String digits)


问题分析:本题目其实并不是字符串的处理题,相反其实是一道比较基础的图论题。在目前的面试阶段里,深度搜索dfs和广度搜索bfs的灵活运用应该能处于扫遍大部分的情况。本题也是这样的。接下来将就这两种算法给出不同的答案。

算法步骤如下:

1.根据数字和字符的mapping,我们可以先建立对应的哈希表作为缓存调用,这样代码写起来比较方便。

2.然后进行dfs或者bfs的图构建。实际上这就是一个建图的过程,到最后结果就是图分支里最底层的所有节点。

先给出的是dfs算法代码:

    public List<String> letterCombinations(String digits) {
        HashMap<Character, char[]> cached = new HashMap<Character, char[]>();
        for(int i = 0; i <= 9; i++){
            Character c = (char)('0' + i);
            switch(c){
                case '0':
                    cached.put(c, "0".toCharArray());
                    break;
                case '1':
                    cached.put(c, "1".toCharArray());
                    break;
                case '2':
                    cached.put(c, "abc".toCharArray());
                    break;
                case '3':
                    cached.put(c, "def".toCharArray());
                    break;
                case '4':
                    cached.put(c, "ghi".toCharArray());
                    break;
                case '5':
                    cached.put(c, "jkl".toCharArray());
                    break;
                case '6':
                    cached.put(c, "mno".toCharArray());
                    break;
                case '7':
                    cached.put(c, "pqrs".toCharArray());
                    break;
                case '8':
                    cached.put(c, "tuv".toCharArray());
                    break;
                case '9':
                    cached.put(c, "wxyz".toCharArray());
                    break;
                default:
                    break;
            }
        }
        List<String> res = new LinkedList<String>();
        dfshelper(res, 0, digits, cached, new StringBuilder());
        return res;
    }
    
    public void dfshelper(List<String> res, int curlevel, String digits, HashMap<Character, char[]> cached, StringBuilder buf){
        if(curlevel == digits.length()){
            res.add(buf.toString());
        }else{
            char[] num_to_char = cached.get(digits.charAt(curlevel));
            for(char c : num_to_char){
                StringBuilder next_buf = new StringBuilder(buf);
                next_buf.append(c);
                dfshelper(res, curlevel + 1, digits, cached, next_buf);
            }
        }
        
    }
dfs作为深度搜索一般都以递归为最先考虑到的实现方案。当然,也可以用Stack来储存当前状态来进行循环做,以后的pre-order和in-order还有post-order遍历二叉树的解里都可以看的到。但是一旦需要储存的状态比较复杂,这个stack就比较让人痛苦了。并且这种做法本质上只是代替了编译器做状态储存和提取的递归,在没有很苛刻的performance要求的情况下,并没有必要。


接下来给出的便是bfs的解题代码:

    public List<String> letterCombinations(String digits) {
        HashMap<Character, char[]> cached = new HashMap<Character, char[]>();
        for(int i = 0; i <= 9; i++){
            Character c = (char)('0' + i);
            switch(c){
                case '0':
                    cached.put(c, "0".toCharArray());
                    break;
                case '1':
                    cached.put(c, "1".toCharArray());
                    break;
                case '2':
                    cached.put(c, "abc".toCharArray());
                    break;
                case '3':
                    cached.put(c, "def".toCharArray());
                    break;
                case '4':
                    cached.put(c, "ghi".toCharArray());
                    break;
                case '5':
                    cached.put(c, "jkl".toCharArray());
                    break;
                case '6':
                    cached.put(c, "mno".toCharArray());
                    break;
                case '7':
                    cached.put(c, "pqrs".toCharArray());
                    break;
                case '8':
                    cached.put(c, "tuv".toCharArray());
                    break;
                case '9':
                    cached.put(c, "wxyz".toCharArray());
                    break;
                default:
                    break;
            }
        }
        List<String> res = new LinkedList<String>();
        int cur_size = 1;
        int cur_level = 0;
        Queue<StringBuilder> bfs_queue = new LinkedList<StringBuilder>();
        bfs_queue.offer(new StringBuilder());
        while(cur_level != digits.length()){
            StringBuilder cur_buf = bfs_queue.remove();
            char[] num_to_char = cached.get(digits.charAt(cur_level));
            for(char c : num_to_char){
                StringBuilder next_buf = new StringBuilder(cur_buf);
                next_buf.append(c);
                bfs_queue.offer(next_buf);
            }
            cur_size--;
            if(cur_size == 0){
                cur_size = bfs_queue.size();
                cur_level++;
            }
        }
        while(!bfs_queue.isEmpty())
            res.add(bfs_queue.poll().toString());
        return res;
    }
对比之下,bfs一般都是以queue作为载体来进行循环的一个过程。一般都需要用到类似cur_size和cur_level的变量来记录每一层的节点数目和当前的层数。


dfs和bfs相比,dfs的特点在与探索分支的状态,也就是可以先返回更深层次的状态。bfs的特点在于是一层层往下检索,而且比较容易用循环实现,在寻找图中某个特别的节点的时候,bfs效率一般更高,因为循环比较容易终结,相反dfs就要采取比较非常规的方法了,譬如全局变量之类的,又或者c++里面可以放入reference来做近似的全局处理,java就办不到了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值