- 记录于2019年5月21日
17. 电话号码的字母组合
- 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
- 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
- 示例:
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
说明: 尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
② 将组合问题(需要多层循环)转化为乘法——速度竟然异常的快。
- 根据题意可知,如果数字为
“23”
,最后的字母组合为3 x 3 = 9
种;如果数字为“234”
,最后的字母组合为9 x 3 = 27
种。这是组合问题,原本应该是有几个数字,就需要几层循环,获得所有字母的组合。 - 没办法在不知道数字个数的情况下,编写多层循环。所以将其改为连乘。当list1的长度为0时,直接返回list2;否则,使用2层循环,将list1和list2中的字母组合起来。
- 通过这样的方法,能将
“234”
对应的字母组合,变成3 x 3 x 3 = 27
的连乘。 - 代码如下:
public List<String> letterCombinations(String digits) {
List<String> result = new ArrayList<>();
if (digits == null || digits.length() == 0) {
return result;
}
for (int i = 0; i < digits.length(); i++) {
result = mul(result, getList(digits.charAt(i) - '0'));
}
return result;
}
public List<String> getList(int num) {
String digitLetter[] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
List<String> res = new ArrayList<>();
for (int i = 0; i < digitLetter[num].length(); i++) {
res.add(digitLetter[num].charAt(i) + "");
}
return res;
}
public List<String> mul(List<String> list1, List<String> list2) {
if (list1.size() == 0 && list2.size() != 0) {
return list2;
}
List<String> res = new ArrayList<>();
for (int i = 0; i < list1.size(); i++) {
for (int j = 0; j < list2.size(); j++) {
res.add(list1.get(i) + list2.get(j));
}
}
return res;
}
③ 使用递归
- 通过动态图可知,使用递归可以获得所有可能的字母组合。
Backtracking is an algorithm for finding all solutions by exploring all potential candidates. If the solution candidate turns to be not a solution (or at least not the last one), backtracking algorithm discards it by making some changes on the previous step, i.e. backtracks and then try again.
- 当digits的长度为0时,表明已经找到了最后的组合,直接添加到result中。
String digitLetter[] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
List<String> result = new ArrayList<>();
public List<String> letterCombinations(String digits) {
if (digits.length() == 0 || digits == null) {
return result;
}
helper("", digits);
return result;
}
public void helper(String com, String digits) {
if (digits.length() == 0) {
result.add(com);
} else {
int index = digits.charAt(0) - '0';
for (int i = 0; i < digitLetter[index].length(); i++) {
helper(com + digitLetter[index].charAt(i), digits.substring(1));
}
}
}
④ 队列迭代
- 巧妙使用
LinkedList
双向链表,从head删除满足条件的组合,从tail添加新的组合。 - 从head删除满足条件的组合,要求组合的长度与当前数字的index相同。当index为0,时组合长度为0,因此需要提前添加
""
;当index为1时,将head处,所有长度为1的组合remove(eg: 'a', 'b', 'c'
),将其与当前数字中的字母组合成新组合,再添加在tail。
String digitLetter[] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
public List<String> letterCombinations(String digits) {
LinkedList<String> result = new LinkedList<>();
if (digits.length() == 0 || digits == null) {
return result;
}
result.add("");
for (int i = 0; i < digits.length(); i++) {
int index = digits.charAt(i) - '0';
while (result.peek().length() == i) {
String com = result.remove();
for (int j = 0; j < digitLetter[index].length(); j++) {
result.add(com + digitLetter[index].charAt(j));
}
}
}
return result;
}
19. 删除链表的倒数第N个节点
① 题目描述
- 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
- 示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:给定的 n 保证是有效的。
- 进阶:
你能尝试使用一趟扫描实现吗?
② 两次遍历
- 一次遍历用于获取链表长度,一次遍历用于找到被删除节点的prev,然后更新prev的指向。
- 第一次遍历后,如果len和要删除的index一致,说明删除的是第一个节点,直接
return head.next;
即可。
public ListNode removeNthFromEnd(ListNode head, int n) {
int len = 0;
ListNode p = head;
while (p != null) {
len++;
p = p.next;
}
if (len == n) {
return head.next;
}
ListNode prev;
p = head;
int cur = 0;
while (p != null) {
cur++;
if (cur == len - n) {
prev = p;
prev.next = prev.next.next;
break;
}
p = p.next;
}
return head;
}
③ 一次遍历
- 主要思想:对比于链表,我们设定两个指针(
first
和second
),先让first
指针遍历n + 1
步,然后再让它俩同时开始遍历。这样的话,当first
指针到null节点
的时候,second
指针就离第一个指针有 n 的距离,所以second
指针的位置就刚好是倒数第 n 个结点的prev
。 - 特点:
① 需要添加dummy
节点作为新的head;
②first
指针先跑的不是n步,而是n + 1
步;
③ 同时跑时,以first
指针指向null时停止。
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode first = dummy, second = dummy;
for (int i = 1; i <= n + 1; i++) {
first = first.next;
}
while (first != null) {
first = first.next;
second = second.next;
}
second.next = second.next.next;
return dummy.next;
}