电子网站:datawhalechina.github.io/leetcode-no…
07.01
————————————
🚩 —— day1
√ 3. 无重复字符的最长子串 【滑动窗口】
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
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. 最长回文子串
方法一: 动态规划
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]
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);// 起始索引, 长度
}
};
⭐ 方法二:中心扩展法 【依次为中心进行扩展】
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
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 算法 【空间换时间】
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
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) 【自动机】
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
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++)。
对于字符串不可变的语言,首先得把字符串转化成其他可变的数据结构,同时还需要在转化的过程中去除空格。
对于字符串可变的语言,就不需要再额外开辟空间了,直接在字符串上原地实现。在这种情况下,反转字符和去除空格可以一起完成。
Python 【字符串不可变】
方法一: 空格分割 + 反转 + 拼接
class Solution:
def reverseWords(self, s: str) -> str:
return " ".join(reversed(s.split()))
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()
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++ 【字符串可变——>可原地修改】
⭐ 方法:双指针
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. 字符串相乘 【乘法竖式模拟 】
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
————————
以下写法 放在循环里面,多了好多次取余求商计算
写法二:
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
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;
}
};
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
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]
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);
}
};
⭐ 方法二: 纵向扫描
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]
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)
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); // 不确定 左边 和 右边 哪个 更短
}
}
};
方法四: 二分查找 【把 基准单词 拆成两半】
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]
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. 二叉树的前序遍历
递归
# 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;
}
};
迭代
# 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 遍历
# 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. 二叉树的中序遍历
递归
# 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;
}
};
迭代
# 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 遍历
# 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
/**
* 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. 二叉树的后序遍历
递归
# 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;
}
};
迭代
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 遍历
# 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
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
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)】
# 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
/**
* 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 【层序遍历】
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
中间栈
/**
* 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
# 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
/**
* 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;
}
};