LeetCode hot 100(C++版本)
哈希
第一题:两数之和
题干 :给定一个整数数组 nums和一个整数目标值 target,请你在该数组中找出和为目标值 target的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。你可以按任意顺序返回答案。
示例:
- 示例 1:输入:
nums = [2,7,11,15],target = 9;输出:[0,1](解释:nums[0] + nums[1] == 9)。 - 示例 2:输入:
nums = [3,2,4],target = 6;输出:[1,2]。 - 示例 3:输入:
nums = [3,3],target = 6;输出:[0,1]。
C++
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> numMap;//存储{数组:下标}
for(int i=0;i<nums.size();i++)
{
int complement=target-nums[i];
if(numMap.find(complement)!=numMap.end())
{
return {numMap[complement],i};
}
numMap[nums[i]]=i;//插入/更新操作,将数值 nums[i]作为键,下标 i作为值,存入哈希表
}
return {};//为了保证有返回值,不会执行
}
};
第二题:最长连续序列
题干 :给定一个未排序的整数数组 nums,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。请你设计并实现时间复杂度为 O(n)的算法解决此问题。
示例:
- 示例 1:输入:
nums = [100,4,200,1,3,2];输出:4(解释:最长连续序列是[1,2,3,4],长度为 4)。 - 示例 2:输入:
nums = [0,3,7,2,5,8,4,6,0,1];输出:9。 - 示例 3:输入:
nums = [1,0,1,2];输出:3。
C++
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> numSet(nums.begin(),nums.end());
int maxLength=0;
for(int num:numSet)
{
// 只有当前数字是序列起点时才处理
if(numSet.find(num-1)==numSet.end())
{
int currentNum=num;
int currentLength=1;
// 向后延伸连续序列
while(numSet.find(currentNum+1)!=numSet.end())
{
currentNum++;
currentLength++;
}
maxLength=max(maxLength,currentLength);
}
}
return maxLength;
}
};
双指针
第三题: 移动零
题干 :给定数组 nums,将所有 0移到数组末尾,保持非零元素相对顺序,原地操作(不复制数组)。
示例:
-
输入:
nums = [0,1,0,3,12];输出:[1,3,12,0,0]。 -
输入:
nums = [0];输出:[0]。C++class Solution { public: void moveZeroes(vector<int>& nums) { for(int cur=0,dest=-1;cur<nums.size();cur++) if(nums[cur]) swap(nums[++dest],nums[cur]); } };
第四题:盛最多水的容器
题干 :给定长度为 n的整数数组 height(表示 n条垂线的高度),选两条线与 x轴构成容器,求最大容水量(不能倾斜)。
示例:
- 输入:
height = [1,8,6,2,5,4,8,3,7];输出:49(两线高度8和7,宽度7,面积7×7=49)。 - 输入:
height = [1,1];输出:1(两线高度1,宽度1,面积1×1=1)。
C++
class Solution {
public:
int maxArea(vector<int>& height) {
int left=0,right=height.size()-1,ret=0;
while(left<right)
{
int v=min(height[left],height[right])*(right-left);
ret=max(ret,v);
if(height[left]<height[right])left++;
else right--;
}
return ret;
}
};
第五题: 三数之和
题干 :给定整数数组 nums,找出所有和为 0且不重复的三元组 [nums[i], nums[j], nums[k]](满足 i≠j≠k),返回所有符合条件的三元组。
示例:
- 输入:
nums = [-1,0,1,2,-1,-4];输出:[[-1,-1,2], [-1,0,1]]。 - 输入:
nums = [0,1,1];输出:[](无和为0的三元组)。 - 输入:
nums = [0,0,0];输出:[[0,0,0]]。
C++
class Solution
{
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
vector<vector<int>> ret;
// 1. 排序
sort(nums.begin(), nums.end());
// 2. 利⽤双指针解决问题
int n = nums.size();
for(int i = 0; i < n; ) // 固定数 a
{
if(nums[i] > 0) break; // ⼩优化
int left = i + 1, right = n - 1, target = -nums[i];
while(left < right)
{
int sum = nums[left] + nums[right];
if(sum > target) right--;
else if(sum < target) left++;
else
{
ret.push_back({nums[i], nums[left], nums[right]});
left++, right--;
// 去重操作 left 和 right
while(left < right && nums[left] == nums[left - 1]) left++;
while(left < right && nums[right] == nums[right + 1])
right--;
}
}
// 去重 i
i++;
while(i < n && nums[i] == nums[i - 1]) i++;
}
return ret;
}
};
滑动窗口
第六题:无重复字符的最长子串
题干 :给定字符串 s,找出其中不含有重复字符的最长子串的长度。
示例:
- 输入:
s = "abcabcbb";输出:3(最长子串如"abc"、"bca"、"cab")。 - 输入:
s = "bbbbb";输出:1(最长子串为"b")。 - 输入:
s = "pwwkew";输出:3(最长子串为"wke")。
C++
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int ret=0;
int n=s.size();
for(int i=0;i<n;i++)
{
int hash[128]={0};
for(int j=i;j<n;j++)
{
hash[s[j]]++;
if(hash[s[j]]>1)
{
break;
}
ret=max(ret,j-i+1);
}
}
return ret;
}
};
第七题: 找到字符串中所有字母异位词
题干 :给定字符串 s和 p,找到 s中所有 p的字母异位词的子串,返回这些子串的起始索引(不考虑输出顺序)。
示例:
- 输入:
s = "cbaebabacd", p = "abc";输出:[0,6](子串"cba"、"bac"是"abc"的异位词)。 - 输入:
s = "abab", p = "ab";输出:[0,1,2](子串"ab"、"ba"、"ab"是"ab"的异位词)。
C++
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res;
int n=s.size(),m=p.size();
if(n<m) return res;
vector<int> pCount(26,0),winCount(26,0);
// 统计 p 的字符频次
for(char c:p)
{
pCount[c-'a']++;
}
// 初始化第一个窗口
for(int i=0;i<m;i++)
{
winCount[s[i]-'a']++;
}
// 比较第一个窗口
if(pCount==winCount)
{
res.push_back(0);
}
// 滑动窗口
for(int i=m;i<n;i++)
{
// 加入新字符
winCount[s[i]-'a']++;
// 移除旧字符
winCount[s[i-m]-'a']--;
// 检查是否匹配
if(pCount==winCount)
{
res.push_back(i-m+1);
}
}
return res;
}
};
子串
第八题:和为 K 的子数组
题干 :给你一个整数数组 nums和一个整数 k,统计并返回该数组中和为 k的子数组的个数(子数组是数组中元素的连续非空序列)。
示例:
- 示例 1:输入:
nums = [1,1,1], k = 2;输出:2(子数组[1,1](前两个元素)、[1,1](后两个元素)的和为 2)。 - 示例 2:输入:
nums = [1,2,3], k = 3;输出:2(子数组[1,2]、[3]的和为 3)。
C++
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int> prefixCount;
prefixCount[0] =1;// 初始状态:前缀和为0出现1次
int sum=0,count=0;
for(int num:nums)
{
sum+=num;//当前前缀和
// 如果之前出现过 prefixSum = sum - k,说明存在子数组和为k
if (prefixCount.find(sum - k) != prefixCount.end()) {
count += prefixCount[sum - k];
}
// 更新当前前缀和的出现次数
prefixCount[sum]++;
}
return count;
}
};
普通数组
第九题:最大子数组和
题干 :给定整数数组 nums,找出一个连续子数组(至少包含一个元素),使其和最大,返回这个最大和。
示例:
- 输入:
nums = [-2,1,-3,4,-1,2,1,-5,4];输出:6(最大和子数组为[4,-1,2,1])。 - 输入:
nums = [1];输出:1(仅一个元素,和为自身)。 - 输入:
nums = [5,4,-1,7,8];输出:23(最大和子数组为[5,4,-1,7,8])。
C++
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int current_sum = nums[0];
int max_sum = nums[0];
for (int i = 1; i < nums.size(); i++) {
// 如果 current_sum 大于 0,保留并累加;否则抛弃,从当前元素重新开始
current_sum = max(nums[i], current_sum + nums[i]);
// 更新全局最大值
max_sum = max(max_sum, current_sum);
}
return max_sum;
}
};
第十题: 合并区间
题干 :给定若干区间的集合 intervals(每个区间为 [start_i, end_i]),合并所有重叠的区间,返回一个不重叠且恰好覆盖所有输入区间的新区间数组。
示例:
- 输入:
intervals = [[1,3],[2,6],[8,10],[15,18]];输出:[[1,6],[8,10],[15,18]](区间[1,3]与[2,6]重叠,合并为[1,6])。 - 输入:
intervals = [[1,4],[4,5]];输出:[[1,5]](区间[1,4]与[4,5]可视为重叠,合并为[1,5])。 - 输入:
intervals = [[4,7],[1,4]];输出:[[1,7]](区间[4,7]与[1,4]可视为重叠,合并为[1,7])。
C++
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (intervals.empty()) return {};
// 1. 按区间起点排序
sort(intervals.begin(), intervals.end());
vector<vector<int>> res;
res.push_back(intervals[0]); // 第一个区间直接加入
for (int i = 1; i < intervals.size(); i++) {
// 当前区间的起点 <= 上一个区间的终点 → 有重叠
if (intervals[i][0] <= res.back()[1]) {
// 合并:更新上一个区间的终点为较大值
res.back()[1] = max(res.back()[1], intervals[i][1]);
} else {
// 无重叠,直接加入
res.push_back(intervals[i]);
}
}
return res;
}
};
第十一题: 轮转数组
题干 :给定整数数组 nums,将数组中的元素向右轮转 k个位置 (k为非负数),需原地修改数组(或满足空间复杂度要求)。
示例:
- 输入:
nums = [1,2,3,4,5,6,7], k = 3;输出:[5,6,7,1,2,3,4](向右轮转 3 步后的结果)。 - 输入:
nums = [-1,-100,3,99], k = 2;输出:[3,99,-1,-100](向右轮转 2 步后的结果)。
C++
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
if (n == 0) return;
k = k % n; // 防止 k 大于 n
if (k == 0) return;
// 1. 整体反转
reverse(nums.begin(), nums.end());
// 2. 反转前 k 个
reverse(nums.begin(), nums.begin() + k);
// 3. 反转剩余 n-k 个
reverse(nums.begin() + k, nums.end());
}
};
第十二题: 除了自身以外数组的乘积
题干 :给定整数数组 nums,返回数组 answer,其中 answer[i]等于 nums中除 nums[i]之外其余所有元素的乘积。要求:不使用除法,时间复杂度 O(n),且任意元素的前缀/后缀乘积在 32 位整数范围内。
示例:
- 输入:
nums = [1,2,3,4];输出:[24,12,8,6](answer[0]=2×3×4=24,answer[1]=1×3×4=12,依此类推)。 - 输入:
nums = [-1,1,0,-3,3];输出:[0,0,9,0,0](answer[0]=1×0×(-3)×3=0,answer[2]=(-1)×1×(-3)×3=9,依此类推)。
C++
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> answer(n);
// 1. 计算前缀积:answer[i] = nums[0] * nums[1] * ... * nums[i-1]
answer[0] = 1; // 第一个元素左边没有数,乘积为1
for (int i = 1; i < n; i++) {
answer[i] = answer[i - 1] * nums[i - 1];
}
// 2. 计算后缀积并乘入 answer:从右往左,用变量 rightProduct 记录右边乘积
int rightProduct = 1;
for (int i = n - 1; i >= 0; i--) {
answer[i] *= rightProduct;
rightProduct *= nums[i];
}
return answer;
}
};
矩阵
第十三题:矩阵置零
题干 :给定一个 m x n的矩阵,若某个元素为 0,则将其所在的行和列的所有元素 都设为 0。要求使用原地算法(直接修改输入的矩阵,不使用额外矩阵)。
示例:
- 输入:
matrix = [[1,1,1],[1,0,1],[1,1,1]];输出:[[1,0,1],[0,0,0],[1,0,1]](中间元素为0,其所在行、列均置0)。 - 输入:
matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]];输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]](第一列和第四列的元素因含0被置0,第一行和第四行也因含0被置0)。
C++
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m=matrix.size();
int n=matrix[0].size();
vector<int> row(m),col(n);
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(!matrix[i][j])
{
row[i]=col[j]=true;
}
}
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(row[i]||col[j])
{
matrix[i][j]=0;
}
}
}
}
};
第十四题:螺旋矩阵
题干 :给定一个 m行 n列的矩阵 matrix,按照顺时针螺旋顺序返回矩阵中的所有元素。
示例:
- 输入:
matrix = [[1,2,3],[4,5,6],[7,8,9]];输出:[1,2,3,6,9,8,7,4,5](按顺时针螺旋遍历:右→下→左→上→右...)。 - 输入:
matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]];输出:[1,2,3,4,8,12,11,10,9,5,6,7](螺旋遍历所有元素)。
c++
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if (matrix.empty() || matrix[0].empty()) return {};
int m = matrix.size(), n = matrix[0].size();
int top = 0, bottom = m - 1, left = 0, right = n - 1;
vector<int> result;
while (top <= bottom && left <= right) {
// 1. 左 → 右
for (int j = left; j <= right; j++) {
result.push_back(matrix[top][j]);
}
top++;
// 2. 上 → 下
for (int i = top; i <= bottom; i++) {
result.push_back(matrix[i][right]);
}
right--;
// 3. 右 → 左(需判断是否还有行)
if (top <= bottom) {
for (int j = right; j >= left; j--) {
result.push_back(matrix[bottom][j]);
}
bottom--;
}
// 4. 下 → 上(需判断是否还有列)
if (left <= right) {
for (int i = bottom; i >= top; i--) {
result.push_back(matrix[i][left]);
}
left++;
}
}
return result;
}
};
第十五题: 旋转图像
题干 :给定一个 n x n的二维矩阵(表示图像),将图像顺时针旋转 90 度 。要求原地旋转(直接修改输入的矩阵,不使用额外矩阵)。
示例:
- 输入:
matrix = [[1,2,3],[4,5,6],[7,8,9]];输出:[[7,4,1],[8,5,2],[9,6,3]](顺时针旋转 90 度后的结果)。 - 输入:
matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]];输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]](顺时针旋转 90 度后的结果)。
C++
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n=matrix.size();
// Step 1: 转置矩阵(沿主对角线)
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
swap(matrix[i][j],matrix[j][i]);
}
}
// Step 2: 反转每一行
for(int i=0;i<n;i++)
{
reverse(matrix[i].begin(),matrix[i].end());
}
}
};
第十六题: 搜索二维矩阵 II
题干 :编写一个高效的算法,搜索 m x n矩阵 matrix中的一个目标值 target。矩阵特性:
-
每行的元素从左到右升序排列;
-
每列的元素从上到下升序排列。
示例:
-
输入:
matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5;输出:true(目标值5存在于矩阵中)。 -
输入:
matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20;输出:false(目标值20不存在于矩阵中)。
C++
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.empty()||matrix[0].empty())
{
return false;
}
int m=matrix.size(),n=matrix[0].size();
int row=0,col=n-1;//从右上角开始
while(row<m&&col>=0)
{
if(matrix[row][col]==target)
{
return true;
}
else if(matrix[row][col]>target)
{
col--;//向左
}
else
{
row++;//向下移动
}
}
return false;
}
};
链表
第十七题: 相交链表
题干 :给定两个单链表的头节点 headA、headB,找到并返回它们的相交起始节点 (若无相交返回 null)。保证链表无环。
核心 :双指针法(A走完走 B,B走完走 A,相遇时即为相交点,或都走到 null)。
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (!headA || !headB) return nullptr;
ListNode *pA = headA, *pB = headB;
while (pA != pB) {
pA = pA ? pA->next : headB;
pB = pB ? pB->next : headA;
}
return pA;
}
};
第十八题:反转链表
题干 :给定单链表头节点 head,反转链表并返回新头节点。
核心 :迭代法(三指针 prev, curr, next)或递归法(从后往前反转)。
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr; // 指向前一个节点
ListNode* curr = head; // 当前节点
while (curr != nullptr) {
ListNode* nextTemp = curr->next; // 临时保存下一个节点
curr->next = prev; // 反转指针
prev = curr; // 移动 prev
curr = nextTemp; // 移动 curr
}
return prev; // prev 现在是新链表的头
}
};
第十九题:回文链表
题干 :判断单链表是否为回文链表(正读和反读相同)。
核心:快慢指针找中点,反转后半部分,比较前半和后半是否一致。
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
if (!head || !head->next) return true;
// 1. 快慢指针找中点
ListNode *slow = head, *fast = head;
while (fast->next && fast->next->next) {
slow = slow->next;
fast = fast->next->next;
}
// 2. 反转后半部分(从 slow->next 开始)
ListNode *secondHalf = reverseList(slow->next);
slow->next = nullptr; // 断开前半部分和后半部分(可选,便于比较)
// 3. 比较前后两部分
ListNode *p1 = head, *p2 = secondHalf;
bool result = true;
while (p1 && p2) {
if (p1->val != p2->val) {
result = false;
break;
}
p1 = p1->next;
p2 = p2->next;
}
// 4. (可选)恢复链表结构
slow->next = reverseList(secondHalf);
return result;
}
private:
ListNode* reverseList(ListNode* head) {
ListNode *prev = nullptr, *curr = head, *nextTemp = nullptr;
while (curr) {
nextTemp = curr->next;
curr->next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
};
第二十题: 环形链表
题干 :判断单链表是否有环(节点可通过 next再次到达)。
核心:快慢指针(快指针走两步,慢指针走一步,相遇则有环)。
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if (!head || !head->next) return false;
ListNode *slow = head;
ListNode *fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
return true;
}
}
return false;
}
};
第二十一题: 环形链表 II
题干 :找到环形链表中环的入口节点 (若无环返回 null)。
核心:快慢指针相遇后,一个指针回到头,两指针同速前进,相遇点即为环入口。
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (!head || !head->next) return nullptr;
ListNode *slow = head, *fast = head;
// 第一步:找相遇点,判断是否有环
do {
if (!fast || !fast->next) return nullptr; // 无环
slow = slow->next;
fast = fast->next->next;
} while (slow != fast);
// 第二步:找环入口
slow = head; // slow 回到起点
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
return slow; // 返回环入口节点
}
};
第二十二题:合并两个有序链表
题干 :将两个升序链表合并为一个新的升序链表,返回新链表头节点。
核心:迭代法(新建虚拟头节点,依次拼接较小的节点)或递归法(合并剩余部分)。
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
// 创建伪头节点
ListNode dummy(0);
ListNode* tail = &dummy;
// 双指针遍历两个链表
while (list1 != nullptr && list2 != nullptr) {
if (list1->val <= list2->val) {
tail->next = list1;
list1 = list1->next;
} else {
tail->next = list2;
list2 = list2->next;
}
tail = tail->next;
}
// 拼接剩余部分
if (list1 != nullptr) {
tail->next = list1;
} else {
tail->next = list2;
}
return dummy.next;
}
};
第二十三题:两数相加
题干 :两个非负整数的链表(逆序存储每位数字),将它们相加,返回和的链表(同样逆序)。
核心:逐位相加,处理进位,注意链表长度不同时的补零。
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* cur1 = l1, *cur2 = l2;
ListNode* newhead = new ListNode(0); // 创建虚拟头结点
ListNode* prev = newhead; // 尾指针
int t = 0; // 记录进位
while (cur1 || cur2 || t) {
// 加上第一个链表
if (cur1) {
t += cur1->val;
cur1 = cur1->next;
}
// 加上第二个链表
if (cur2) {
t += cur2->val;
cur2 = cur2->next;
}
// 创建新节点,存储当前位结果
prev->next = new ListNode(t % 10);
prev = prev->next;
t /= 10; // 计算进位
}
prev = newhead->next;
delete newhead; // 释放虚拟头结点
return prev;
}
};
第二十四题: 删除链表的倒数第 N 个结点
题干 :删除单链表的倒数第 n个节点,返回头节点。
核心 :双指针法(快指针先走 n步,然后快慢同走,快到末尾时慢指针指向待删节点的前一个)。
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0); // 创建哑节点
dummy->next = head;
ListNode* fast = dummy;
ListNode* slow = dummy;
// 快指针先走 n 步
for (int i = 0; i < n; i++) {
fast = fast->next;
}
// 快慢指针同时走,直到快指针到末尾
while (fast->next) {
fast = fast->next;
slow = slow->next;
}
// 删除倒数第 n 个节点
ListNode* toDelete = slow->next;
slow->next = slow->next->next;
delete toDelete; // 可选:释放内存(C++)
ListNode* result = dummy->next;
delete dummy; // 释放哑节点
return result;
}
};
第二十五题:两两交换链表中的节点
题干 :两两交换链表中的相邻节点,返回新头节点。要求只交换节点(不修改节点值)。
核心:迭代法(虚拟头节点 + 三指针)或递归法(交换前两个节点,递归处理剩余)。
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
// 边界情况
if (!head || !head->next) return head;
// 创建虚拟头节点
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* prev = dummy;
while (head && head->next) {
ListNode* first = head;
ListNode* second = head->next;
// 交换节点指针
prev->next = second;
first->next = second->next;
second->next = first;
// 移动指针准备下一轮
prev = first;
head = first->next;
}
return dummy->next;
}
};
第二十六题:复制带随机指针的链表
题干 :复制一个含 random指针的链表(每个节点有 val、next、random),要求深拷贝,且新链表的 random指针与原链表对应节点一致。
核心:哈希表存储原节点与新节点的映射,或原地插入复制节点后再拆分。
C++
// Definition for a Node.
// class Node {
// public:
// int val;
// Node* next;
// Node* random;
// Node(int _val) {
// val = _val;
// next = nullptr;
// random = nullptr;
// }
// };
class Solution {
public:
Node* copyRandomList(Node* head) {
if (head == nullptr) {
return nullptr;
}
// 第一步:在每个原节点后面插入一个拷贝节点
Node* pcur = head;
while (pcur != nullptr) {
Node* copyNode = new Node(pcur->val);
copyNode->next = pcur->next;
pcur->next = copyNode;
pcur = copyNode->next;
}
// 第二步:设置拷贝节点的random指针
pcur = head;
while (pcur != nullptr) {
Node* copyNode = pcur->next;
if (pcur->random != nullptr) {
copyNode->random = pcur->random->next;
}
pcur = copyNode->next;
}
// 第三步:拆分原链表和拷贝链表
pcur = head;
Node* newHead = head->next;
Node* newTail = newHead;
while (pcur != nullptr) {
// 恢复原链表
pcur->next = newTail->next;
pcur = pcur->next;
// 构建新链表
if (pcur != nullptr) {
newTail->next = pcur->next;
newTail = newTail->next;
}
}
return newHead;
}
};
第二十七题: 排序链表
题干 :给定单链表头节点 head,将链表升序排列后返回。
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(!head||!head->next) return head;
ListNode *slow=head;
ListNode *fast=head->next;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
ListNode* rightHead=slow->next;
slow->next = nullptr; // 断开
ListNode* left = sortList(head);
ListNode* right = sortList(rightHead);
// 3. 合并两个有序链表
return merge(left, right);
}
private:
ListNode* merge(ListNode* l1, ListNode* l2) {
ListNode dummy(0);
ListNode* tail = &dummy;
while (l1 && l2) {
if (l1->val <= l2->val) {
tail->next = l1;
l1 = l1->next;
} else {
tail->next = l2;
l2 = l2->next;
}
tail = tail->next;
}
tail->next = l1 ? l1 : l2;
return dummy.next;
}
};
第二十八题: LRU 缓存
题干 :设计实现 LRUCache数据结构,满足以下要求:
-
LRUCache(int capacity):以正整数capacity为容量初始化缓存。 -
int get(int key):若key存在则返回对应value,否则返回-1。 -
void put(int key, int value):若key存在则更新value;否则插入键值对。若插入后缓存大小超过capacity,则逐出最近最少使用的键值对。 -
get和put操作的平均时间复杂度需为O(1)。C++class LRUCache { public: LRUCache(int capacity) : _capacity(capacity) {} int get(int key) { auto it = _hashmap.find(key); if (it == _hashmap.end()) return -1; // 移动到头部 auto listit = it->second; pair<int, int> kv = *listit; _list.erase(listit); _list.push_front(kv); _hashmap[key] = _list.begin(); return kv.second; } void put(int key, int value) { auto it = _hashmap.find(key); if (it == _hashmap.end()) { // 新增 if (_list.size() >= _capacity) { _hashmap.erase(_list.back().first); // 删除哈希记录 _list.pop_back(); // 删除链表尾 } _list.push_front({key, value}); _hashmap[key] = _list.begin(); } else { // 更新 auto listit = it->second; pair<int, int> kv = *listit; kv.second = value; _list.erase(listit); _list.push_front(kv); _hashmap[key] = _list.begin(); } } private: list<pair<int, int>> _list; size_t _capacity; unordered_map<int, list<pair<int, int>>::iterator> _hashmap; };
二叉树
第二十九题:二叉树的中序遍历
题干 :给定二叉树的根节点 root,返回它的中序遍历结果(左子树→根节点→右子树)。
示例:
- 输入:
root = [1,null,2,3];输出:[1,3,2](中序遍历:1→3→2)。
C++
/**
* 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> result;
inorder(root, result);
return result;
}
private:
void inorder(TreeNode* node, vector<int>& result) {
if (node == nullptr) return;
inorder(node->left, result); // 1. 遍历左子树
result.push_back(node->val); // 2. 访问根节点
inorder(node->right, result); // 3. 遍历右子树
}
};
第三十题:二叉树的最大深度
题干 :给定二叉树 root,求其最大深度(从根节点到最远叶子节点的最长路径上的节点数)。
示例:
- 输入:
root = [3,9,20,null,null,15,7];输出:3(路径:3→20→15 或 3→20→7,长度3)。
C++
/*
* 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 == nullptr) {
return 0;
}
int leftDepth = maxDepth(root->left);
int rightDepth = maxDepth(root->right);
return max(leftDepth, rightDepth) + 1;
}
};
第三十一题:对称二叉树
题干 :给定二叉树 root,判断它是否为对称二叉树(沿根节点的垂直中线折叠后,左右子树完全重合)。
示例:
-
输入:
root = [1,2,2,3,4,4,3];输出:true(左右子树对称)。C++/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: bool isSymmetric(TreeNode* root) { if (!root) return true; return isMirror(root->left, root->right); } private: bool isMirror(TreeNode* left, TreeNode* right) { if (!left && !right) return true; if (!left || !right) return false; if (left->val != right->val) return false; return isMirror(left->left, right->right) && isMirror(left->right, right->left); } };
第三十二题:二叉树的层序遍历
题干 :给定二叉树 root,按层序遍历(从顶部到底部,每层从左到右)返回节点值的列表。
示例:
- 输入:
root = [3,9,20,null,null,15,7];输出:[[3],[9,20],[15,7]](逐层输出)。
C++
/**
* 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>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
if(!root) return result;
queue<TreeNode*> q;
q.push(root);
while(!q.empty())
{
int levelSize=q.size();
vector<int> currentLevel;
for(int i=0;i<levelSize;i++)
{
TreeNode* node=q.front();
q.pop();
currentLevel.push_back(node->val);
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
result.push_back(currentLevel);
}
return result;
}
};
第三十三题:将有序数组转换为二叉搜索树
题干 :给定升序数组 nums,将其转换为一棵高度平衡的二叉搜索树(左右子树高度差≤1,且左子树所有节点<根<右子树所有节点)。
示例:
- 输入:
nums = [-10,-3,0,5,9];输出:[0,-3,9,-10,null,5](平衡BST)。
C++
/**
* 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:
TreeNode* sortedArrayToBST(vector<int>& nums) {
// 入口函数:调用递归辅助函数,初始范围是整个数组
return buildBST(nums, 0, nums.size() - 1);
}
private:
// 递归辅助函数:在区间 [left, right] 上构建 BST
TreeNode* buildBST(vector<int>& nums, int left, int right) {
// 基础情况:如果左边界超过右边界,说明没有元素可构建,返回空
if (left > right) {
return nullptr;
}
// 取中间位置(防止整数溢出,推荐使用 left + (right - left) / 2)
int mid = left + (right - left) / 2;
// 创建根节点
TreeNode* root = new TreeNode(nums[mid]);
// 递归构建左子树(左半部分)
root->left = buildBST(nums, left, mid - 1);
// 递归构建右子树(右半部分)
root->right = buildBST(nums, mid + 1, right);
// 返回当前构建好的子树的根节点
return root;
}
};
第三十四题: 二叉树的右视图
题干 :给定二叉树 root,返回从右侧观察能看到的节点值(即每层最右边的节点)。
示例:
- 输入:
root = [1,2,3,null,5,null,4];输出:[1,3,4](每层最右节点:1→3→4)。
C++
/**
* 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> rightSideView(TreeNode* root) {
vector<int> result;
if (!root) return result;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int levelSize = q.size(); // 当前层的节点数
for (int i = 0; i < levelSize; ++i) {
TreeNode* curr = q.front();
q.pop();
// 如果是当前层的最后一个节点,加入结果
if (i == levelSize - 1) {
result.push_back(curr->val);
}
// 先左后右入队(保证下一层从左到右处理)
if (curr->left) q.push(curr->left);
if (curr->right) q.push(curr->right);
}
}
return result;
}
};
第三十五题: 翻转二叉树
题干 :给定二叉树 root,翻转该树(每个节点的左右子树交换位置),返回翻转后的根节点。
示例:
- 输入:
root = [4,2,7,1,3,6,9];输出:[4,7,2,9,6,3,1](左右子树交换)。
C++
**
* 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:
TreeNode* invertTree(TreeNode* root) {
// 基础情况:空节点直接返回
if (!root) return nullptr;
// 递归翻转左右子树
TreeNode* left = invertTree(root->left);
TreeNode* right = invertTree(root->right);
// 交换左右子树
root->left = right;
root->right = left;
return root;
}
};
第三十六题: 二叉树的直径
题干 :给定二叉树 root,求其直径(任意两节点之间最长路径的边数,路径可能不经过根节点)。
示例:
- 输入:
root = [1,2,3,4,5];输出:3(路径:4→2→5 或 5→2→1→3,边数3)。
C++
/**
* 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 diameterOfBinaryTree(TreeNode* root) {
int m=0;
dfs(root,m);
return m;
}
private:
int dfs(TreeNode* node,int &m)
{
if(!node) return 0;
int dleft=dfs(node->left,m);
int dright=dfs(node->right,m);
m=max(m,dleft+dright);
return max(dleft,dright)+1;
}
};
第三十七题:二叉搜索树中第 K 小的元素
题干 :给定二叉搜索树 root和整数 k,返回树中第 k 小的元素(按升序排列的第 k 个节点值)。
示例:
- 输入:
root = [3,1,4,null,2], k=1;输出:1(升序:1→2→3→4,第1小是1)。
C++
class Solution {
public:
int kthSmallest(TreeNode* root, int k) {
int result = 0;
inorder(root, k, result);
return result;
}
private:
void inorder(TreeNode* node, int& k, int& result) {
if (!node) return;
inorder(node->left, k, result); // 左
k--; // 访问当前节点,计数减一
if (k == 0) { // 找到第k个
result = node->val;
return;
}
inorder(node->right, k, result); // 右
}
};
第三十八题:验证二叉搜索树
题干 :给定二叉树 root,判断它是否为有效二叉搜索树(左子树所有节点<根,右子树所有节点>根,且子树自身也满足BST)。
示例:
- 输入:
root = [2,1,3];输出:true(1<2<3,子树合法)。
C++
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
long long prev=LLONG_MIN;
return inorder(root,prev);
}
private:
bool inorder(TreeNode *node,long long&prev)
{
if (!node) return true;
// 遍历左子树
if (!inorder(node->left, prev)) return false;
// 检查当前节点是否大于前一个节点
if (node->val <= prev) return false;
prev = node->val; // 更新前一个值
// 遍历右子树
return inorder(node->right, prev);
}
};
第三十九题:路径总和 III
题干 :给定二叉树的根节点 root和整数 targetSum,求该二叉树里节点值之和等于 targetSum的路径数目。路径不需要从根节点开始,也不需要在叶子节点结束,但路径方向必须是向下的(只能从父节点到子节点)。
C++
/**
* 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 pathSum(TreeNode* root, int targetSum) {
if(!root) return 0;
//从当前节点开始
int count=dfs(root,targetSum);
count+=pathSum(root->left,targetSum);
count+=pathSum(root->right,targetSum);
return count;
}
private:
int dfs(TreeNode* node,long long target)
{
if(!node) return 0;
int count=0;
if(node->val==target) count++;
// 继续向下找,目标和减去当前节点值
count += dfs(node->left, target - node->val);
count += dfs(node->right, target - node->val);
return count;
}
};
第四十题:二叉树的最近公共祖先
题干 :给定二叉树的两个节点 p、q,找到它们的最近公共祖先 (LCA)。最近公共祖先定义为:一个节点 x,满足 x是 p和 q的祖先,且 x的深度尽可能大(节点本身也可以是自己的祖先)。
C++
/**
* 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) return nullptr;
if (p == root || q == root) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (right && left) return root;
if (left) return left;
if (right) return right;
return nullptr;
}
};
第四十一题:从前序与中序遍历序列构造二叉树
题干 :给定两个整数数组 preorder(先序遍历)和 inorder(中序遍历),构造该二叉树并返回其根节点。
C++
/**
* 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:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
// 建立值到索引的映射,加速查找
unordered_map<int, int> indexMap;
for (int i = 0; i < inorder.size(); ++i) {
indexMap[inorder[i]] = i;
}
// 递归函数:传入前序和中序的左右边界
function<TreeNode*(int, int, int, int)> dfs = [&](int preLeft, int preRight, int inLeft, int inRight) -> TreeNode* {
// 递归终止条件
if (preLeft > preRight || inLeft > inRight) return nullptr;
// 前序第一个元素是当前根节点
int rootVal = preorder[preLeft];
TreeNode* root = new TreeNode(rootVal);
// 在中序中找到根节点位置
int rootIdx = indexMap[rootVal];
// 计算左子树节点数
int leftSize = rootIdx - inLeft;
// 递归构建左子树:前序[preLeft+1, preLeft+leftSize],中序[inLeft, rootIdx-1]
root->left = dfs(preLeft + 1, preLeft + leftSize, inLeft, rootIdx - 1);
// 递归构建右子树:前序[preLeft+leftSize+1, preRight],中序[rootIdx+1, inRight]
root->right = dfs(preLeft + leftSize + 1, preRight, rootIdx + 1, inRight);
return root;
};
return dfs(0, preorder.size() - 1, 0, inorder.size() - 1);
}
};
第四十二题:二叉树展开为链表
题干 :给定二叉树的根节点 root,将其展开为一个单链表。展开后的链表应使用 TreeNode,其中 right指针指向链表的下一个节点,left指针始终为 null。链表应与二叉树的先序遍历顺序相同。
C++
class Solution {
public:
void flatten(TreeNode* root) {
if (!root) return;
// 递归展开左右子树
flatten(root->left);
flatten(root->right);
// 暂存右子树
TreeNode* right = root->right;
// 将左子树移到右子树位置
root->right = root->left;
root->left = nullptr;
// 找到当前右链表的末尾
TreeNode* p = root;
while (p->right) {
p = p->right;
}
// 接上原右子树
p->right = right;
}
};
图论
第四十三题:岛屿数量
给定由 '1'(陆地)和 '0'(水)组成的二维网格,计算岛屿数量。岛屿定义为水平/竖直方向相邻的陆地,且网格四边均被水包围。
C++
class Solution {
int m, n;
int dx[4] = {0, 0, -1, 1};
int dy[4] = {1, -1, 0, 0};
public:
int numIslands(vector<vector<char>>& grid) {
if(grid.empty() || grid[0].empty()) return 0;
m = grid.size(), n = grid[0].size();
int ret = 0;
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++) {
if(grid[i][j] == '1') {
ret++;
dfs(grid, i, j);
}
}
return ret;
}
private:
void dfs(vector<vector<char>>& grid, int i, int j) {
if(i < 0 || i >= m || j < 0 || j >= n || grid[i][j] != '1')
return;
grid[i][j] = '0'; // 标记为已访问
for(int k = 0; k < 4; k++) {
int x = i + dx[k], y = j + dy[k];
dfs(grid, x, y);
}
}
};
第四十四题: 腐烂的橘子
在 m×n网格中,单元格值 0(空)、1(新鲜橘子)、2(腐烂橘子)。每分钟,腐烂橘子会使其上下左右相邻 的新鲜橘子腐烂。求所有橘子腐烂所需的最小分钟数 ;若存在新鲜橘子永远无法腐烂(与腐烂橘子不连通),返回 -1。
C++
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
queue<pair<int, int>> q;
int fresh = 0; // 新鲜橘子计数
int time = 0; // 腐烂所需时间
// 初始化:找到所有腐烂橘子入队,统计新鲜橘子
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 2) {
q.push({i, j});
} else if (grid[i][j] == 1) {
fresh++;
}
}
}
// 特判:如果没有新鲜橘子,直接返回0
if (fresh == 0) return 0;
// 四个方向:上、下、左、右
vector<pair<int, int>> dirs = {{-1,0}, {1,0}, {0,-1}, {0,1}};
// BFS 多源扩散
while (!q.empty()) {
int size = q.size();
bool rottenThisRound = false;
for (int k = 0; k < size; k++) {
auto [x, y] = q.front(); q.pop();
for (auto& dir : dirs) {
int nx = x + dir.first, ny = y + dir.second;
if (nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] == 1) {
grid[nx][ny] = 2; // 标记为腐烂
q.push({nx, ny});
fresh--; // 新鲜橘子减少
rottenThisRound = true;
}
}
}
// 如果本轮有感染发生,时间+1
if (rottenThisRound) time++;
}
// 如果还有新鲜橘子没被感染,说明无法全部腐烂
return fresh == 0 ? time : -1;
}
};
第四十五题: 课程表
给定 numCourses门课程(编号 0~numCourses-1)和先修关系数组 prerequisites(prerequisites[i] = [a_i, b_i]表示学 a_i前需先学 b_i)。判断是否能完成所有课程(即先修关系无循环依赖)。
C++
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
//构建邻接表和入度数组
vector<vector<int>> graph(numCourses);
vector<int> indegree(numCourses,0);
for(auto& pre:prerequisites)
{
int course=pre[0];
int prereq=pre[1];
graph[prereq].push_back(course);
indegree[course]++;
}
queue<int> q;
for(int i=0;i<numCourses;i++)
{
if(indegree[i]==0)
{
q.push(i);
}
}
int visited=0;
while(!q.empty())
{
int cur=q.front();
q.pop();
visited++;
for(int next:graph[cur])
{
indegree[next]--;
if(indegree[next]==0)
{
q.push(next);
}
}
}
return visited==numCourses;
}
};
第四十六题:实现 Trie(前缀树)
实现前缀树(Trie)数据结构,支持以下操作:
insert(word):插入字符串word;search(word):判断word是否已插入(精确匹配);startsWith(prefix):判断是否存在已插入的字符串以prefix为前缀。
C++
class Trie {
private:
struct TrieNode {
bool isEnd;
TrieNode* children[26];
TrieNode() {
isEnd = false;
for (int i = 0; i < 26; i++) {
children[i] = nullptr;
}
}
};
TrieNode* root;
public:
Trie() {
root = new TrieNode();
}
void insert(string word) {
TrieNode* node = root;
for (char ch : word) {
int idx = ch - 'a';
if (node->children[idx] == nullptr) {
node->children[idx] = new TrieNode();
}
node = node->children[idx];
}
node->isEnd = true;
}
bool search(string word) {
TrieNode* node = root;
for (char ch : word) {
int idx = ch - 'a';
if (node->children[idx] == nullptr) {
return false;
}
node = node->children[idx];
}
return node->isEnd;
}
bool startsWith(string prefix) {
TrieNode* node = root;
for (char ch : prefix) {
int idx = ch - 'a';
if (node->children[idx] == nullptr) {
return false;
}
node = node->children[idx];
}
return true;
}
};
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/