leetcode 17. Letter Combinations of a Phone Number

一 题目

Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent.

A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.

Example:

Input: "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

二 分析

这个是字符串的题目,medium级别。求数字代表的字母的组合,

先想到的就是暴力循环,时间复杂度是O(4^n) ,取决于输入字符串的长度。怎么遍历就是新问题。

2.1 递归(DFS):

Backtracking

画图很关键有助于厘清思路。递归就是一种深度优先DFS的策略。

画图有两个问题要明白,一个是树的深度,一个是分叉处理。

这里树的深度就是输入字符串的长度(比较直观,每多一位就多一层),

分叉多少,取决于数字是几,也就是初始化是对于数组的值(3个或者4个)。

具体代码实现,对于这个也是分为两部分,一开始是coner case(边界判断),因为LeetCode做几道就知道了,不做校验是过不了的,这个不重要,大不了根据case补充下。

  然后是递归的算法,这里要考虑涉及的参数,有字符串的prefix,要处理的digits, 树的高度level,还有输出的结果res.

这里面有两部分,先写基本的返回条件。对于深度优先,就是树的高度达到底部,(生成一条满足标准的结果)。同时加入返回结果。return很重要。

     还有就是正常的业务规则,这里很关键,对照上面的图,就是处理 分叉的字符,我们根据数字取出来对应的字符串,遍历这个字符串,然后根据规则,追加到prefix,再把树的高度加1,从图上看,就是取到2对应的字符串“abc”,level 是1,循环先处理a,及对应的level=2,取出3对应的“def”,此时prefix是“a”,level=2.复合条件,把"ad","ae","af"加入并返回,在后面以此类推处理“b”,“c”.

结合代码跟图看会更好理解些。

private final static String[] dict = { "", "", "abc", "def", "ghi",
			 "jkl", "mno", "pqrs", "tuv", "wxyz" };
     //递归
	  public static List<String> letterCombinations(String digits) {
		  List<String> res = new ArrayList<>();
		  if(digits== null || digits.equals("")){
			  return res;
		  }else if(digits.length()==1 ){
			 return Arrays.asList(dict[digits.charAt(0)-'0'].split(""));
		  }
		  
		    combinationDFS("", digits, 0, res);
		    return res;
		 
	  }
	private static void combinationDFS(String prefix, String digits, int level, List<String> res) {
		//结束条件
		if(level == digits.length() ){
			res.add(prefix);
			return ;
		}
		//rule
		String s =dict[digits.charAt(level)-'0'];
		for(char c:s.toCharArray()){
			combinationDFS(prefix+c,digits,level+1,res );
		}
	}

Runtime: 1 ms, faster than 64.43% of Java online submissions for Letter Combinations of a Phone Number.

Memory Usage: 36.4 MB, less than 98.63% of Java online submissions forLetter Combinations of a Phone Number.

2.2 BFS

上面是深度优先,那么对应的广度优先是啥呢?例如:2,就是a,b,c. 在输入3就是那d,e,f分别追加到a,b,c的后面。先把每个都遍历完,不用递归也可以,使用迭代,我们在遍历 digits 中所有的数字时,我们先建立一个临时的list。还是向上面那样,通过数字到 dict 中取出字符串 str(为了对比测试,这里我使用了map,因为都是O(1)的操作,所以map跟数组没有新能太多的区别),然后遍历取出字符串中的所有字符,再遍历当前结果 res 中的每一个字符串,将字符加到后面,并加入到临时字符串list中。取出的字符串 str 遍历完成后,将临时字符串list赋值给结果 res.

这个就是广度优先的BFS的实现方式,还是深度的更容易理解些。光理解大流程还不行,还得写出来.,没看大神的代码之前,我也卡住了,初始值哪里就没想好。

	public static void main(String[] args) {

		List<String>  res =	letterCombinations("234");
		System.out.println(JSON.toJSON(res ) );
	}

	  public static List<String> letterCombinations(String digits) {
		  Map<Character,String> map = new HashMap();
		  map.put('2', "abc");
		  map.put('3', "def");
		  map.put('4', "ghi");
		  map.put('5', "jkl");
		  map.put('6', "mno");
		  map.put('7', "pqrs");
		  map.put('8', "tuv");
		  map.put('9', "wxyz");
		  List<String> res = new ArrayList();
		
		  if(digits== null || digits.equals("")){
			  return res;
		  }else if(digits.length()==1 ){
			 return Arrays.asList(map.get(digits.charAt(0)).split(""));
		  }
		  res.add("");
		
		 
		  for(int i=0;i< digits.length() ;i++ ){
			  List<String> tmp = new ArrayList<>();
			  for (String t : res) {
				  String option = map.get(digits.charAt(i));
				  for(int p=0;p<option.length();p++){
				  tmp.add(new StringBuilder(t).append(option.charAt(p)).toString() );
				  }
			  }
			  res = tmp;
		  }		 
		  return res;		  
	    }
	

 

Runtime: 2 ms, faster than 6.88% of Java online submissions for Letter Combinations of a Phone Number.

Memory Usage: 36.4 MB, less than 98.63% of Java online submissions forLetter Combinations of a Phone Number.

时间复杂度一样(4^n),优点就是空间占用小O(n)

总结:

   这个题目相对来说考点清晰,就是 关于BFS、DFS,没有掺和其他的算法(基本的api操作还是有的),得理清楚思路,画出递归树那个图来,帮助自己却确定返回条件是什么?业务规则如何去递归。再次感谢花花酱大神的文章。

 

参考:

https://zxi.mytechroad.com/blog/searching/leetcode-17-letter-combinations-of-a-phone-number/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值