最近在刷leetcode hot 100,想做一个记录,方便后面来复习不太熟练的题目,于是想通过python来爬取 leetcode hot 100 的题单,格式为 "- 1 两数之和、[两数之和](两数之和链接)"
借助Claude 编写代码,爬取成功,以下是程序代码:
"""
LeetCode Hot 100 题目爬虫
这个程序可以自动爬取 LeetCode 热门 100 题的题目列表
并保存为文本、CSV 和 JSON 三种格式
功能:绕过 Cloudflare 防护,自动爬取题目信息
"""
# ==================== 导入需要的库 ====================
# undetected_chromedriver: 可以绕过网站反爬虫检测的浏览器驱动
import undetected_chromedriver as uc
# selenium 的相关模块,用于自动化控制浏览器
from selenium.webdriver.common.by import By # 用于定位网页元素
from selenium.webdriver.support.ui import WebDriverWait # 用于等待页面加载
from selenium.webdriver.support import expected_conditions as EC # 用于判断元素是否加载完成
# 其他标准库
import time # 用于添加等待时间
import csv # 用于保存 CSV 格式文件
import json # 用于保存 JSON 格式文件
import re # 用于正则表达式匹配(提取题号和标题)
def crawl_leetcode_hot100():
"""
主要爬取函数 - 自动模式
这个函数会:
1. 自动打开浏览器
2. 访问 LeetCode Hot 100 页面
3. 等待 Cloudflare 验证通过
4. 滚动页面加载所有题目
5. 提取题目信息(编号、标题、链接)
6. 保存为三种格式的文件
返回值:题目列表(list),每个元素是一个字典,包含 id、title、url
"""
# ==================== 第一步:配置浏览器 ====================
# 创建浏览器配置选项
options = uc.ChromeOptions()
# 可选:无头模式(不显示浏览器窗口,取消注释下一行可启用)
# options.add_argument('--headless')
# 禁用自动化控制特征,让网站更难检测到我们是机器人
options.add_argument('--disable-blink-features=AutomationControlled')
print("正在初始化浏览器...")
# 创建浏览器实例(使用 undetected_chromedriver 来绕过反爬虫)
driver = uc.Chrome(options=options)
try:
# ==================== 第二步:访问目标网页 ====================
# LeetCode Hot 100 的页面地址
url = 'https://leetcode.cn/problem-list/2cktkvj/'
print(f"正在访问: {url}")
# 让浏览器打开这个网页
driver.get(url)
# ==================== 第三步:等待 Cloudflare 验证 ====================
print("等待页面加载(可能需要通过Cloudflare验证)...")
# 等待 10 秒,让页面有时间加载和通过验证
time.sleep(10)
# 检查页面源代码中是否还有 Cloudflare 的验证提示
if "cloudflare" in driver.page_source.lower() or "请稍候" in driver.page_source:
print("检测到Cloudflare防护,等待验证完成...")
# 如果还在验证,再多等 15 秒
time.sleep(15)
# ==================== 第四步:滚动页面加载所有内容 ====================
print("滚动页面加载内容...")
# 获取当前页面的高度
last_height = driver.execute_script("return document.body.scrollHeight")
scroll_attempts = 0 # 记录已经滚动了多少次
max_scrolls = 10 # 最多滚动 10 次
# 循环滚动页面,直到没有新内容加载或达到最大次数
while scroll_attempts < max_scrolls:
# 滚动到页面底部(这会触发加载更多内容)
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# 等待 2 秒让新内容加载
time.sleep(2)
# 获取滚动后的新页面高度
new_height = driver.execute_script("return document.body.scrollHeight")
# 如果页面高度没有变化,说明已经加载完所有内容
if new_height == last_height:
break
# 更新页面高度,继续下一次滚动
last_height = new_height
scroll_attempts += 1
print(f"滚动进度: {scroll_attempts}/{max_scrolls}")
# ==================== 第五步:查找题目元素 ====================
# 创建一个等待对象,最多等待 20 秒
wait = WebDriverWait(driver, 20)
elements = [] # 用于存储找到的题目元素
# 定义多个可能的选择器(因为网页结构可能变化)
# XPath 是一种定位网页元素的语言
selectors = [
"//a[contains(@href, '/problems/')]", # 查找所有包含 /problems/ 的链接
"//div[@role='row']//a", # 查找表格行中的链接
"//table//a[contains(@href, '/problems/')]", # 查找表格中的题目链接
"//div[contains(@class, 'question')]//a", # 查找问题类元素中的链接
]
# 尝试每一个选择器,直到找到元素
for selector in selectors:
try:
print(f"尝试选择器: {selector}")
# 使用当前选择器查找元素
found_elements = driver.find_elements(By.XPATH, selector)
# 如果找到了元素,就不再尝试其他选择器
if found_elements:
elements = found_elements
print(f"✓ 找到 {len(elements)} 个元素")
break
except Exception as e:
# 如果这个选择器失败了,继续尝试下一个
print(f"✗ 选择器失败: {e}")
continue
# 如果所有选择器都没找到元素
if not elements:
print("\n⚠️ 未找到题目元素,保存页面源代码用于调试...")
# 保存页面源代码,方便我们分析问题
with open('debug_page.html', 'w', encoding='utf-8') as f:
f.write(driver.page_source)
print("页面已保存到 debug_page.html")
return [] # 返回空列表
# ==================== 第六步:提取题目信息 ====================
problems = [] # 用于存储所有题目信息的列表
seen_urls = set() # 用于去重(set 不允许重复元素)
print("\n开始提取题目信息...")
# 遍历每一个找到的元素
for element in elements:
try:
# 获取链接地址
href = element.get_attribute('href')
# 过滤掉不符合要求的链接:
# 1. href 为空
# 2. 不包含 /problems/
# 3. 已经处理过(去重)
if not href or '/problems/' not in href or href in seen_urls:
continue
# 将这个链接加入已处理集合
seen_urls.add(href)
# 获取元素的文本内容(题目标题)
text = element.text.strip() # strip() 去掉首尾空格
# 如果文本为空,跳过这个元素
if not text:
continue
# ==================== 解析题目编号和标题 ====================
# LeetCode 的题目文本通常是:
# "160. 相交链表\n67.9%\n简单"
# 我们只需要第一行的 "160. 相交链表"
# split('\n') 按换行符分割,[0] 取第一行
text = text.split('\n')[0]
# 初始化题号和标题
problem_id = "" # 题目编号(如 "160")
problem_title = text # 题目标题(如 "相交链表")
# 使用正则表达式匹配 "数字. 标题" 的格式
# r'^(\d+)\.\s*(.+)$' 的含义:
# ^ : 从字符串开头开始匹配
# (\d+) : 匹配一个或多个数字,并捕获(括号表示捕获组)
# \. : 匹配一个点号(. 在正则中有特殊含义,需要转义)
# \s* : 匹配零个或多个空白字符
# (.+) : 匹配一个或多个任意字符,并捕获
# $ : 匹配到字符串末尾
match = re.match(r'^(\d+)\.\s*(.+)$', text)
if match:
# 如果匹配成功,提取题号和标题
problem_id = match.group(1) # 第一个捕获组:题号
problem_title = match.group(2).strip() # 第二个捕获组:标题
else:
# 如果没有匹配到标准格式,尝试从文本开头提取数字
id_match = re.match(r'^(\d+)', text)
if id_match:
problem_id = id_match.group(1)
# 移除题号和点号,得到标题
# len(problem_id) 是题号的长度
# lstrip('. ') 移除左边的点号和空格
problem_title = text[len(problem_id):].lstrip('. ')
# 将题目信息存储为字典
problems.append({
'id': problem_id, # 题目编号
'title': problem_title, # 题目标题
'url': href # 题目链接
})
# 在控制台显示进度
print(f"已爬取: [{problem_id}] {problem_title}")
except Exception as e:
# 如果提取某个元素失败,打印错误但继续处理下一个
print(f"提取元素失败: {e}")
continue
print(f"\n总共爬取到 {len(problems)} 道题目")
# ==================== 第七步:保存数据到文件 ====================
if problems: # 如果有题目数据
# --- 保存为纯文本格式(Markdown)---
txt_filename = 'leetcode_hot100.txt'
with open(txt_filename, 'w', encoding='utf-8') as f:
# 遍历每道题目
for p in problems:
# 生成 Markdown 格式的文本
# 格式:- 1 两数之和、[两数之和](链接)
if p['id']: # 如果有题号
line = f"- {p['id']} {p['title']}、[{p['title']}]({p['url']})\n"
else: # 如果没有题号
line = f"- {p['title']}、[{p['title']}]({p['url']})\n"
# 写入文件
f.write(line)
print(f"✓ 已保存到 {txt_filename}")
# --- 保存为 CSV 格式(表格)---
csv_filename = 'leetcode_hot100.csv'
with open(csv_filename, 'w', newline='', encoding='utf-8-sig') as f:
# 创建 CSV 写入器,指定列名
writer = csv.DictWriter(f, fieldnames=['id', 'title', 'url'])
writer.writeheader() # 写入表头
writer.writerows(problems) # 写入所有数据行
print(f"✓ 已保存到 {csv_filename}")
# --- 保存为 JSON 格式 ---
json_filename = 'leetcode_hot100.json'
with open(json_filename, 'w', encoding='utf-8') as f:
# ensure_ascii=False 保证中文正常显示
# indent=2 格式化输出,每一层缩进 2 个空格
json.dump(problems, f, ensure_ascii=False, indent=2)
print(f"✓ 已保存到 {json_filename}")
return problems # 返回题目列表
except Exception as e:
# ==================== 错误处理 ====================
print(f"\n❌ 发生错误: {str(e)}")
# 打印详细的错误堆栈信息
import traceback
traceback.print_exc()
# 保存出错时的页面源代码,方便调试
try:
with open('error_page.html', 'w', encoding='utf-8') as f:
f.write(driver.page_source)
print("错误页面已保存到 error_page.html")
except:
pass
return [] # 返回空列表
finally:
# ==================== 清理工作 ====================
# finally 块中的代码无论是否发生错误都会执行
print("\n关闭浏览器...")
driver.quit() # 关闭浏览器,释放资源
def crawl_with_manual_login():
"""
手动登录版本
这个函数会:
1. 打开浏览器
2. 暂停程序,让用户手动完成验证和登录
3. 用户确认后继续爬取
适用场景:
- 自动模式失败时使用
- 需要登录账号才能访问的页面
"""
# 创建浏览器实例
options = uc.ChromeOptions()
driver = uc.Chrome(options=options)
try:
# 访问目标页面
url = 'https://leetcode.cn/problem-list/2cktkvj/'
print(f"正在访问: {url}")
driver.get(url)
# 显示操作提示
print("\n" + "="*60)
print("请在浏览器中完成以下操作:")
print("1. 等待Cloudflare验证通过")
print("2. 如需要,请手动登录LeetCode账号")
print("3. 确保页面完全加载后,在控制台输入 'y' 继续")
print("="*60 + "\n")
# 等待用户确认
response = input("准备好了吗?输入 'y' 继续: ")
if response.lower() != 'y':
print("操作已取消")
return []
# 滚动页面加载内容
print("滚动页面...")
for i in range(5): # 滚动 5 次
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2) # 每次滚动后等待 2 秒
# 查找所有题目链接
elements = driver.find_elements(By.XPATH, "//a[contains(@href, '/problems/')]")
problems = [] # 存储题目信息
seen_urls = set() # 用于去重
# 遍历所有链接元素
for element in elements:
href = element.get_attribute('href')
# 过滤无效链接和重复链接
if not href or '/problems/' not in href or href in seen_urls:
continue
seen_urls.add(href)
text = element.text.strip()
if text:
# 清理文本:只取第一行,去除通过率和难度
text = text.split('\n')[0]
problem_id = ""
problem_title = text
# 尝试匹配 "数字. 标题" 格式
match = re.match(r'^(\d+)\.\s*(.+)$', text)
if match:
problem_id = match.group(1)
problem_title = match.group(2).strip()
else:
# 尝试提取数字开头
id_match = re.match(r'^(\d+)', text)
if id_match:
problem_id = id_match.group(1)
problem_title = text[len(problem_id):].lstrip('. ')
problems.append({
'id': problem_id,
'title': problem_title,
'url': href
})
print(f"已爬取: [{problem_id}] {problem_title}")
# 保存结果到文件
if problems:
# 保存为文本格式
with open('leetcode_hot100.txt', 'w', encoding='utf-8') as f:
for p in problems:
if p['id']:
line = f"- {p['id']} {p['title']}、[{p['title']}]({p['url']})\n"
else:
line = f"- {p['title']}、[{p['title']}]({p['url']})\n"
f.write(line)
# 保存为 CSV 格式
with open('leetcode_hot100.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=['id', 'title', 'url'])
writer.writeheader()
writer.writerows(problems)
# 保存为 JSON 格式
with open('leetcode_hot100.json', 'w', encoding='utf-8') as f:
json.dump(problems, f, ensure_ascii=False, indent=2)
print(f"\n✓ 成功爬取 {len(problems)} 道题目")
print("✓ 已保存到 leetcode_hot100.txt")
print("✓ 已保存到 leetcode_hot100.csv")
print("✓ 已保存到 leetcode_hot100.json")
return problems
finally:
# 关闭浏览器
driver.quit()
# ==================== 程序入口 ====================
if __name__ == '__main__':
"""
这是程序的主入口
当直接运行这个 Python 文件时,会执行这里的代码
"""
print("LeetCode Hot 100 爬虫")
print("="*60)
print("请选择模式:")
print("1. 自动模式(推荐,使用undetected-chromedriver)")
print("2. 手动模式(打开浏览器后暂停,让你手动登录)")
print("="*60)
# 获取用户输入
choice = input("请输入选择 (1/2): ").strip()
# 根据用户选择执行不同的函数
if choice == '2':
print("\n使用手动模式...")
problems = crawl_with_manual_login()
else:
print("\n使用自动模式...")
problems = crawl_leetcode_hot100()
# 显示爬取结果
if problems:
print("\n" + "="*60)
print(f"✓ 爬取完成!共获取 {len(problems)} 道题目")
print("\n前10道题目预览:")
# 显示前 10 道题目
for i, p in enumerate(problems[:10], 1):
print(f"{i}. [{p['id']}] {p['title']}")
print(f" {p['url']}")
else:
print("\n❌ 爬取失败,请检查网络或尝试手动模式")
需要安装相对应的库。
运行结果,leetcode hot 100 题单:
- 160 相交链表、[相交链表](https://leetcode.cn/problems/intersection-of-two-linked-lists?envType=problem-list-v2&envId=2cktkvj) - 236 二叉树的最近公共祖先、[二叉树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree?envType=problem-list-v2&envId=2cktkvj) - 234 回文链表、[回文链表](https://leetcode.cn/problems/palindrome-linked-list?envType=problem-list-v2&envId=2cktkvj) - 739 每日温度、[每日温度](https://leetcode.cn/problems/daily-temperatures?envType=problem-list-v2&envId=2cktkvj) - 226 翻转二叉树、[翻转二叉树](https://leetcode.cn/problems/invert-binary-tree?envType=problem-list-v2&envId=2cktkvj) - 221 最大正方形、[最大正方形](https://leetcode.cn/problems/maximal-square?envType=problem-list-v2&envId=2cktkvj) - 215 数组中的第K个最大元素、[数组中的第K个最大元素](https://leetcode.cn/problems/kth-largest-element-in-an-array?envType=problem-list-v2&envId=2cktkvj) - 208 实现 Trie (前缀树)、[实现 Trie (前缀树)](https://leetcode.cn/problems/implement-trie-prefix-tree?envType=problem-list-v2&envId=2cktkvj) - 207 课程表、[课程表](https://leetcode.cn/problems/course-schedule?envType=problem-list-v2&envId=2cktkvj) - 206 反转链表、[反转链表](https://leetcode.cn/problems/reverse-linked-list?envType=problem-list-v2&envId=2cktkvj) - 200 岛屿数量、[岛屿数量](https://leetcode.cn/problems/number-of-islands?envType=problem-list-v2&envId=2cktkvj) - 198 打家劫舍、[打家劫舍](https://leetcode.cn/problems/house-robber?envType=problem-list-v2&envId=2cktkvj) - 169 多数元素、[多数元素](https://leetcode.cn/problems/majority-element?envType=problem-list-v2&envId=2cktkvj) - 238 除自身以外数组的乘积、[除自身以外数组的乘积](https://leetcode.cn/problems/product-of-array-except-self?envType=problem-list-v2&envId=2cktkvj) - 155 最小栈、[最小栈](https://leetcode.cn/problems/min-stack?envType=problem-list-v2&envId=2cktkvj) - 152 乘积最大子数组、[乘积最大子数组](https://leetcode.cn/problems/maximum-product-subarray?envType=problem-list-v2&envId=2cktkvj) - 148 排序链表、[排序链表](https://leetcode.cn/problems/sort-list?envType=problem-list-v2&envId=2cktkvj) - 146 LRU 缓存、[LRU 缓存](https://leetcode.cn/problems/lru-cache?envType=problem-list-v2&envId=2cktkvj) - 142 环形链表 II、[环形链表 II](https://leetcode.cn/problems/linked-list-cycle-ii?envType=problem-list-v2&envId=2cktkvj) - 141 环形链表、[环形链表](https://leetcode.cn/problems/linked-list-cycle?envType=problem-list-v2&envId=2cktkvj) - 139 单词拆分、[单词拆分](https://leetcode.cn/problems/word-break?envType=problem-list-v2&envId=2cktkvj) - 136 只出现一次的数字、[只出现一次的数字](https://leetcode.cn/problems/single-number?envType=problem-list-v2&envId=2cktkvj) - 647 回文子串、[回文子串](https://leetcode.cn/problems/palindromic-substrings?envType=problem-list-v2&envId=2cktkvj) - 128 最长连续序列、[最长连续序列](https://leetcode.cn/problems/longest-consecutive-sequence?envType=problem-list-v2&envId=2cktkvj) - 124 二叉树中的最大路径和、[二叉树中的最大路径和](https://leetcode.cn/problems/binary-tree-maximum-path-sum?envType=problem-list-v2&envId=2cktkvj) - 322 零钱兑换、[零钱兑换](https://leetcode.cn/problems/coin-change?envType=problem-list-v2&envId=2cktkvj) - 494 目标和、[目标和](https://leetcode.cn/problems/target-sum?envType=problem-list-v2&envId=2cktkvj) - 461 汉明距离、[汉明距离](https://leetcode.cn/problems/hamming-distance?envType=problem-list-v2&envId=2cktkvj) - 448 找到所有数组中消失的数字、[找到所有数组中消失的数字](https://leetcode.cn/problems/find-all-numbers-disappeared-in-an-array?envType=problem-list-v2&envId=2cktkvj) - 438 找到字符串中所有字母异位词、[找到字符串中所有字母异位词](https://leetcode.cn/problems/find-all-anagrams-in-a-string?envType=problem-list-v2&envId=2cktkvj) - 437 路径总和 III、[路径总和 III](https://leetcode.cn/problems/path-sum-iii?envType=problem-list-v2&envId=2cktkvj) - 416 分割等和子集、[分割等和子集](https://leetcode.cn/problems/partition-equal-subset-sum?envType=problem-list-v2&envId=2cktkvj) - 406 根据身高重建队列、[根据身高重建队列](https://leetcode.cn/problems/queue-reconstruction-by-height?envType=problem-list-v2&envId=2cktkvj) - 399 除法求值、[除法求值](https://leetcode.cn/problems/evaluate-division?envType=problem-list-v2&envId=2cktkvj) - 394 字符串解码、[字符串解码](https://leetcode.cn/problems/decode-string?envType=problem-list-v2&envId=2cktkvj) - 347 前 K 个高频元素、[前 K 个高频元素](https://leetcode.cn/problems/top-k-frequent-elements?envType=problem-list-v2&envId=2cktkvj) - 338 比特位计数、[比特位计数](https://leetcode.cn/problems/counting-bits?envType=problem-list-v2&envId=2cktkvj) - 337 打家劫舍 III、[打家劫舍 III](https://leetcode.cn/problems/house-robber-iii?envType=problem-list-v2&envId=2cktkvj) - 121 买卖股票的最佳时机、[买卖股票的最佳时机](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock?envType=problem-list-v2&envId=2cktkvj) - 312 戳气球、[戳气球](https://leetcode.cn/problems/burst-balloons?envType=problem-list-v2&envId=2cktkvj) - 309 买卖股票的最佳时机含冷冻期、[买卖股票的最佳时机含冷冻期](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown?envType=problem-list-v2&envId=2cktkvj) - 301 删除无效的括号、[删除无效的括号](https://leetcode.cn/problems/remove-invalid-parentheses?envType=problem-list-v2&envId=2cktkvj) - 300 最长递增子序列、[最长递增子序列](https://leetcode.cn/problems/longest-increasing-subsequence?envType=problem-list-v2&envId=2cktkvj) - 297 二叉树的序列化与反序列化、[二叉树的序列化与反序列化](https://leetcode.cn/problems/serialize-and-deserialize-binary-tree?envType=problem-list-v2&envId=2cktkvj) - 287 寻找重复数、[寻找重复数](https://leetcode.cn/problems/find-the-duplicate-number?envType=problem-list-v2&envId=2cktkvj) - 283 移动零、[移动零](https://leetcode.cn/problems/move-zeroes?envType=problem-list-v2&envId=2cktkvj) - 279 完全平方数、[完全平方数](https://leetcode.cn/problems/perfect-squares?envType=problem-list-v2&envId=2cktkvj) - 253 会议室 II、[会议室 II](https://leetcode.cn/problems/meeting-rooms-ii?envType=problem-list-v2&envId=2cktkvj) - 240 搜索二维矩阵 II、[搜索二维矩阵 II](https://leetcode.cn/problems/search-a-2d-matrix-ii?envType=problem-list-v2&envId=2cktkvj) - 239 滑动窗口最大值、[滑动窗口最大值](https://leetcode.cn/problems/sliding-window-maximum?envType=problem-list-v2&envId=2cktkvj) - 22 括号生成、[括号生成](https://leetcode.cn/problems/generate-parentheses?envType=problem-list-v2&envId=2cktkvj) - 49 字母异位词分组、[字母异位词分组](https://leetcode.cn/problems/group-anagrams?envType=problem-list-v2&envId=2cktkvj) - 48 旋转图像、[旋转图像](https://leetcode.cn/problems/rotate-image?envType=problem-list-v2&envId=2cktkvj) - 46 全排列、[全排列](https://leetcode.cn/problems/permutations?envType=problem-list-v2&envId=2cktkvj) - 42 接雨水、[接雨水](https://leetcode.cn/problems/trapping-rain-water?envType=problem-list-v2&envId=2cktkvj) - 39 组合总和、[组合总和](https://leetcode.cn/problems/combination-sum?envType=problem-list-v2&envId=2cktkvj) - 543 二叉树的直径、[二叉树的直径](https://leetcode.cn/problems/diameter-of-binary-tree?envType=problem-list-v2&envId=2cktkvj) - 34 在排序数组中查找元素的第一个和最后一个位置、[在排序数组中查找元素的第一个和最后一个位置](https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array?envType=problem-list-v2&envId=2cktkvj) - 33 搜索旋转排序数组、[搜索旋转排序数组](https://leetcode.cn/problems/search-in-rotated-sorted-array?envType=problem-list-v2&envId=2cktkvj) - 32 最长有效括号、[最长有效括号](https://leetcode.cn/problems/longest-valid-parentheses?envType=problem-list-v2&envId=2cktkvj) - 31 下一个排列、[下一个排列](https://leetcode.cn/problems/next-permutation?envType=problem-list-v2&envId=2cktkvj) - 538 把二叉搜索树转换为累加树、[把二叉搜索树转换为累加树](https://leetcode.cn/problems/convert-bst-to-greater-tree?envType=problem-list-v2&envId=2cktkvj) - 23 合并 K 个升序链表、[合并 K 个升序链表](https://leetcode.cn/problems/merge-k-sorted-lists?envType=problem-list-v2&envId=2cktkvj) - 560 和为 K 的子数组、[和为 K 的子数组](https://leetcode.cn/problems/subarray-sum-equals-k?envType=problem-list-v2&envId=2cktkvj) - 21 合并两个有序链表、[合并两个有序链表](https://leetcode.cn/problems/merge-two-sorted-lists?envType=problem-list-v2&envId=2cktkvj) - 20 有效的括号、[有效的括号](https://leetcode.cn/problems/valid-parentheses?envType=problem-list-v2&envId=2cktkvj) - 19 删除链表的倒数第 N 个结点、[删除链表的倒数第 N 个结点](https://leetcode.cn/problems/remove-nth-node-from-end-of-list?envType=problem-list-v2&envId=2cktkvj) - 17 电话号码的字母组合、[电话号码的字母组合](https://leetcode.cn/problems/letter-combinations-of-a-phone-number?envType=problem-list-v2&envId=2cktkvj) - 15 三数之和、[三数之和](https://leetcode.cn/problems/3sum?envType=problem-list-v2&envId=2cktkvj) - 11 盛最多水的容器、[盛最多水的容器](https://leetcode.cn/problems/container-with-most-water?envType=problem-list-v2&envId=2cktkvj) - 10 正则表达式匹配、[正则表达式匹配](https://leetcode.cn/problems/regular-expression-matching?envType=problem-list-v2&envId=2cktkvj) - 5 最长回文子串、[最长回文子串](https://leetcode.cn/problems/longest-palindromic-substring?envType=problem-list-v2&envId=2cktkvj) - 4 寻找两个正序数组的中位数、[寻找两个正序数组的中位数](https://leetcode.cn/problems/median-of-two-sorted-arrays?envType=problem-list-v2&envId=2cktkvj) - 3 无重复字符的最长子串、[无重复字符的最长子串](https://leetcode.cn/problems/longest-substring-without-repeating-characters?envType=problem-list-v2&envId=2cktkvj) - 2 两数相加、[两数相加](https://leetcode.cn/problems/add-two-numbers?envType=problem-list-v2&envId=2cktkvj) - 79 单词搜索、[单词搜索](https://leetcode.cn/problems/word-search?envType=problem-list-v2&envId=2cktkvj) - 114 二叉树展开为链表、[二叉树展开为链表](https://leetcode.cn/problems/flatten-binary-tree-to-linked-list?envType=problem-list-v2&envId=2cktkvj) - 621 任务调度器、[任务调度器](https://leetcode.cn/problems/task-scheduler?envType=problem-list-v2&envId=2cktkvj) - 617 合并二叉树、[合并二叉树](https://leetcode.cn/problems/merge-two-binary-trees?envType=problem-list-v2&envId=2cktkvj) - 105 从前序与中序遍历序列构造二叉树、[从前序与中序遍历序列构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal?envType=problem-list-v2&envId=2cktkvj) - 104 二叉树的最大深度、[二叉树的最大深度](https://leetcode.cn/problems/maximum-depth-of-binary-tree?envType=problem-list-v2&envId=2cktkvj) - 102 二叉树的层序遍历、[二叉树的层序遍历](https://leetcode.cn/problems/binary-tree-level-order-traversal?envType=problem-list-v2&envId=2cktkvj) - 101 对称二叉树、[对称二叉树](https://leetcode.cn/problems/symmetric-tree?envType=problem-list-v2&envId=2cktkvj) - 98 验证二叉搜索树、[验证二叉搜索树](https://leetcode.cn/problems/validate-binary-search-tree?envType=problem-list-v2&envId=2cktkvj) - 96 不同的二叉搜索树、[不同的二叉搜索树](https://leetcode.cn/problems/unique-binary-search-trees?envType=problem-list-v2&envId=2cktkvj) - 94 二叉树的中序遍历、[二叉树的中序遍历](https://leetcode.cn/problems/binary-tree-inorder-traversal?envType=problem-list-v2&envId=2cktkvj) - 85 最大矩形、[最大矩形](https://leetcode.cn/problems/maximal-rectangle?envType=problem-list-v2&envId=2cktkvj) - 84 柱状图中最大的矩形、[柱状图中最大的矩形](https://leetcode.cn/problems/largest-rectangle-in-histogram?envType=problem-list-v2&envId=2cktkvj) - 1 两数之和、[两数之和](https://leetcode.cn/problems/two-sum?envType=problem-list-v2&envId=2cktkvj) - 78 子集、[子集](https://leetcode.cn/problems/subsets?envType=problem-list-v2&envId=2cktkvj) - 76 最小覆盖子串、[最小覆盖子串](https://leetcode.cn/problems/minimum-window-substring?envType=problem-list-v2&envId=2cktkvj) - 75 颜色分类、[颜色分类](https://leetcode.cn/problems/sort-colors?envType=problem-list-v2&envId=2cktkvj) - 72 编辑距离、[编辑距离](https://leetcode.cn/problems/edit-distance?envType=problem-list-v2&envId=2cktkvj) - 70 爬楼梯、[爬楼梯](https://leetcode.cn/problems/climbing-stairs?envType=problem-list-v2&envId=2cktkvj) - 581 最短无序连续子数组、[最短无序连续子数组](https://leetcode.cn/problems/shortest-unsorted-continuous-subarray?envType=problem-list-v2&envId=2cktkvj) - 64 最小路径和、[最小路径和](https://leetcode.cn/problems/minimum-path-sum?envType=problem-list-v2&envId=2cktkvj) - 62 不同路径、[不同路径](https://leetcode.cn/problems/unique-paths?envType=problem-list-v2&envId=2cktkvj) - 56 合并区间、[合并区间](https://leetcode.cn/problems/merge-intervals?envType=problem-list-v2&envId=2cktkvj) - 55 跳跃游戏、[跳跃游戏](https://leetcode.cn/problems/jump-game?envType=problem-list-v2&envId=2cktkvj) - 53 最大子数组和、[最大子数组和](https://leetcode.cn/problems/maximum-subarray?envType=problem-list-v2&envId=2cktkvj)