# 0322【LeetCode 算法笔记】Task03 75 - 87

71 阅读12分钟

开源内容:github.com/datawhalech…

电子网站:datawhalechina.github.io/leetcode-no…

07.03

对应题解 速查链接

——————————

🚩——day9

958. 二叉树的完全性检验

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isCompleteTree(self, root: Optional[TreeNode]) -> bool:
        q = [root]
        flag = False  # 标记 之前是否 出现过 空结点
        while q:
            nxt = []
            for node in q:
                if not node:
                    flag = True 
                else: # 现在 不空
                    # 之前 出现过 空节点
                    if flag:
                        return False 
                    nxt.append(node.left)  # 直接 将 子节点 加到 下一层
                    nxt.append(node.right)

            q = nxt

        return True 

image.png

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool isCompleteTree(TreeNode* root) {
        vector<TreeNode*> q;
        q.emplace_back(root);
        bool flag = false; // 是否 出现了 空节点
        while (!q.empty()){
            vector<TreeNode*> nxt;
            for (TreeNode* node : q){// vector 才可以 这样遍历
                if (!node){
                    flag = true; // 出现 空结点了
                }else{
                    if (flag) return false;
                    nxt.emplace_back(node->left);
                    nxt.emplace_back(node->right);
                }
            }
            q = move(nxt);            
        }
        return true;
    }
};

543. 二叉树的直径

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        # 如何 求两个叶子结点 之间的最短路径长度   
        # 如何 找到 最长路径
        self.ret = 0
        def dfs(node):
            if not node:
                return 0 
            L = dfs(node.left)
            R = dfs(node.right)

            self.ret = max(self.ret, L + R)  # 只 比 深度 计算 多了 这行 代码
            return max(L, R) + 1  

        dfs(root)
        return self.ret

image.png

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    int ret;
    int dfs(TreeNode* node){
        if (!node) return 0;
        int L = dfs(node->left);
        int R = dfs(node->right);
        ret = max(ret, L + R); // 只比 求深度模块 多了这句判断
        return max(L, R) + 1;
    }
public:
    int diameterOfBinaryTree(TreeNode* root) { // 观察发现, 路径长度 恰好 等于 左边的深度 + 右边的深度
        ret = 0;
        dfs(root);
        return ret;
    }
};

662. 二叉树最大宽度

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        # 最左 的 非空结点  null null...  最右的非空结点  宽度
        # 记录每层 最左的节点编号  最右节点的编号 求 差 
        ret = 1  # 最大宽度
        q = [[root, 1]]  # 节点, 编号
        while q:
            ret = max(ret, q[-1][1] - q[0][1] + 1)  # 注意 , 是根据 q 来算。 q 可保证 非空
            nxt = []
            for node, i in q:
                if node.left:
                    nxt.append([node.left, i * 2])
                if node.right:
                    nxt.append([node.right, i * 2 + 1])             
            q = nxt 
        return ret


image.png

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int widthOfBinaryTree(TreeNode* root) {
        vector<pair<TreeNode*, unsigned long long>> q; 
        unsigned long long ret = 1; // 最大 宽度
        q.emplace_back(root, 1L);  // 结点, 编号
        while (!q.empty()){
            ret = max(ret, q.back().second - q[0].second + 1);   // 可保证 q 必定 非空         
            vector<pair<TreeNode*, unsigned long long>> nxt;
            for (auto &[node, i] : q){
                if (node->left){
                    nxt.emplace_back(node->left, i * 2);
                }
                if (node->right){
                    nxt.emplace_back(node->right, i * 2 + 1);
                }
            }
            q = move(nxt);
        }
        return ret;
    }
};

🚩——day10

322. 零钱兑换

float('inf') 和 math.inf

image.png

动态规划 O(len(coins)×amount)O(len(coins) \times amount)

image.png

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # 自下而上 
        # dp[i]: 组成金额 i 所需最少的硬币数量
        # dp[i] = min(dp[i - cj]) + 1  # 遍历 硬币面值
        
        # dp = [inf] * (amount + 1)  # 直接用 inf 的前提是 import math。LeetCode 一般都导入,所以不会错,实际应用忘记导入会报错 
        dp = [float('inf')] * (amount + 1)
        dp[0] = 0

        for coin in coins:
            for x in range(coin, amount + 1):
                dp[x] = min(dp[x], dp[x - coin] + 1)
        return dp[amount] if dp[amount] != inf else -1

image.png

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        // dp[i]: 凑成 金额 i 所需的最少硬币数 为 dp[i]
        // dp[i] = min(dp[i - cj]) + 1  遍历 各面值
        int Max = amount + 1;
        vector<int> dp(amount + 1, Max);
        dp[0] = 0;
        for (int coin : coins){// 遍历 硬币面值
            for (int x = coin; x <= amount; ++x){// 从面值 coin 开始 凑
                dp[x] = min(dp[x], dp[x - coin] + 1);
            }
        }
        return dp[amount] > amount ? -1 : dp[amount]; 
    }
};

BFS

image.png

根据题意, 凑钱的过程 可表示为一棵树, 求最少的硬币数,即最短路径, 可考虑 用 BFS

一定 要记得标记 访问过的点 !!!

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0 

        q = collections.deque([[amount, 0]])  # 待凑的金额, 当前的硬币数
        visited = set()
        visited.add(amount)
        while q:
            cur, cnt = q.popleft()
            for coin in coins:
                if cur == coin:
                    return cnt + 1
                if cur - coin > 0 and cur - coin not in visited:
                    q.append([cur - coin, cnt + 1])
                    visited.add(cur - coin)
        return -1

78. 子集

回溯

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        ret = []
        path = []
        n = len(nums)
        def dfs(i):  # nums[i] 选不选
            if i == n:
                ret.append(path.copy())
                return
            path.append(nums[i]) # 选
            dfs(i + 1)
            path.pop() # 不选
            dfs(i + 1)

        dfs(0)
        return ret

image.png

class Solution {
public:
    vector<int> path;
    vector<vector<int>> ret;

    void dfs(int cur, vector<int>& nums){
        if (cur == nums.size()){
            ret.emplace_back(path);
            return;
        }
        path.emplace_back(nums[cur]); // 选
        dfs(cur + 1, nums);
        path.pop_back(); // 不选
        dfs(cur + 1, nums);
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        dfs(0, nums);
        return ret;
    }
};

二进制 mask 表示选或不选

image.png

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        # 用 长度 为 len(nunms) 的 二进制 表示 选不选 nums[i]
        n = len(nums)
        ret = []
        for mask in range(1 << n):#  0 - 2^n-1  如 n = 2, 依次为 00 01 10 11  依次构造 这些模版的子集
            path = []
            for i in range(n):
                if mask & (1 << i): # mask 的 第 i 位 为 1   # 写法: (mask >> i) & 1
                    path.append(nums[i])
            ret.append(path)
        return ret

image.png

class Solution {
public:
    vector<int> path;
    vector<vector<int>> ret;

    vector<vector<int>> subsets(vector<int>& nums) {
        // O(n * 2^n)   O(n)
        int n = nums.size();
        for (int mask = 0; mask < (1 << n); ++mask){
            path.clear();
            for (int i = 0; i < n; ++i){
                if (mask & (1 << i)){// 1 左移 i  选 nums[i]
                    path.emplace_back(nums[i]);
                }
            }
            ret.emplace_back(path);
        }
        return ret;
    }
};

221. 最大正方形

image.png

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        # 动态规划
        # dp[i][j]:  以 (i, j) 为 右下角, 且只包含 1 的 最大正方形的边长
        # matrix[i][j] = 0  dp[i][j] = 0
        # dp[i][j] = min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]) + 1

        maxSide = 0  # 记录 最大正方形的 边长
        m, n = len(matrix), len(matrix[0])
        dp = [[0] * n for _ in range(m)]
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == '1':
                    if i == 0 or j == 0:
                        dp[i][j] = 1
                    else:
                        dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
                    maxSide = max(maxSide, dp[i][j])
        return maxSide**2

image.png

class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        // 动态规划 O(mn)、O(mn)
        int m = matrix.size(), n = matrix[0].size();
        int maxSide = 0;
        vector<vector<int>> dp(m, vector<int>(n, 0));
        for (int i = 0; i < m; ++i){
            for (int j = 0; j < n; ++j){
                if (matrix[i][j] == '1'){
                    if (i == 0 || j == 0){
                        dp[i][j] = 1;
                    }else{
                        dp[i][j] = min(min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
                    }
                }
                maxSide = max(maxSide, dp[i][j]);
            }
        }
        return maxSide * maxSide;
    }
};

🚩——day11

24. 两两交换链表中的节点 【迭代】

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 迭代 O(n) O(1)
        # 0 1 2 3 4  cur node1 node2 node2.next
        # 0 2 1 3 4
        dummy = ListNode(0)
        dummy.next = head
        cur = dummy
        while cur.next and cur.next.next:
            node1 = cur.next 
            node2 = cur.next.next 

            cur.next = node2 
            node1.next = node2.next # 先走 这一步
            node2.next = node1            
            
            cur = node1

        return dummy.next
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 迭代 O(n) O(1)
        # 0 1 2 3 4  node0 node1 node2 node3
        # 0 2 1 3 4
        node0 = dummy = ListNode(next=head)
        node1 = head
        while node1 and node1.next:
            node2 = node1.next 
            node3 = node2.next 

            node0.next = node2
            node2.next = node1 
            node1.next = node3 

            node0 = node1
            node1 = node3 

        return dummy.next

image.png

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        // 0 1 2 3 4  
        //   2 1  
        ListNode* dummy = new ListNode(0, head);
        ListNode *node0 = dummy, *node1 = head;
        ListNode *node2, *node3;
        while (node1 && node1->next){
            node2 = node1->next;
            node3 = node2->next;

            node0->next = node2;
            node2->next = node1;
            node1->next = node3;
     
            node0 = node1;// 注意顺序
            node1 = node3;            
        }
        return dummy->next;
    }
};
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        // 0 1 2 3 4  
        //   2 1  
        ListNode* dummy = new ListNode(0, head);
        ListNode *cur = dummy;
        ListNode *node1 = head, *node2;
        while (cur->next && cur->next->next){
            node1 = cur->next;
            node2 = cur->next->next;

            cur->next = node2;
            node1->next = node2->next; 
            node2->next = node1;
     
            cur = node1;            
        }
        return dummy->next;
    }
};

70. 爬楼梯

class Solution:
    def climbStairs(self, n: int) -> int:
    # O(n) O(n)
        dp = [0] * (n + 1)   # 到达 第 i 个 阶梯的方法数
        dp[0] = dp[1] = 1  # 注意 dp[0] 是 1
        for i in range(2, n + 1):
            dp[i] = dp[i - 1] + dp[i - 2]  # 注意 计算 dp[i] 的时候 只用了 前两个数, 为进一步优化提供了 可能
        return dp[n]
class Solution:
    def climbStairs(self, n: int) -> int:
        f0 = 1
        f1 = 1
        for i in range(2, n + 1):
            f0, f1 = f1, f0 + f1
        return f1

image.png

class Solution {
public:
    int climbStairs(int n) {
        int f0 = 1, f1 = 1;
        for (int i = 2; i <= n; ++i){
            int fi = f0 + f1;
            f0 = f1;
            f1 = fi;
        }
        return f1;
    }
};

image.png

53. 最大子数组和

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:        
        n = len(nums)
        pre = nums[0]
        ret = nums[0]
        for i in range(1, n):
            pre = max(pre + nums[i], nums[i]) # 是否 取 nums[i] 前面的累加和
            ret = max(ret, pre)
        return ret

image.png

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int pre = nums[0], ret = nums[0];
        for (int i = 1; i < nums.size(); ++i){
            pre = max(pre + nums[i], nums[i]);
            ret = max(ret, pre);
        }
        return ret;
    }
};

分治

image.png

参考链接

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        def myDivide(left, right):
            if left == right:
                return nums[left]

            mid = (left + right)//2
            leftMax = myDivide(left, mid)
            rightMax = myDivide(mid + 1, right)

            # 计算 包含 nums[mid] 的两边 跨序列的 crossMax
            leftCrossMax = nums[mid]
            leftSum = 0  # 从 mid 到 left 的累加和
            for i in range(mid, left - 1, -1):
                leftSum += nums[i]
                leftCrossMax = max(leftCrossMax, leftSum)

            rightCrossMax = nums[mid + 1]
            rightSum = 0 
            for i in range(mid + 1, right + 1):
                rightSum += nums[i]
                rightCrossMax = max(rightCrossMax, rightSum)

            crossMax = leftCrossMax + rightCrossMax

            return max(leftMax, rightMax, crossMax)

        return myDivide(0, len(nums) - 1)

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        return myDivide(nums, 0, nums.size() - 1);
    }

private:
    int myDivide(vector<int>& nums, int left, int right){
        if (left == right){
            return nums[left];
        }

        int mid = (left + right) / 2;
        int leftMax = myDivide(nums, left, mid);
        int rightMax = myDivide(nums, mid + 1, right);

        // 横 跨 左右的 最大值
        int leftCrossMax = nums[mid];
        int leftCrossSum = 0;
        for (int i = mid; i >= left; --i){
            leftCrossSum += nums[i];
            leftCrossMax = max(leftCrossSum, leftCrossMax); // 累加后 左边 能获得的最大值
        }

        // 右边 累加的最大值
        int rightCrossMax = nums[mid + 1]; 
        int rightCrossSum = 0;
        for (int i = mid + 1; i <= right; ++i){
            rightCrossSum += nums[i];
            rightCrossMax = max(rightCrossSum, rightCrossMax);
        }

        int crossMax = leftCrossMax + rightCrossMax;

        return max(leftMax, max(rightMax, crossMax));
    }
};

🚩——day12

46. 全排列 【回溯】

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        ret = []
        path = [0] * n  # 依次 填入 选的数
        on_path = [False] * n   # 是否 已选
        def backtrack(i):
            if i == n: # 都 填好了
                ret.append(path.copy()) 
                return 

            for j in range(n):
                if not on_path[j]:  # 第 j 个 数 没选过
                    path[i] = nums[j]  # 选
                    on_path[j] = True 
                    dfs(i + 1)
                    on_path[j] = False  # 不选 回溯

        backtrack(0)
        return ret 

image.png

class Solution {
    void backtrack(vector<int>& nums, vector<vector<int>>& ret, vector<int>& path, vector<int>& on_path, int i){
        int n = nums.size();
        if (i == n){
            ret.emplace_back(path);
            return;
        }
        for (int j = 0; j < n; ++j){
            if (!on_path[j]){
                path[i] = nums[j];
                on_path[j] = true;
                backtrack(nums, ret, path, on_path, i + 1); // 每次 回溯 都需要知道 path 和 on_path
                on_path[j] = false;
            }
        }
    }

public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> ret;  
        int n = nums.size();
        vector<int> path(n), on_path(n);    
        backtrack(nums, ret, path, on_path, 0);
        return ret;
    }
};

22. 括号生成

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        # 
        ret = []
        m = 2 * n  # 注意  n 是 括号对数
        path = [''] * m  # 
        def dfs(i, cnt_left): # 当前 处理的位置  左括号个数
            if i == m:
                ret.append(''.join(path))
                return 

            if cnt_left < n:
                path[i] = '('
                dfs(i + 1, cnt_left + 1)
            
            if i - cnt_left < cnt_left:  # 右括号数 < 左括号数  可填 右括号
                path[i] =')'
                dfs(i + 1, cnt_left)

        dfs(0, 0)
        return ret

image.png

class Solution {
    // C++ 字符串 是可改的, 无需像 python 那样 变成 list
    void dfs(const int n, vector<string>& ret, int cnt_left, string& path){
        if (path.size() == n * 2){
            ret.emplace_back(path);
            return;
        }
        if (cnt_left < n){
            path += '(';
            dfs(n, ret, cnt_left + 1, path);
            path.pop_back();
        }
        if (path.size() - cnt_left < cnt_left){// 右括号数 < 左括号数
            path += ')';
            dfs(n, ret,cnt_left, path);
            path.pop_back();
        }        
    }

public:
    vector<string> generateParenthesis(int n) {
        vector<string> ret;
        string path;
        dfs(n, ret, 0, path); //   左括号 个数  
        return ret;
    }
};

39. 组合总和

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:

        ret = []
        path = []
        def backtrack(start, cur):
            if cur == 0:
                ret.append(path.copy())
                return 

            for i in range(start, len(candidates)):  # 避免 重复 子序列
                if cur - candidates[i] < 0:
                    break 
                path.append(candidates[i])  # 选 
                backtrack(i, cur - candidates[i])  # 注意 这里 是 i 
                path.pop() # 回溯


        candidates.sort()
        backtrack(0, target) # 当前 位置, 当前 和
        return ret
class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> ret;
        vector<int> path;
        sort(candidates.begin(), candidates.end()); // 避免 重复 子序列
        backtrack(candidates, ret, 0, path, target);
        return ret;
    }
private:
    void backtrack(const vector<int>& candidates, vector<vector<int>>& ret, int start, vector<int>& path, int cur){
        if (cur == 0){
            ret.emplace_back(path);
            return;
        }
        for (int i = start; i < candidates.size(); ++i){// 从 start 开始, 避免 重复
            if (cur - candidates[i] < 0) break;

            path.emplace_back(candidates[i]);
            backtrack(candidates, ret, i, path, cur - candidates[i]);
            path.pop_back();  // 回溯
        }
    }
};

93. 复原 IP 地址 【递归】

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        if len(s) < 4 or len(s) > 12:  return [] #  因为  题目中 1 <= s.length <= 20

        ret = []
        path = ""
        def dfs(cur, i, path):  # 当前已获取的段数, 当前 处理的 s 索引下标
            if cur == 4 and i == len(s):
                ret.append(path[:-1])
                return 

            if cur > 4:   # 后面 循环 切割的时候 已经限制 len(s) 之内, 因此判断 分段数 是否超过 4 即可
                return 

            for j in range(i, min(i + 3, len(s))):  # 某段的 结束索引  255 最多 3位数
                if int(s[i: j + 1]) <= 255 and (s[i] != '0' or i == j):
                    dfs(cur + 1, j + 1, path + s[i : j + 1] + '.')

        dfs(0, 0, "")
        return ret
class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        if len(s) < 4 or len(s) > 12:  return [] #  因为  题目中 1 <= s.length <= 20

        ret = []
        path = [0] * 4  # 存储 4个 数字 0 - 255
        def dfs(cur, i):  # 处理到 path[cur], 当前 处理的 s 索引下标
            if cur == 4:
                if i == len(s):
                    ipAddr = '.'.join(str(seg) for seg in path)
                    ret.append(ipAddr)
                return # 不管 是否 找到符合 要求的, 都返回, 超 4 段了

            if i == len(s):   # 后面 循环 切割的时候 已经限制 len(s) 之内, 因此判断 分段数 是否超过 4 即可
                return 

            # 处理 前导 0 
            if s[i] == '0':
                path[cur] = 0  # 单独成段
                dfs(cur + 1, i + 1)
                return 

            # 无 前导0
            addr = 0
            for j in range(i, len(s)):  # 某段的 结束索引 
                addr = addr * 10 + (ord(s[j]) - ord('0'))
                if 0 < addr <= 255:
                    path[cur] = addr
                    dfs(cur + 1, j + 1) # 注意 这里 是 j + 1
                else:
                    break

        dfs(0, 0)
        return ret

image.png

class Solution {
private:
    vector<string> ret;
    vector<int> path;

public:
    void dfs(const string& s, int cur, int i) {
        // 如果找到了 4 段 IP 地址并且遍历完了字符串,那么就是一种答案
        if (cur == 4) {
            if (i == s.size()) {
                string ipAddr;
                for (int i = 0; i < 4; ++i) {
                    ipAddr += to_string(path[i]);
                    if (i != 3) {
                        ipAddr += ".";
                    }
                }
                ret.emplace_back(move(ipAddr));
            }
            return; // 这里 要是 cur > 4 就 跳出了
        }

        // 如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯
        if (i == s.size()) {
            return;
        }

        // 由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0
        if (s[i] == '0') {
            path[cur] = 0;
            dfs(s, cur + 1, i + 1);
            return;
        }

        // 一般情况,枚举每一种可能性并递归
        int addr = 0;
        for (int j = i; j < s.size(); ++j) {
            addr = addr * 10 + (s[j] - '0');
            if (addr > 0 && addr <= 0xFF) {
                path[cur] = addr;
                dfs(s, cur + 1, j + 1);
            } else {
                break;
            }
        }
    }

    vector<string> restoreIpAddresses(string s) {
        path.resize(4);
        dfs(s, 0, 0);
        return ret;
    }
};