LeetCode Top100之17,19题

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;
}
③ 一次遍历
  • 主要思想:对比于链表,我们设定两个指针(firstsecond),先让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;
}

猜你喜欢

转载自blog.csdn.net/u014454538/article/details/90400524