0
点赞
收藏
分享

微信扫一扫

Leetcoder Day43| 单调栈2

月半小夜曲_ 2024-03-29 阅读 34

89. 格雷编码,92. 反转链表 II,93. 复原 IP 地址,每题做详细思路梳理,配套Python&Java双语代码, 2024.03.24 可通过leetcode所有测试用例。

目录

89. 格雷编码

解题思路

完整代码

Python

Java

92. 反转链表 II

解题思路

完整代码

Python

Java

93. 复原 IP 地址

解题思路

完整代码

Python

Java


89. 格雷编码

解题思路

要生成一个 n 位的格雷码序列,我们可以使用递归的方法。格雷码有一个很有趣的性质,即可以通过 n-1 位的格雷码来生成 n 位的格雷码。这种方法称为反射法,其步骤如下:

  1. 开始于基础情况:当 n = 1 时,格雷码序列是 [0, 1]。
  2. 递归生成 n-1 位的格雷码序列:对于 n > 1,首先生成 n-1 位的格雷码序列。
  3. 复制并反射:将 n-1 位的格雷码序列复制一份,并反转这个副本,以保持相邻数字只有一位二进制差异。
  4. 添加高位 1:在反转的副本序列的每个数字前添加一个高位的 1,而在原始序列的每个数字前添加一个高位的 0(实际上不需要操作,因为默认高位就是0)。
  5. 合并序列:最后将这两个序列合并,得到 n 位的格雷码序列。

完整代码

Python
class Solution:
    def grayCode(self, n: int) -> List[int]:
        if n == 0:
            return [0]
        
        # 递归生成 n-1 位的格雷码
        prev_gray = self.grayCode(n - 1)
        result = []
        
        # 将前一序列的值添加进结果,前面加0(实际不操作,因为默认就是0)
        result.extend(prev_gray)
        
        # 反射并添加高位的1
        for code in reversed(prev_gray):
            result.append(code | 1 << (n - 1))
        
        return result
Java
class Solution {
    public List<Integer> grayCode(int n) {
        List<Integer> result = new ArrayList<>();
        // 基础情况:n=1
        if (n == 0) {
            result.add(0);
            return result;
        }

        // 递归生成 n-1 位的格雷码
        List<Integer> prevGray = grayCode(n - 1);
        // 添加低位的格雷码
        result.addAll(prevGray);
        // 添加高位的 1 并反射
        int addNumber = 1 << (n - 1);
        for (int i = prevGray.size() - 1; i >= 0; i--) {
            result.add(prevGray.get(i) + addNumber);
        }
        return result;
    }

}

92. 反转链表 II

解题思路

要在链表中反转从位置 left 到位置 right 的节点,可以通过一次遍历来实现。这个过程大致可以分为以下几个步骤:

  1. 定位到 left:首先,遍历链表直到到达位置 left。我们需要记录这个位置的前一个节点,因为反转之后,left 节点将会连接到 left 前一个节点上。

  2. 反转 left 到 right:从 left 开始,遍历到 right,并在遍历过程中逐个反转节点的指向。我们需要记录 left 节点,因为反转后,它将指向 right 节点之后的节点。

  3. 重连链表:最后,将链表的未反转部分与反转后的部分正确连接起来。

完整代码

Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
        if not head or left == right:
            return head
        
        dummy = ListNode(0, head)
        prev = dummy
        
        # Step 1: 定位到 left 的前一个节点
        for _ in range(left - 1):
            prev = prev.next
        
        # Step 2: 开始反转
        reverse = None
        current = prev.next
        for _ in range(right - left + 1):
            next_temp = current.next
            current.next = reverse
            reverse = current
            current = next_temp
        
        # Step 3: 重连链表
        prev.next.next = current
        prev.next = reverse
        
        return dummy.next
Java
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        if (head == null || left == right) {
            return head;
        }

        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode prev = dummy;

        // Step 1: 定位到 left 的前一个节点
        for (int i = 0; i < left - 1; i++) {
            prev = prev.next;
        }

        // Step 2: 开始反转
        ListNode start = prev.next;
        ListNode then = start.next;

        for (int i = 0; i < right - left; i++) {
            start.next = then.next;
            then.next = prev.next;
            prev.next = then;
            then = start.next;
        }

        // Step 3: 链表已经在反转过程中正确连接,不需要额外操作

        return dummy.next;
    }
}

93. 复原 IP 地址

解题思路

生成所有可能的有效 IP 地址,可以通过回溯算法来解决。这个问题的关键在于逐步构建 IP 地址的每一部分,并在每一步中确保该部分是有效的。具体步骤如下:

  1. 定义回溯函数:定义一个回溯函数,该函数接收当前构造的 IP 地址部分、剩余的字符串和已经确定的段数作为参数。

  2. 结束条件:当已经确定了 4 段 IP 地址并且消耗完了输入字符串时,将当前构建的 IP 地址添加到结果集中。

  3. 递归和回溯:从输入字符串中选择 1 到 3 个字符作为当前段的候选,如果选择的字符串是有效的 IP 段(0-255,且无前导 0,除非是单独的 0),则递归地继续选择下一段。

  4. 有效性检查:检查当前选择的字符串是否形成一个有效的 IP 地址段。

  5. 剪枝:在某些情况下提前终止搜索,例如,如果剩余的字符串太长或太短,无法形成有效的剩余段。

完整代码

Python
class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        def isValid(segment):
            # 检查 IP 段的有效性
            return len(segment) == 1 or (segment[0] != '0' and int(segment) <= 255)

        def backtrack(start=0, parts=[]):
            # 如果找到了 4 部分,并且用完了所有字符
            if len(parts) == 4 and start == len(s):
                result.append('.'.join(parts))
                return
            if len(parts) == 4 or start == len(s):
                return
            
            # 尝试每一部分的长度:1, 2, 或 3
            for end in range(start + 1, min(start + 4, len(s) + 1)):
                segment = s[start:end]
                if isValid(segment):
                    backtrack(end, parts + [segment])

        result = []
        backtrack()
        return result
Java
public class Solution {
    public List<String> restoreIpAddresses(String s) {
        List<String> result = new ArrayList<>();
        
        void backtrack(int start, List<String> parts) {
            if (parts.size() == 4 && start == s.length()) {
                result.add(String.join(".", parts));
                return;
            }
            if (parts.size() == 4 || start == s.length()) return;
            
            for (int end = start + 1; end <= Math.min(start + 3, s.length()); end++) {
                String segment = s.substring(start, end);
                if ((segment.length() == 1 || (segment.length() > 1 && segment.charAt(0) != '0')) && Integer.parseInt(segment) <= 255) {
                    parts.add(segment);
                    backtrack(end, new ArrayList<>(parts));
                    parts.remove(parts.size() - 1);
                }
            }
        }
        
        backtrack(0, new ArrayList<>());
        return result;
    }
}
举报

相关推荐

0 条评论