# 0314【LeetCode 算法笔记】Task01 51 - 62

155 阅读24分钟

开源内容:github.com/datawhalech…

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

07.01

对应题解 速查链接

————————————

🚩 —— day1

3. 无重复字符的最长子串 【滑动窗口】

image.png

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 滑动窗口  O(N)  O(字符集)
        # 
        c = set()  # 记录 当前窗口里的字符, 用于 判断是否重复
        n = len(s)
        right = -1
        res = 0 
        for left in range(n):  # 依次 以 s[i] 为 窗口左侧
            if left != 0: # 不是第一次进这个 循环
                c.remove(s[left - 1])
            
            while right + 1 < n and s[right + 1] not in c: # 判断 能否 扩大窗口
                c.add(s[right + 1])
                right += 1

            res = max(res, right - left + 1)

        return res 

image.png

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if (!s.size()){
            return 0;
        }
        unordered_set<char> occ; // 记录 当前窗口中的字符
        int n = s.size();

        int right = -1, res = 0;
        for (int left = 0; left < n; ++left){
            if (left != 0){
                occ.erase(s[left - 1]); // 不是 第一次进该循环,要删掉 前一循环的左侧
            }
            
            while (right + 1 < n && !occ.count(s[right + 1])){// 判断 能否 右扩 窗口
                occ.insert(s[right + 1]);
                ++right;
            }
            res = max(res, right - left + 1);
        }

        return res;
    }
};

5. 最长回文子串

方法一: 动态规划 O(n2) \lgroup O(n^2) \rgroup

class Solution:
    def longestPalindrome(self, s: str) -> str:
        # 动态规划  O(n^2)
        # 长度 > 2 的 回文串 去掉两端后, 仍然 是 回文串
        # dp[i][j]: 索引 在 [i, j] 之间的子串 是否为 回文串
        # 情况1: 长度 大于 2  dp[i][j] = (dp[i+1][j -1] and s[i] == s[j])
        # 情况2: 长度为1 必为 回文串
        #  情况3:  长度 为2  dp[i][i+1]  = (s[i] == s[i + 1])

        n = len(s)
        if n < 2:
            return s

        max_len = 1
        max_left = 0  # 记录 最长回文子串的左侧 索引,因为 要返回 子串
        dp = [[False] * n for _ in range(n)]
        for i in range(n):
            dp[i][i] = True  # 长度为 1

        for right in range(1, n):
            for left in range(right):
                if s[left] == s[right]:
                    if right - left <= 2:
                        dp[left][right] = True 
                    else:
                        dp[left][right] = dp[left + 1][right - 1]

                if dp[left][right] and right - left + 1 > max_len:
                    max_len = right - left + 1
                    max_left = left

        return s[max_left : max_left + max_len]

写法二: 固定 滑动窗口 宽度

class Solution:
    def longestPalindrome(self, s: str) -> str:
        # 动态规划  O(n^2)
        # 长度 > 2 的 回文串 去掉两端后, 仍然 是 回文串
        # dp[i][j]: 索引 在 [i, j] 之间的子串 是否为 回文串
        # 情况1: 长度 大于 2  dp[i][j] = (dp[i+1][j -1] and s[i] == s[j])
        # 情况2: 长度为1 必为 回文串
        #  情况3:  长度 为2  dp[i][i+1]  = (s[i] == s[i + 1])

        n = len(s)
        if n < 2:
            return s

        max_len = 1
        max_left = 0  # 记录 最长回文子串的左侧 索引,因为 要返回 子串
        dp = [[False] * n for _ in range(n)]
        for i in range(n):
            dp[i][i] = True  # 长度为 1

        # 更新 dp  滑动窗口 模拟。  
        for w in range(2, n + 1):  # 滑动窗口 宽度
            for left in range(0, n - w + 1): # 窗口左侧  遍历滑动  # right = left + w - 1 < n
                right = left + w - 1
                if s[left] == s[right]:
                    if right - left <= 2:
                        dp[left][right] = True 
                    else:
                        dp[left][right] = dp[left + 1][right - 1]

                if dp[left][right] and right - left + 1 > max_len:
                    max_len = right - left + 1
                    max_left = left

        return s[max_left : max_left + max_len]

image.png

class Solution {
public:
    string longestPalindrome(string s) {
        // 动态规划  O(n^2)
        int n = s.size();
        if (n < 2){
            return s;
        }

        int max_len = 1; // 最长回文串的长度
        int begin = 0;  // 最长回文串 的起点
        vector<vector<bool>> dp(n, vector(n, false));
        for (int i = 0; i < n; ++i){
            dp[i][i] = true;
        }

        // 更新 dp 
        for (int right = 1; right < n; ++right){// 子串 左坐标
            for (int left = 0; left < right; ++left){// 子串 右坐标
                if (s[left] == s[right]){
                    if (right - left <= 2){// 012 aba 01 aa 这样 去掉两端 为一个字符 为空必 为 回文串
                        dp[left][right] = true;
                    }else{
                        dp[left][right] = dp[left + 1][right - 1];
                    }

                }
                if (dp[left][right] && right - left + 1 > max_len){
                    max_len = right - left + 1;
                    begin = left;
                }
            }
        }
        return s.substr(begin, max_len);// 起始索引, 长度
    }
};

⭐ 方法二:中心扩展法 【依次为中心进行扩展】 O(n2)O(1) \lgroup O(n^2)、O(1) \rgroup

class Solution:
    def longestPalindrome(self, s: str) -> str:
        # 中心扩展法  O(n^2) O(1)  要返回子串  所以返回索引
        start = 0
        end = 0
        for i in range(len(s)):
            left1, right1 = self.expandAroundCenter(s, i, i) # 依次 为中心
            left2, right2 = self.expandAroundCenter(s, i, i + 1)  # 中心为 偶数 情况
            if right1 - left1 > end - start:
                start, end = left1, right1 
            if right2 - left2 > end - start:
                start, end = left2, right2 

        return s[start : end + 1]  # 注意 左闭右开


    def expandAroundCenter(self, s, left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return left + 1, right - 1

image.png

class Solution {
public:
    string longestPalindrome(string s) {
        int start = 0, end = 0;
        for (int i = 0; i < s.size(); ++i){
            auto [left1, right1] = expandAroundCenter(s, i, i);
            auto [left2, right2] = expandAroundCenter(s, i, i + 1);
            if (right1 - left1 > end - start){
                start = left1;
                end = right1;
            }
            if (right2 - left2 > end - start){
                start = left2;
                end = right2;
            }
        }
        return s.substr(start, end - start + 1);  // 注意参数 是 位置, 元素个数
    }

    pair<int, int> expandAroundCenter(const string& s, int left, int right){
        while (left >= 0 && right < s.size() && s[left] == s[right]){
            --left;
            ++right;
        }
        return {left + 1, right - 1};
    }
};

⭐ 方法三:Manacher 算法 O(n)\lgroup O(n) \rgroup 【空间换时间】

class Solution:
    def longestPalindrome(self, s: str) -> str:
        # Manacher 算法  O(n)
        # 1、插入字符 统一成奇数情形
        # 2、记录 回文半径, 缩小计算量

        # 回文半径  d  回文长度为 2 * d + 1

        # 插入 特殊字符,使得 总长度为 奇、
        s = '#' + '#'.join(list(s)) + '#'
        print(s)
        ## 维护 盒子 左右侧  已经计算到的回文串 最右侧
        start, end = 0, -1  # 最长回文子串 的起止索引
        left, right = 0, -1  # 盒子的左右侧

        #  更新 d
        d = [] 
        d.append(1)
        for i in range(1, len(s)): # 中心在 s[i] 的回文串
            if i <= right: # 中心在 盒内
                mirror_left = left + right - i # i 相对于 盒子中心 在左侧的位置,因为 左侧已计算过
                min_d = min(d[mirror_left], right - i)  # 盒子内的部分
                cur_d = self.expandAroundCenter(s, i - min_d, i + min_d) # 扩展 盒外的  i - min_d  i i + min_d
            else:# 和盒子 没交集,直接中心扩展
                cur_d = self.expandAroundCenter(s, i, i)
            d.append(cur_d)
            
            # 注意 要在循环里 判断
            if i + cur_d > right: # 更新 盒子的边界  以右侧为准
                left = i - cur_d
                right = i + cur_d

            if right - left > end - start:
                start = left 
                end = right 

        return s[start + 1 : end + 1 : 2]


    def expandAroundCenter(self, s, left, right): # 盒外 需要 中心扩展
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1

        return (right - left - 2) // 2 # 回文半径 (right - 1 - (left + 1))//2

image.png

class Solution {
public:
    string longestPalindrome(string s) {
        // Manacher 算法
        
        // 预处理 字符串
        string t = "#";
        for (char c : s){
            t += c;
            t += "#";
        }
        t += "#";
        s = t;

        vector<int> d; // 记录回文半径
        d.emplace_back(1);

        int start = 0, end = -1; // 最大回文串的 起止索引
        int left = 0, right = -1;  // 记录 最右侧的回文串起止下标  【盒子】
        for (int i = 1; i < s.size(); ++i){
            int cur_d;
            if (i <= right){// 中心 s[i] 在 盒子 里
                int mirror_left = left + right - i;  // i 在 盒子中 左侧对应的回文位置
                int min_d = min(d[mirror_left], right - i);
                cur_d = expandAroundCenter(s, i - min_d, i + min_d);
            }else{
                cur_d = expandAroundCenter(s, i, i);
            }

            d.emplace_back(cur_d);
            // 更新 盒子
            if (i + cur_d > right){
                right = i + cur_d;
                left = i - cur_d;
            }

            // 更新 最大回文串
            if (right - left > end - start){
                start = left;
                end = right;
            }
        }

        string ret; // return 
        for (int i = start; i <= end; ++i){
            if (s[i] != '#'){
                ret += s[i];
            }
        }
        return ret;
    }

    // 中心扩展 模块
    int expandAroundCenter(const string& s, int left, int right){
        while (left >= 0 && right < s.size() && s[left] == s[right]){
            --left;
            ++right;
        }
        return (right - left - 2) / 2;
    }
};

8. 字符串转换整数 (atoi) 【自动机】 O(n)O(1)\lgroup O(n)、O(1) \rgroup

image.png

INT_MAX = 2 ** 31 - 1
INT_MIN = -2 ** 31

class Automaton:
    def __init__(self):
        self.state = 'start'
        self.sign = 1
        self.ret = 0  # 返回 数
        self.table = {
            'start': ['start', 'signed', 'in_number', 'end'],
            'signed': ['end', 'end', 'in_number', 'end'],
            'in_number': ['end', 'end', 'in_number', 'end'],
            'end': ['end', 'end', 'end', 'end']
        }

    def get_col(self, c): # 获取 下一个状态 的纵轴 索引
        if c.isspace():
            return 0
        if c == '+'or c == '-':
            return 1
        if c.isdigit():
            return 2
        return 3

    def get(self, c):
        self.state = self.table[self.state][self.get_col(c)]
        if self.state == 'in_number':
            self.ret = self.ret * 10 + int(c)
            self.ret = min(self.ret, INT_MAX) if self.sign == 1 else min(self.ret, -INT_MIN) # 截断
        if self.state == 'signed':
            self.sign = 1 if c == '+' else -1 

class Solution:
    def myAtoi(self, s: str) -> int:
        # 自动机  O(n)、O(1)
        automaton = Automaton()
        for c in s:
            automaton.get(c)
        return automaton.sign * automaton.ret  # return 

image.png

class Automaton{
    string state = "start";
    unordered_map<string, vector<string>> table = {
        {"start", {"start", "signed", "in_number", "end"}},
        {"signed", {"end", "end", "in_number", "end"}},
        {"in_number", {"end", "end", "in_number", "end"}},
        {"end", {"end", "end", "end", "end"}}
    };

    int get_col(char c){// 获取 columns
        if (isspace(c)) return 0;
        if (c == '+' or c == '-') return 1;
        if (isdigit(c)) return 2;
        return 3;
    }

public:
    int sign = 1;
    long long res = 0;

    void get(char c){// 状态切换
        state = table[state][get_col(c)];
        if (state == "in_number"){
            res = res * 10 + c -'0';
            res = sign == 1 ? min(res, (long long)INT_MAX) : min(res, -(long long)INT_MIN);
        }
        else if (state == "signed")
            sign = c == '+' ? 1 : -1;
    }

};



class Solution {
public:
    int myAtoi(string s) {
        Automaton automaton;
        for (char c : s){
            automaton.get(c);
        }
        return automaton.sign * automaton.res;
    }
};

🚩 —— day2

151. 反转字符串中的单词

有些语言的字符串不可变(如 Java 和 Python),有些语言的字符串可变(如 C++)。

对于字符串不可变的语言,首先得把字符串转化成其他可变的数据结构,同时还需要在转化的过程中去除空格。

对于字符串可变的语言,就不需要再额外开辟空间了,直接在字符串上原地实现。在这种情况下,反转字符和去除空格可以一起完成。

image.png

Python 【字符串不可变】

方法一: 空格分割 + 反转 + 拼接 O(n)\lgroup O(n) \rgroup

class Solution:
    def reverseWords(self, s: str) -> str:
        return " ".join(reversed(s.split()))

image.png

class Solution:
    def reverseWords(self, s: str) -> str:
        lis = s.split() # 这个切割 现在 只输出 非零部分
        res = []
        for i in range(len(lis)-1, -1, -1):
            res.append(lis[i])

        return ' '.join(res)

⭐ 方法二:双指针 + 临时存储单词容器 list() O(n) \lgroup O(n)\rgroup

class Solution:
    def reverseWords(self, s: str) -> str:
        # 双指针 + 需临时存储空间 字符串 O(n) O(n)
        # 从 后面 开始遍历
        start = len(s) - 1  # 单词的开始索引位置    从后面开始遍历 。 因为 后面的单词要放前面
        # 去掉 后侧 空格
        while start >= 0 and s[start] == ' ':  
            start -= 1
        end = start # 单词的尾索引

        res = []
        while start >= 0:
            while start >= 0 and s[start] != ' ': start -= 1  # 单词的起始索引
            res.append(s[start + 1 : end + 1])  # 注意 前闭后开   此时 s[i] 为空格
            while start >= 0 and s[start] == ' ': start -= 1  # 跳 空格 
            end = start # 找到 单词的尾索引了

        return ' '.join(res)

C++ 【字符串可变——>可原地修改】

⭐ 方法:双指针 O(n)O(1) \lgroup O(n)、O(1) \rgroup

image.png

class Solution {
public:
    string reverseWords(string s) {
        // O(n) O(1)
        // Step1: 直接 反转整个字符串。 可在原始字符串  直接修改
        // Step2:  遍历    反转单词,删除中间多余空格
        // Step3: 去掉 后面的空格

        // 反转 整个字符串
        reverse(s.begin(), s.end()); //  原地 反转  重要!!!

        int n = s.size();
        int idx = 0; // 记录 当前已修改好的 s 索引
        
        for (int start = 0; start < n; ++start){// 单词的起始 索引
            if (s[start] != ' '){
                if (idx != 0){// 说明前面有一个单词了。 加一个空格
                    s[idx++] = ' ';
                }

                // 找单词 尾部
                int end = start;
                while (end < n && s[end] != ' '){
                    s[idx++] = s[end++];   // 先填充,后续反转。可能不是之前的位置了。
                }

                // 反转 整个单词
                reverse(s.begin() + idx - (end - start), s.begin() + idx);

                // 更新 start, 找下一个单词
                start = end;
            }
        } 

        // Step 3: 删掉 后面的空格
        s.erase(s.begin() + idx, s.end());
        return s;
    }
};

43. 字符串相乘 【乘法竖式模拟 O(mn)O(m+n)\lgroup O(mn)、O(m + n)\rgroup

image.png image.png

class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        # 竖式 乘法 模拟
        if num1 == "0" or num2 == "0":
            return "0"

        m = len(num1)
        n = len(num2)
        res = [0] * (m + n)
        for i in range(m - 1, -1, -1):  # 注意 从后面开始遍历
            for j in range(n - 1, -1, -1):
                res[i + j + 1] += int(num1[i]) * int(num2[j])  # 先 都加在 个位 相应位置, 后面再进位。
                # res[i + j + 1] += cur % 10
                # res[i + j] += cur // 10

        # 不能一股脑 直接加,要考虑进位
        # 
        for i in range(m + n - 1, 0, -1):
            res[i - 1] += res[i] // 10
            res[i] %= 10

        idx = 1 if res[0] == 0 else 0
        s = "".join([str(num) for num in res[idx:]])
        return s 

image.png

————————

以下写法 放在循环里面,多了好多次取余求商计算

写法二:

class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        # 竖式 乘法 模拟
        if num1 == "0" or num2 == "0":
            return "0"

        m = len(num1)
        n = len(num2)
        res = ['0'] * (m + n) # 存为 字符 方便计算
        for i in range(m - 1, -1, -1):  # 注意 从后面开始遍历
            for j in range(n - 1, -1, -1):
                cur = int(res[i + j + 1]) + int(num1[i]) * int(num2[j])  # 先 都加在 个位 相应位置, 后面再进位。
                res[i + j + 1] = str(cur % 10)
                res[i + j] = str(int(res[i + j]) + cur // 10)

        idx = 1 if res[0] == '0' else 0
        s = "".join([str(num) for num in res[idx:]])
        return s

写法三:

class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        # 竖式 乘法 模拟
        if num1 == "0" or num2 == "0":
            return "0"

        m = len(num1)
        n = len(num2)
        res = [0] * (m + n)
        for i in range(m - 1, -1, -1):  # 注意 从后面开始遍历
            for j in range(n - 1, -1, -1):
                cur = res[i + j + 1] + int(num1[i]) * int(num2[j])
                res[i + j + 1] = cur % 10  # 先 都加在 个位 相应位置, 后面再进位。
                res[i + j] += cur // 10

        idx = 1 if res[0] == 0 else 0
        s = "".join([str(num) for num in res[idx:]])
        return s 

image.png

C++ 字符&整数 切换

class Solution {
public:
    string multiply(string num1, string num2) {
        if (num1 == "0" || num2 == "0"){
            return "0";
        }
        int m = num1.size(), n = num2.size();
        string res(m + n, '0');
        for(int i = n - 1; i >= 0; --i){
            for(int j = m - 1; j >= 0 ; --j){
                int temp = (res[i + j + 1] - '0') + (num1[j] - '0') * (num2[i] - '0');
                res[i + j + 1] = temp % 10 + '0';//当前位
                res[i + j] += temp / 10; //前一位加上进位,res[i + j]已经初始化为'0',加上int类型自动转化为char,所以此处不加'0'
            }
        }
        return res[0] == '0' ? res.substr(1) : res;
    }
};

image.png

class Solution {
public:
    string multiply(string num1, string num2) {
        if (num1 == "0" || num2 == "0") {
            return "0";
        }
        int m = num1.size(), n = num2.size();
        vector<int> res(m + n, 0);
        for (int i = m - 1; i >= 0; --i) {
            for (int j = n - 1; j >= 0; --j) {
                res[i + j + 1] += (num1[i] - '0') * (num2[j] - '0');
            }
        }
        for (int i = m + n - 1; i > 0; --i) {
            res[i - 1] += res[i] / 10;
            res[i] %= 10;
        }
        int idx = res[0] == 0 ? 1 : 0;
        string ans;
        while (idx < m + n) {
            ans.push_back(res[idx] + '0');// 转成字符 
            idx++;
        }
        return ans;
    }
};

14. 最长公共前缀

方法一: 横向扫描 pre O(mn)O(1) \lgroup O(mn)、O(1) \rgroup

image.png

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        # 横向, 依次求前缀
        n = len(strs)
        pre = strs[0]
        for i in range(1, n):
            pre = self.lcp(pre, strs[i])
            if not pre:  ## 重要, 前缀已为 空,后续再比较,意义不大
                break 
        return pre 

    def lcp(self, s1, s2):
        n = min(len(s1), len(s2))
        idx = 0 
        while idx < n and s1[idx] == s2[idx]:
            idx += 1
        return s1[:idx]

image.png

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        // 横向比较 O(mn)、O(1)
        int n = strs.size();
        string pre = strs[0];
        for (int i = 1; i < n; ++i){
            pre = lcp(pre, strs[i]);
            if (!pre.size()){
                break;
            }
        }
        return pre;
    }

    string lcp(const string& s1, const string& s2){
        int n = min(s1.size(), s2.size());
        int idx = 0;
        while (idx < n && s1[idx] == s2[idx]){
            ++idx;
        }
        return s1.substr(0, idx);
    }
};

⭐ 方法二: 纵向扫描 O(mn)O(1) \lgroup O(mn)、O(1) \rgroup

image.png

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        n = len(strs)
        n0 = len(strs[0]) # 以  strs[0] 为基准, 依次确认 每一个字母。 公共前缀 长度必定不会超过 strs[0]的长度
        for i in range(n0):
            c = strs[0][i]  # 确认 后面所有的单词 的 第 i 位 是否 都和 strs[0] 的一样
            for j in range(1, n): # 
                if (i == len(strs[j]) or strs[j][i] != c):
                    return strs[0][:i]

        return strs[0]  # 若是 上面 没有返回,说明 所有的单词 的 第 i 位  都和 strs[0] 的一样。直接 返回 strs[0]

image.png

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        // 纵向比较: 取每一个单词的同一位置的字母,看是否相同。
        int n = strs.size(), n0 = strs[0].size();
        for (int i = 0; i < n0; ++i){// 逐个字符 进行比较 确认
            char c = strs[0][i];
            for (int j = 1; j < n; ++j){// 确认后面 每个单词的 第 i 位 是否都是 c
                if (i == strs[j].size() || strs[j][i] != c){// 后续长度不够了, 或者 不等了
                    return strs[0].substr(0, i);
                }
            }
        }
        return strs[0]; // 经过上述判断,确认了前缀都和 strs[0] 一样, 返回。
    }
};

——————

以下 是 无情的敲代码机器 🤣

方法三: 分治

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        # 分治  O(mn)、O(m log n)
        def lcp(start, end): # 分成 两两 进行比较
            if start == end:
                return strs[start]

            mid = (start + end) // 2
            lcpLeft, lcpRight = lcp(start, mid), lcp(mid + 1, end) # 不断分治
            # 求解 两边 前缀 的公共前缀
            minLength = min(len(lcpLeft), len(lcpRight))
            for i in range(minLength):
                if lcpLeft[i] != lcpRight[i]:
                    return lcpLeft[: i]
            return lcpLeft[:minLength]  # 两者的前缀为 lcpLeft, lcpRight 中的一个

        return lcp(0, len(strs) - 1)

image.png

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        // 题目 描述 保证 strs 非空
        return lcp(strs, 0, strs.size() - 1); // 分治 O(mn)、O(m log n)  n: 单词数量 
    }

    string lcp(const vector<string>& strs, int start, int end){
        if (start == end){
            return strs[start];
        }else{
            int mid = start + (end - start) / 2;
            string lcpLeft = lcp(strs, start, mid), lcpRight = lcp(strs, mid + 1, end);

            // 求 公共前缀
            int minLength = min(lcpLeft.size(), lcpRight.size());
            for (int i = 0; i < minLength; ++i){
                if (lcpLeft[i] != lcpRight[i]){
                    return lcpLeft.substr(0, i);
                }
            }
            return lcpLeft.substr(0, minLength);  // 不确定 左边 和 右边 哪个 更短
        }
    }
};

方法四: 二分查找 【把 基准单词 拆成两半】

image.png

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        # 二分查找 O(mn log m) O(1)
        # Step1 :  strs 中 最短的字符串 长度为 minLength
        # Step2: 中间 为 mid, 每个字符串 长度为 mid 的前缀 是否相同
        #   相同  >= mid    不同 < mid  缩小了查找范围    O(log m)
        def isCommonPrefix(mid): #
            # 基准 
            s0 = strs[0][:mid]
            n = len(strs)
            for i in range(1, n):# 依次 确认 长度 为 mid 的前缀 是不是都一样
                if strs[i][: mid] != s0:
                    return False 
            return True 

        minLength = min(len(s) for s in strs) # strs 中 最短单词 的长度
        left = 0
        right = minLength 
        while left < right:  # 边界 不好确定
            mid = (right - left + 1) // 2 + left  # ??? (left + right + 1) // 2  左边长 
            if isCommonPrefix(mid):
                left = mid
            else:
                right = mid - 1

        return strs[0][:left]

image.png

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        // 二分 O(mn log m), O(1)

        int minLength = min_element(strs.begin(), strs.end(),[](const string& s, const string& t) {return s.size() < t.size();})->size();
        int left = 0, right = minLength;
        while (left < right){
            int mid = (right - left + 1) / 2 + left;
            if (isCommonPrefix(strs, mid)){
                left = mid; // 只关心 left 在哪里
            }else{
                right = mid - 1; // 缩小范围
            }
        }
        return strs[0].substr(0, left);
    }

    bool isCommonPrefix(const vector<string>& strs, int mid){
        string s0 = strs[0].substr(0, mid);
        int n = strs.size();
        for (int i = 0; i < mid; ++i){// 确认 是否所有单词 都具有 相同的长度为 mid 的前缀
            for (int j = 0; j < n; ++j){
                if (s0[i] != strs[j][i]){
                    return false;
                }
            }
        }
        return true;
    }
};

其它

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        ans = ''
        for temp in list(zip(*strs)):  # 例 2 的 list(zip(*strs)):  [('d', 'r', 'c'), ('o', 'a', 'a'), ('g', 'c', 'r')]
            if len(set(temp)) == 1:
                ans += temp[0]
            else:
                break
        return ans

示例 2:

输入: strs = ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。

🚩 —— day3

144. 二叉树的前序遍历

递归 O(n)O(n)

# 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 preorder(self, root, ret):
        if not root:
            return 

        ret.append(root.val)
        self.preorder(root.left, ret)
        self.preorder(root.right, ret)

    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ret = []
        self.preorder(root, ret)
        return ret
/**
 * 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:
    void preorder(TreeNode* cur, vector<int>& ret){// 注意要 引用
        if (!cur){
            return;
        }
        ret.emplace_back(cur->val);
        preorder(cur->left, ret);
        preorder(cur->right, ret);
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ret;
        preorder(root, ret);
        return ret;
    }
};

迭代 O(n)O(n)

# 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 preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 根 左右
        ret = []
        if not root:
            return []

        stack = []  # 存储 根节点
        node = root 
        while stack or node:
            while node:
                ret.append(node.val)
                stack.append(node)
                node = node.left   # 存储 左子结点
            node = stack.pop()
            node = node.right   # 遍历 右子节点

        return ret 
/**
 * 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:
    vector<int> preorderTraversal(TreeNode* root) {
        // 根 左右
        vector<int> ret;
        if (!root){
            return ret;
        }

        stack<TreeNode*> stk;
        TreeNode* cur = root;
        while (!stk.empty() || cur){
            while (cur){
                ret.emplace_back(cur->val);
                stk.emplace(cur);
                cur = cur->left;
            }
            cur = stk.top(); stk.pop();
            cur = cur->right; 
        }
        return ret;
        
    }
};

Morris 遍历 O(n)O(1)O(n)、 O(1)

# 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 preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # Morris 遍历  利用树的闲置指针  根 左 右
        
        ret = []
        cur = root
        pre = None 
        while cur:
            if not cur.left: # 只需 处理 根 和 右子节点
                ret.append(cur.val) # 
                cur = cur.right 

            else:
                # 找 pre  找 左子树 的 mostRight 
                pre = cur.left 
                while pre.right and pre.right != cur:
                    pre = pre.right 
                if not pre.right:  # mostRight 的 右子节点 为空
                    pre.right = cur
                    ret.append(cur.val)  ## 这里 处理 根
                    cur = cur.left  # 这里 开始处理 左子树
                else:
                    pre.right = None  # 断开 与 cur 的链接
                    # ret.append(cur.val)    中序遍历 是在 这里 加
                    cur = cur.right   # 直接 遍历 右子树

        return ret 
/**
 * 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:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (!root){
            return ret;
        }

        TreeNode* cur = root;
        TreeNode* pre = nullptr;
        while (cur){// 根 左右
            if (!cur->left){
                ret.emplace_back(cur->val);
                cur = cur->right;
            }else{
                pre = cur->left;
                while (pre->right && pre->right != cur){
                    pre = pre->right;
                }

                if (!pre->right){// mostRight 的右节点 为空, 直接 链接到 cur
                    pre->right = cur;
                    ret.emplace_back(cur->val);
                    cur = cur->left;
                }else{// 左子树 遍历完成
                    pre->right = nullptr;
                    cur = cur->right;
                }
            }
        }
        return ret;
    }
};

94. 二叉树的中序遍历

递归 O(n)O(n)

# 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 inorder(self, root, ret):
        if not root:
            return 
        self.inorder(root.left, ret)
        ret.append(root.val)
        self.inorder(root.right, ret)

    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 中序遍历: 左 根 右
        ret = []
        self.inorder(root, ret)
        return ret 
/**
 * 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:
    void inorder(TreeNode* cur, vector<int>& ret){
        if (!cur){
            return;
        }
        inorder(cur->left, ret);
        ret.emplace_back(cur->val);
        inorder(cur->right, ret);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;
        inorder(root, ret);
        return ret;
    }
};

迭代 O(n)O(n)

# 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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ret = []
        if not root:
            return ret 

        stack = [] # 存储 根节点
        node = root
        while stack or node:
            while node:
                stack.append(node)  # 存储 根节点
                node = node.left                 

            node = stack.pop()
            ret.append(node.val)
            node = node.right 

        return ret
/**
 * 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:
    vector<int> inorderTraversal(TreeNode* root) {
        // 左 根 右
        vector<int> ret;
        if (!root){
            return ret;
        }

        stack<TreeNode*> stk;
        TreeNode* cur = root;
        while (!stk.empty() || cur){
            while (cur){
                stk.emplace(cur);
                cur = cur->left;
            }

            cur = stk.top(); stk.pop();
            ret.emplace_back(cur->val);  // 根
            cur = cur->right; // 右
        }
        return ret;
    }
};

Morris 遍历 O(n)O(1)O(n)、 O(1)

# 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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        #  左 根 右
        ret = []
        cur = root 
        pre = None  
        while cur:
            if not cur.left: # 只需 处理 根 和 右子节点
                ret.append(cur.val)
                cur = cur.right 

            else:
                # 找 pre  左子树的mostRight 
                pre = cur.left 
                while pre.right and pre.right != cur:
                    pre = pre.right 

                if not pre.right: # 该结点 的右节点 为空
                    pre.right = cur 
                    cur = cur.left 
                else:  # 左子树 已处理完毕
                    pre.right = None 
                    ret.append(cur.val)
                    cur = cur.right 
        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:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (!root){
            return ret;
        }

        TreeNode* cur = root;
        TreeNode* pre = nullptr;
        while (cur){// 左 根 右
            if (!cur->left){
                ret.emplace_back(cur->val);
                cur = cur->right;                
            }else{
                pre = cur->left;
                while (pre->right and pre->right != cur){
                    pre = pre->right;
                }
                if (!pre->right){// 链接
                    pre->right = cur;
                    cur = cur->left;
                }else{// 左子树 遍历完成
                    pre->right = nullptr;
                    ret.emplace_back(cur->val);
                    cur = cur->right;
                }
            }
        }
        return ret;
    }
};

145. 二叉树的后序遍历

递归 O(n)O(n)

# 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 postorder(self, root, ret):
        if not root:
            return 
        self.postorder(root.left, ret)
        self.postorder(root.right, ret)
        ret.append(root.val)

    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ret = []
        self.postorder(root, ret)
        return ret
/**
 * 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:
    void postorder(TreeNode* cur, vector<int>& ret){
        if (!cur){
            return;
        }
        postorder(cur->left, ret);
        postorder(cur->right, ret);
        ret.emplace_back(cur->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;
        postorder(root, ret);
        return ret;
    }
};

迭代 O(n)O(n)

class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        
        ret = []
        stack = []
        prev = None  # 用于 判断 右子树 是否 已遍历

        node = root
        while node or stack:
            while node:
                stack.append(node)
                node = node.left
            node = stack.pop()
            if not node.right or node.right == prev:
                ret.append(node.val)
                prev = node
                node = None
            else:
                stack.append(node)
                node = node.right
        
        return ret
/**
 * 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:
    vector<int> postorderTraversal(TreeNode* root) {
        // 左右根
        vector<int> ret;
        if (!root){
            return ret;
        }

        TreeNode* pre = nullptr;
        TreeNode* cur = root;
        stack<TreeNode*> stk;

        while (!stk.empty() || cur){
            while (cur){
                stk.emplace(cur);
                cur = cur->left;
            }

            cur = stk.top(); stk.pop();
            if (!cur->right || cur->right == pre){// 右子树 已处理
                ret.emplace_back(cur->val);
                pre = cur;
                cur = nullptr;
            }else{// 处理 右子树
                stk.emplace(cur);
                cur = cur->right;
            }
        }  
        return ret; 

    }
};

Morris 遍历 O(n)O(1)O(n)、 O(1)

# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 后序 左 右根   中序: 左 根右
        # 不同的地方: pre.right == cur 时, 要  倒序 输出 pre  到 cur 的 路径结点。 其实 就是 将 根右 转成 右根 

        # 逆序打印 模块
        def addPath(node):  # cur ——> pre
            cnt = 0  # cur 结点在的层数
            while node:
                cnt += 1
                ret.append(node.val)
                node = node.right 
            i, j = len(ret) - cnt, len(ret) - 1
            while i < j:
                ret[i], ret[j] = ret[j], ret[i]
                i += 1
                j -= 1


        ret = []
        cur = root 
        pre = None 
        while cur:
            if not cur.left:
                cur = cur.right 
                # 这里 没处理
            else:
                pre = cur.left
                while pre.right and pre.right != cur:
                    pre = pre.right 
                if not pre.right:
                    pre.right = cur 
                    cur = cur.left 
                else:
                    pre.right = None 
                    addPath(cur.left)  ## 
                    cur = cur.right 
        addPath(root)  ##
        return ret
/**
 * 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:
    // 子模块  逆序打印 结点
    void addPath(TreeNode* cur, vector<int>& ret){
        int cnt = 0;
        while (cur){
            cnt += 1;
            ret.emplace_back(cur->val);
            cur = cur->right;
        }
        reverse(ret.end() - cnt, ret.end());  //原地 逆序
    }
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (!root){
            return ret;
        }

        TreeNode* pre = nullptr;
        TreeNode* cur = root;

        while (cur){// 左 右 根 
            if (!cur->left){
                cur = cur->right;
            }else{
                pre = cur->left;
                while (pre->right && pre->right != cur){
                    pre = pre->right;
                }
                if (!pre->right){// 连接
                    pre->right = cur;
                    cur = cur->left;
                }else{
                    pre->right= nullptr;
                    addPath(cur->left, ret);
                    cur = cur->right;
                }
            }
        }
        addPath(root, ret);
        return ret;
    }
};

102. 二叉树的层序遍历

两个 普通栈

# 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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []

        ret = []
        q = [root]
        while q:
            level = []
            nxt = []  # 临时存储
            for node in q:
                level.append(node.val)
                if node.left:
                    nxt.append(node.left)
                if node.right:
                    nxt.append(node.right)
            q = nxt
            ret.append(level)

        return ret 

写法二:一个双端队列

# 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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []

        ret = []
        q = collections.deque([root])
        while q:
            level = []
            n = len(q)
            for _ in range(n):
                node = q.popleft()
                level.append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)

            ret.append(level)

        return ret 

image.png

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode *root) {
        if (!root){
            return {};
        }

        vector<vector<int>> ret;
        vector<TreeNode*> q = {root};
        while (!q.empty()) {
            vector<TreeNode*> nxt;
            vector<int> level;
            for (auto node : q) {
                level.emplace_back(node->val);
                if (node->left)  nxt.emplace_back(node->left);
                if (node->right) nxt.emplace_back(node->right);
            }
            q = move(nxt);
            ret.emplace_back(level);
        }
        return ret;
    }
};

🚩 —— day4

103. 二叉树的锯齿形层序遍历 【层序遍历 + 双端队列 O(N) 】

Python: deuqe append() appendleft()

# 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 zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        ### 层序遍历 + 双端队列  O(N)
        # 左 右  右 左
        if not root:
            return []

        ret = []
        q = collections.deque([root]) # 存储 正常 遍历的结点
        isOrderLeft = True   # 标记
        while q:
            level = collections.deque()  # 通过 append  和 appendleft 调次序
            n = len(q)
            for _ in range(n):
                node = q.popleft()
                if isOrderLeft:
                    level.append(node.val)  # 右边先放  左-> 右
                else:
                    level.appendleft(node.val) # 左边 先放   右 -> 左

                if node.left:
                    q.append(node.left)                  
                if node.right:
                    q.append(node.right)                      

            ret.append(list(level))
            isOrderLeft = not isOrderLeft  # 下一层

        return ret 

image.png

C++ deque push_back() push_front()

/**
 * 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:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> ret;  // return 
        if (!root){
            return ret;
        }

        queue<TreeNode*> q;
        q.push(root);
        bool isOrderLeft = true;  // 该层 需要 从左到右遍历

        while (!q.empty()){
            deque<int> level; // 这里 是 deque   push_back   push_front
            int n = q.size();
            for (int i = 0; i < n; ++i){
                TreeNode* node = q.front(); q.pop();
                if (isOrderLeft){
                    level.push_back(node->val);
                }else{
                    level.push_front(node->val);
                }
                if (node->left){
                    q.push(node->left);
                }
                if (node->right){
                    q.push(node->right);
                }
            }
            ret.emplace_back(vector<int>{level.begin(), level.end()}); // 要转
            isOrderLeft = !isOrderLeft;
        }
        return ret;
    }
};

236. 二叉树的最近公共祖先 【递归 O(N)】

image.png

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root in (None, p, q):
            return root 

        left = self.lowestCommonAncestor(root.left, p, q) # 返回的可能 是 None p, q 
        right = self.lowestCommonAncestor(root.right, p, q)
        if left and right:  # 同时 都 不是 None ,那只能是 p, q  分列两边
            return root # 

        return left or right  # 

解法二: 预处理 记录 parent 结点

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        # 先用 哈希表 记录 parent 结点
        # 然后 p  往上跳, 记录已访问的。 然后让 q 往上跳  碰到已被访问过的 即是最近公共祖先

        pa = defaultdict()
        def dfs(root):
            if root.left:
                pa[root.left.val] = root   # 值唯一
                dfs(root.left)

            if root.right:
                pa[root.right.val] = root
                dfs(root.right)

        visited = set()
        dfs(root)
        while p:
            visited.add(p.val)
            p = pa.get(p.val) # 这里 不知道为啥  pa[p.val] 不行
            # p = pa[p.val]  # 报错 !!!
        
        while q:
            if q.val in visited:
                return q
            q = pa[q.val]

        return None

image.png

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root || root == p || root == q){
            return root;
        }
        TreeNode* left0 = lowestCommonAncestor(root->left, p, q);// 会返回 None, p, q 中的一个
        TreeNode* right0 = lowestCommonAncestor(root->right, p, q); // 会返回 None, p, q 中的一个
        if (left0 && right0){
            return root;
        } 
        return left0 ? left0 : right0;        
    }
};

解法二:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    unordered_map<int, TreeNode*> pa;
    unordered_map<int, bool> vis;
    void dfs(TreeNode* root){
        if (root->left){
            pa[root->left->val] = root;
            dfs(root->left);
        }
        if (root->right){
            pa[root->right->val] = root;
            dfs(root->right);
        }        
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        // 预处理  找到 每个结点 的pa 结点, 让 p 往上 跳, 记录经过的地方, 让 q 往上跳,遇到的第一个 被遍历过的结点 即为 公共祖先
       pa[root->val] = nullptr;
       dfs(root);
       while (p){
           vis[p->val] = true;
           p = pa[p->val];
       }
       while (q){
           if (vis[q->val]) return q;
           q = pa[q->val];
       }
       return nullptr;        
    }
};

104. 二叉树的最大深度

BFS 【层序遍历】 O(n)\lgroup O(n) \rgroup

python 用中间列表 比 长度遍历 好写。 因为不用弹出, C++ 也是的。

# 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 maxDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        res = 0
        q = [root] # 当前层 结点
        while q:
            res += 1
            nxt = []  # 
            for node in q: # 遍历当前层
                if node.left:
                    nxt.append(node.left)
                if node.right:
                    nxt.append(node.right)

            q = nxt  # 覆盖 ,为下一循环 做准备

        return res 

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 maxDepth(TreeNode* root) {
        // BFS 层序 遍历
        if (!root) return 0;

        int res = 0;
        vector<TreeNode*> q;
        q.emplace_back(root);
        while (!q.empty()){
            vector<TreeNode*> nxt; // 存储 下一层
            res += 1;
            for (TreeNode* node : q){
                if (node->left) nxt.emplace_back(node->left);
                if (node->right) nxt.emplace_back(node->right);
            }
            q = move(nxt);
        }
        return res;
    }
};

写法二:根据 长度 遍历

/**
 * 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 maxDepth(TreeNode* root) {
        // BFS 层序 遍历
        if (!root) return 0;

        int res = 0;
        queue<TreeNode*> q;  // back  front push pop size empty
        q.push(root);
        while (!q.empty()){
            res += 1;  // 层数 加1 
            int n = q.size();
            for (int i = 0; i < n; ++i){
                TreeNode* node = q.front(); q.pop();
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
        }
        return res;
    }
};

DFS O(n)O(height)\lgroup O(n)、 O(height) \rgroup

image.png

# 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 maxDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0 

        return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1

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 maxDepth(TreeNode* root) {
        if (!root){
            return 0;
        }
        return max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};