Leetcode Top Interview 150
Source: Top Interview 150
88. Merge Sorted Array
画蛇添足的做法:
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
priority_queue<int, vector<int>, greater<int>> que; // 使用一个最小堆
for(int i = 0; i < m; i++) que.push(nums1[i]);
for(int j = 0; j < n; j++) que.push(nums2[j]);
nums1.clear(); // 清空nums1中的元素
while(!que.empty()){ // 逐个弹出最小堆中的元素
nums1.push_back(que.top());
que.pop();
}
}
};
暴力直接但能A过的做法:
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
for(int i = m, j = 0; i < m + n && j < n ; i++) nums1[i] = nums2[j++];
sort(nums1.begin(), nums1.end());
}
};
最希望看到的做法:#Two Pointers
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int p = m + n - 1; // 指向:排序后放入的位置
int idx1 = m - 1; // 指向:待排序的nums1的元素
int idx2 = n - 1; // 指向:待排序的nums2的元素
while(idx1 >= 0 && idx2 >= 0){ // 存在比较的情况
if(nums2[idx2] > nums1[idx1]){
nums1[p] = nums2[idx2];
idx2--;
}
else{
nums1[p] = nums1[idx1];
idx1--;
}
p--;
}
// 剩下没放完nums2的情况
while(idx2 >= 0){
nums1[p] = nums2[idx2];
idx2--;
p--;
}
}
};
优化过更好的代码
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int p = m + n - 1;
int i = m - 1;
int j = n - 1;
while(j >= 0){
// nums1中还有元素能“挪动”的情况
if(i >= 0 && nums1[i] >= nums2[j]) nums1[p--] = nums1[i--];
// nums1中没有元素能“挪动”的情况,因此不断放入nums2的元素
else nums1[p--] = nums2[j--];
}
}
};
27. Remove Element
方法:#Two Pointers
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int j = 0; // 指向:不被移除的元素
int cnt = 0;
/* i用来遍历一遍数组 */
for(int i = 0; i < nums.size(); i++){
if(nums[i] == val) continue;
else{
cnt++;
nums[j++] = nums[i];
}
}
return cnt;
}
};
26. Remove Duplicates from Sorted Array
方法:#Tow Pointers
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k = 0;
int j = 0; // 指向需要放入的元素
for(int i = 1; i < nums.size(); i++){
if(nums[i] != nums[j]){
j++;
nums[j] = nums[i];
k++;
}
}
return ++k;
}
};
80. Remove Duplicates from Sorted Array II
Solution: #Two Pointers
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int j = 0; // 指向放入的位置
int duplicates_count = 1; // 记录重复元素个数
for(int i = 1; i < nums.size(); i++){
if(nums[i] == nums[j]){ // 元素相同的情况
duplicates_count++;
if(duplicates_count > 2) continue; // 重复个数超过2,跳过
else{ // 不超过2,则放入指定位置
nums[++j] = nums[i];
}
}
else{ // 不相同的情况,放入指定位置,重制重复元素个数
duplicates_count = 1;
nums[++j] = nums[i];
}
}
return ++j;
}
};
更简洁的代码:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k = 2; // 指向应该放入的位置
if(nums.size() <= 2) return nums.size();
for(int i = 2; i < nums.size(); i++){
if(nums[i] != nums[k - 2]){ // 直接与前面第二个元素进行比较
nums[k] = nums[i];
k++;
}
}
return k;
}
};
169. Majority Element
Solution: #Math
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size() / 2];
}
};
Solution: #Hash Map
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map<int, int> mp;
for(auto x : nums) mp[x]++;
int n = nums.size();
for(auto it : mp){
if(it.second > n/2) return it.first;
}
return -1;
}
};
Solution: #Boyer-Moore 多数投票算法
- 初始化:
candidate
:候选元素,初始为 0。count
:计数器,初始为 0。
- 遍历数组:
- 如果
count == 0
,则将当前元素设为候选元素candidate
。 - 如果当前元素等于
candidate
,则count
增加 1。 - 如果当前元素不等于
candidate
,则 count 减少 1。
- 返回结果:
- 遍历结束后,
candidate
就是多数元素。
class Solution {
public:
int majorityElement(vector<int>& nums) {
int count = 0;
int candidate = 0;
for(auto x : nums){
if(count == 0) candidate = x;
if(x == candidate) count++;
else count--;
}
return candidate;
}
};
189. Rotate Array
Solution: #Simulation
class Solution {
public:
void rotate(vector<int>& nums, int k) {
k = k % nums.size();
vector<int> ans; // 用来拷贝原数组
for(int i = nums.size() - k; i < nums.size(); i++) ans.push_back(nums[i]);
for(int j = 0; j < nums.size() - k; j++) ans.push_back(nums[j]);
nums = ans;
}
};
Solution: #Math
class Solution {
public:
void rotate(vector<int>& nums, int k) {
k %= nums.size();
reverse(nums.begin(), nums.end());
reverse(nums.begin(), nums.begin() + k);
reverse(nums.begin() + k, nums.end());
}
};
reverse
是algorithm
头文件中的一个标准函数,调用方式为:reverse(起始迭代器, 结束迭代器);
121. Best Time to Buy and Sell Stock
Solution: #Greedy
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ans = 0;
int cur_profit = 0;
for(int i = 1; i < prices.size(); i++){
int gap = prices[i] - prices[i -1];
if(cur_profit + gap < gap) cur_profit = gap;
else cur_profit += gap;
ans = max(ans, cur_profit);
}
return ans;
}
};
// 0, -6, 4, -2, 3, -2
// 0, -1, -2, -1, -2
122. Best Time to Buy and Sell Stock II
Solution: #DP
这里的DP数组含义截止到i天,不持有/持有 股票时拥有的最大利润
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
vector<vector<int>> dp(n, vector<int>(2, 0));
dp[0][1] = -prices[0];
for(int i = 1; i < n; i++){
// 第i天有股票的情况:i-1天无股票在i天买 or i-1天有股票在i天继续持有
dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1]);
// 第i天无股票的情况:i-1天无股票在i天不买 or i-1天有股票在i天卖
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
}
return dp[n - 1][0]; // 在n-1天没有股票是最好情况
}
};
更加优化的方法:#Greedy
class Solution {
public:
int maxProfit(vector<int>& prices) {
int profit = 0;
for(int i = 1; i < prices.size(); i++){
// 第i天贪心选择:卖 or 不卖 利润最大的情况
profit = max(profit, profit + prices[i] - prices[i - 1]);
}
return profit;
}
};
55. Jump Game
Solution: #Greedy
class Solution {
public:
bool canJump(vector<int>& nums) {
int edge = 0; // 最大边界
int n = nums.size();
for(int i = 0; i < nums.size(); i++){
if(edge < i) return false; // 够不着下一个Index的情况
/** 贪心维护最大边界 **/
edge = max(edge, i + nums[i]);
}
return true;
}
};
Solution: 将上面的过程进行反推
class Solution {
public:
bool canJump(vector<int>& nums) {
int goal = nums.size() - 1;
for(int i = nums.size() - 2; i >= 0; i--){
/** 不断放缩可以达到边界的起点 **/
if(i + nums[i] >= goal) goal = i;
}
return goal == 0; // 判断最后放缩到的起点是否index = 0;
}
};
45. Jump Game II
这道题真的蛮有意思的,之前手撕出来了,时隔一段时间又来做又A不出来了…还是贪心的思路,贪心的选择最大边界,不过要维护一个当前边界和跳跃次数
class Solution {
public:
int jump(vector<int>& nums) {
int cnt = 0; // 计数
int end = 0; // 当前边界
int farthest = 0; // 下一步可以达到的最远位置
int goal = nums.size() - 1; // 目标位置
for(int i = 0; i < nums.size() - 1; i++){
farthest = max(farthest, i + nums[i]); // 计算当前能跳到的最大位置
if(i == end){ // 到达当前边界,必须增加跳跃次数
cnt++;
end = farthest;
if(end >= goal) break;
}
}
return cnt;
}
};
class Solution {
public:
int jump(vector<int>& nums) {
int cnt = 0;
int cur_end = 0; // 当前边界
int next_end = 0; // 接下来的边界
for(int i = 0; i < nums.size() -1; i++){
next_end = max(next_end, i + nums[i]); // 贪心选择接下来的边界
if(i == cur_end){ // 循环到当前边界,需要更新接下来的边界
cnt++;
cur_end = next_end;
}
}
return cnt;
}
};
274. H-Index
Solution: #Brute Force
class Solution {
public:
int hIndex(vector<int>& citations) {
int ans = 0;
for(int i = 0; i < citations.size(); i++){
int nominate = citations[i]; // 当前参照的元素
if(nominate == 0) continue; // 跳过引用次数为0的元素
int cnt = 0; // 记录大于等于参照元素引用的个数
for(int j = 0; j < citations.size(); j++){
if(citations[j] >= nominate) cnt++;
}
cnt = min(nominate, cnt); // 取最小值
ans = max(ans, cnt); // 取最大值
}
return ans;
}
};
Solution: #Sort
class Solution {
public:
int hIndex(vector<int>& citations) {
// 从小到大排列
sort(citations.begin(), citations.end(), greater<int>());
int ans = 0;
for(int i = 0; i < citations.size(); i++){
// 遍历找h-index
if(citations[i] >= i + 1) ans = i + 1;
else break;
}
return ans;
}
};
380. Insert Delete GetRandom O(1)
Solution: #Simulation
class RandomizedSet {
public:
unordered_map<int, int> mp; // 元素 : 放入位置
vector<int> vec; // 存放元素
RandomizedSet() {
mp.clear();
vec.clear(); // 初始化
}
bool insert(int val) {
if(mp.count(val)) return false; // 已放入的情况
vec.push_back(val);
int idx = vec.size() - 1; // 放入的index
mp[val] = idx; // 记录位置
return true;
}
bool remove(int val) {
if(!mp.count(val)) return false; // 没放入的情况
int idx = mp[val]; // 获取放入的位置
int last_element = vec.back(); // 获取最后一个元素
vec[idx] = last_element; // 覆盖要删除的元素
vec.pop_back();
mp[last_element] = idx; // 记录调整的元素位置
mp.erase(val); // 消除记录的位置
return true;
}
int getRandom() {
int idx = rand() % vec.size(); // 随机获取下标
return vec[idx];
}
};
/**
* Your RandomizedSet object will be instantiated and called as such:
* RandomizedSet* obj = new RandomizedSet();
* bool param_1 = obj->insert(val);
* bool param_2 = obj->remove(val);
* int param_3 = obj->getRandom();
*/
238. Product of Array Except Self
Solution: #前缀数组和后缀数组
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> pre_product(n, 1); // 前缀积
int start = 1;
for(int i = 1; i < n; i++){
start *= nums[i - 1];
pre_product[i] = start;
}
vector<int> nxt_product(n, 1); // 后缀积
int end = 1;
for(int i = n - 2; i >= 0; i--){
end *= nums[i + 1];
nxt_product[i] = end;
}
vector<int> ans(n, 0);
for(int i = 0; i < n; i++){
ans[i] = pre_product[i] * nxt_product[i];
}
return ans;
}
};
优化后的代码如下:
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> ans(n, 1);
int cur = 1;
for(int i = 1; i < n; i++){
cur *= nums[i - 1];
ans[i] = cur;
}
cur = 1;
for(int i = n - 2; i >= 0; i--){
cur *= nums[i + 1];
ans[i] *= cur;
}
return ans;
}
};
134. Gas Station
纯暴力爆超时的做法(33/39):
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int n = gas.size();
vector<int> buff(n, 0);
for(int i = 0; i < n; i++){
buff[i] = gas[i] - cost[i];
}
vector<int> candidates;
for(int i = 0; i < n; i++){
if(buff[i] >= 0) candidates.push_back(i);
}
for(int j = 0; j < candidates.size(); j++){
int idx = candidates[j];
int sum = 0;
bool flag = true;
for(int i = idx; i < n; i++){
sum += buff[i];
if(sum < 0) flag = false;
}
for(int j = 0; j < idx; j++){
if(!flag) continue;
sum += buff[j];
if(sum < 0) flag = false;
}
if(flag) return idx;
}
return -1;
}
};
Solution: #Greedy
- 遍历每个加油站:
- 对于每个加油站,计算出从当前加油站到下一个加油站所需的油量:
curGas += (gas[i] - cost[i])
。 - 如果
curGas
变成了负值,意味着从当前加油站开始无法继续前进,因此将当前的起点更新为i + 1
,并且重置curGas
为 0,尝试从下一个加油站作为起点重新开始。
- 对于每个加油站,计算出从当前加油站到下一个加油站所需的油量:
这段代码的精妙之处在于:如果某一段的油量不足,前面的加油站不可能成为起点(因为从前一个加油站开始也不可能成功),所以可以跳过一段,直接从下一个加油站开始尝试。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int gasSum = 0;
int costSum = 0;
for(int i = 0; i < gas.size(); i++){
gasSum += gas[i];
costSum += cost[i];
}
if(gasSum < costSum) return -1;
int curGas = 0;
int start = 0;
for(int i = 0; i < gas.size(); i++){
curGas += (gas[i] - cost[i]);
/*如果累加起来的gas总量小于0,那么这一段都不能作为起点*/
if(curGas < 0){
curGas = 0; // 重新从当前点出发
start = i + 1; // 这里非常有数学意味
}
}
return start;
}
};
135. Candy
Solution: #Greedy,从左到右遍历局部最优,从右到左局部最优
class Solution {
public:
int candy(vector<int>& ratings) {
int n = ratings.size();
if(n <= 1) return n;
vector<int> allo(n, 0); // 每个元素的糖果分配情况
allo[0] = 1;
for(int i = 1; i < n; i++){ // 从左到右分配
if(ratings[i] > ratings[i - 1]) allo[i] = allo[i -1] + 1;
else allo[i] = 1;
}
for(int i = n - 2; i >= 0; i--){ // 从右到左分配
if(ratings[i] > ratings[i + 1]){
if(allo[i] <= allo[i + 1]) allo[i] = allo[i + 1] + 1;
}
}
int sum = 0;
for(auto x : allo) sum += x;
return sum;
}
};
42. Trapping Rain Water
Solution: #Monotonic Stack
class Solution {
public:
int trap(vector<int>& height) {
stack<int> st; // 递增单调递减,存放index
int sum = 0;
for(int i = 0; i < height.size(); i++){
while(!st.empty() && height[i] > height[st.top()]){
int mid = height[st.top()];
st.pop();
if(!st.empty()){
int h = min(height[i], height[st.top()]) - mid;
int w = i - st.top() - 1;
sum += h * w;
}
}
st.push(i);
}
return sum;
}
};
Solution: #Two Pointers
class Solution {
public:
int trap(vector<int>& height) {
int l = 0, r = height.size() - 1; // 左右指针初始化
int left_max = -1, right_max = -1; // 初始化左右最大高度
int water = 0; // 用来累积水量
// 双指针遍历,直到左右指针相遇
while(l < r) {
// 更新左右最大值
left_max = max(left_max, height[l]);
right_max = max(right_max, height[r]);
// 根据左右最大值的大小来决定移动哪一边的指针
if(left_max < right_max) {
// 当前位置的水量 = 左侧最大值 - 当前柱子高度
water += left_max - height[l++];
} else {
// 当前位置的水量 = 右侧最大值 - 当前柱子高度
water += right_max - height[r--];
}
}
return water; // 返回总的水量
}
};
13. Roman to Integer
Solution: 暴力的做法,直接打表
class Solution {
public:
unordered_map<char, int> mp = {
{'I', 1},
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000}
};
unordered_map<string, int> dmp = {
{"IV", 4},
{"IX", 9},
{"XL", 40},
{"XC", 90},
{"CD", 400},
{"CM", 900}
};
int romanToInt(string s) {
int sum = 0;
int i = 0;
while(i + 1 < s.size()){
char c1 = s[i];
char c2 = s[i + 1];
string tmp = {c1, c2};
if(dmp.count(tmp)){
sum += dmp[tmp];
i += 2;
}
else{
sum += mp[c1];
i++;
}
}
if(i == s.size() - 1){
sum += mp[s[i]];
}
return sum;
}
};
优化一下:
class Solution {
public:
int romanToInt(string s) {
unordered_map<char,int>m;
m['I']=1;
m['V']=5;
m['X']=10;
m['L']=50;
m['C']=100;
m['D']=500;
m['M']=1000;
int ans=0;
for(int i=0;i<s.length();i++){
if(m[s[i]]<m[s[i+1]]){
ans-=m[s[i]];
}
else{
ans+=m[s[i]];
}
}
return ans;
}
};
12. Integer to Roman
Solution: #Hash Map, #Math
class Solution {
public:
string intToRoman(int num) {
int d[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
string s[] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
string ans = "";
for(int i = 0 ; i < 13; i++){
while(num >= d[i]){
num -= d[i];
ans += s[i];
}
}
return ans;
}
};
58. Length of Last Word
Solution: #STL
class Solution {
public:
int lengthOfLastWord(string s) {
istringstream iss(s);
string words;
vector<string> vec;
while(iss >> words){
vec.push_back(words);
}
return vec.back().size();
}
};
Solution: 移动尾指针
class Solution {
public:
int lengthOfLastWord(string s) {
int p = s.size() - 1;
int cnt = 0;
/*定位到最后一个单词末尾*/
while(p >= 0 && s[p] == ' '){
p--;
}
/*计算最后一个单词长度*/
while(p >= 0 && s[p] != ' '){
p--;
cnt++;
}
return cnt;
}
};
Solution: #Flag
class Solution {
public:
int lengthOfLastWord(string s) {
int length = 0;
bool counting = false; // 开始计数的标志
for(int i = s.size() - 1; i >= 0; i--){
if(s[i] != ' '){
counting = true;
length++;
}
else if(counting){ // 停止计数
break;
}
}
return length;
}
};
14. Longest Common Prefix
Solution: #Greedy
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
string ans = "";
bool end = true; // 比对结束的标志
/*暴力枚举第一个字符串的所有前缀*/
for(int i = 0; i < strs[0].size(); i++){
/*对每个字符串进行暴力比对*/
for(int j = 0; j < strs.size(); j++){
if(strs[j][i] != strs[0][i]){
end = false;
break;
}
}
if(end) ans += strs[0][i];
else break;
}
return ans;
}
};
Solution: #Sort, #Double Pointers
- 如果这些两个字符串有共同前缀,那么整个数组中的所有字符串也会有相同的前缀。因此,只需要比较数组中最小和最大字符串的前缀即可。
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
string ans = "";
sort(strs.begin(), strs.end());
int n = strs.size();
string firstStr = strs[0];
string lastStr = strs[n - 1];
for(int i = 0; i < min(firstStr.size(), lastStr.size()); i++){
if(firstStr[i] != lastStr[i]) break;
ans += firstStr[i];
}
return ans;
}
};
151. Reverse Words in a String
Solution: #STL
class Solution {
public:
string reverseWords(string s) {
string word;
istringstream iss(s);
vector<string> vec;
while(iss >> word){
vec.push_back(word);
}
string ans = "";
for(int i = vec.size() - 1; i >= 0; i--){
ans += vec[i];
if(i != 0) ans += " ";
}
return ans;
}
};
Solution: #Simulation
class Solution {
public:
string reverseWords(string s) {
string ans = "";
string tmp = "";
for(int i = 0; i < s.size(); i++){
if(s[i] == ' '){
if(!tmp.empty()){ // 遍历完单词的情况
if(!ans.empty()) ans = tmp + " " + ans; // 不是第一个单词的情况
else ans = tmp; // 是第一个单词的情况
tmp.clear();
}
}
else tmp += s[i];
}
if(!tmp.empty()){ // 还剩下最后一个单词
if(!ans.empty()) ans = tmp + " " + ans; // 不是第一个单词的情况
else ans = tmp; // 第一个单词的情况
}
return ans;
}
};
Solution: #Two Pointers
class Solution {
public:
string reverseWords(string s) {
int slow = 0, fast = 0;
reverse(s.begin(), s.end()); // 第一步:整体反转字符串
bool wordStart = false; // 标记是否正在处理一个单词
// 使用 fast 指针遍历字符串
for (fast = 0; fast < s.size(); fast++) {
if (s[fast] != ' ') { // 找到一个单词的开始
if (wordStart && slow > 0) {
s[slow++] = ' '; // 如果已经找到一个单词,且不是字符串开头,加空格分隔单词
}
int start = fast;
while (fast < s.size() && s[fast] != ' ') {
s[slow++] = s[fast++]; // 把当前单词复制到新的位置
}
// 反转单词:把已复制的单词部分反转回来
reverse(s.begin() + slow - (fast - start), s.begin() + slow);
wordStart = true; // 标记已经找到了一个单词
}
}
// 调整字符串的大小,去掉多余的部分
s.resize(slow);
return s;
}
};
6. Zigzag Conversion
Solution: #Simulation
class Solution {
public:
string convert(string s, int numRows) {
if(numRows == 1) return s;
unordered_map<int, vector<char>> mp;
int r = 0;
bool flop = false; // 控制反转。初始化为false
for(int i = 0; i < s.size(); i++){
/*到达边界的时候,则需要反转*/
if(r == 0 || r == numRows - 1) flop = !flop;
mp[r].push_back(s[i]);
if(flop) r++;
else r--;
}
string ans = "";
for(int i = 0; i < numRows; i++){
for(auto c : mp[i]) ans += c;
}
return ans;
}
};
优化后的代码:
class Solution {
public:
string convert(string s, int numRows) {
string ans;
vector<string> temp(numRows);
int i = 0;
while(i < s.size()){
/*模拟从上到下的遍历过程*/
for(int j = 0; j < numRows && i < s.size(); j++){
temp[j] += s[i++];
}
/*模拟从下到上的过程*/
for(int j = numRows - 2; j > 0 && i < s.size(); j--){
temp[j] += s[i++];
}
}
for(int i = 0; i < numRows; i++) ans += temp[i];
return ans;
}
};
28. Find the Index of the First Occurrence in a String
Solution: string
类成员函数
class Solution {
public:
int strStr(string haystack, string needle) {
return haystack.find(needle);
}
};
正规的做法:#Tow Pointers
class Solution {
public:
int strStr(string haystack, string needle) {
int idx = -1;
int j = 0;
for(int i = 0; i < haystack.size(); i++){
int start = i;
while(start < haystack.size() && j < needle.size() && haystack[start] == needle[j]){
start++;
j++;
}
if(j == needle.size()) return i;
else j = 0;
}
return -1;
}
};
Solution:枚举子串进行比较
class Solution {
public:
int strStr(string haystack, string needle) {
if(haystack.size() < needle.size()) return -1;
for(int i = 0; i <= haystack.size() - needle.size(); i++){
if(haystack.substr(i, needle.size()) == needle) return i;
}
return -1;
}
};
68. Text Justification
Solution: #Greedy
class Solution {
public:
vector<string> fullJustify(vector<string>& words, int maxWidth) {
vector<string> result;
vector<string> currentLine; // 用来存储当前行的单词,直到该行的长度超过 maxWidth。
int currentLength = 0; // 记录当前行中所有单词的总长度(不包括空格)
for(const string& word : words){
/* 判断当前行加上当前单词是否会超出 maxWidth */
if(currentLength + word.length() + currentLine.size() > maxWidth){
/* 计算当前行需要的空格*/
int spaces = maxWidth - currentLength;
/* 如果当前行只有一个单词,直接左对齐,空格填充右侧 */
if(currentLine.size() == 1){
result.push_back(currentLine[0] + string(spaces, ' '));
}
else{
/* 均匀分配空格 */
int evenSpace = spaces / (currentLine.size() - 1); // 每个空格应该有的基本数量
int extraSpace = spaces % (currentLine.size() - 1); // 剩余空格数量
string line = currentLine[0];
/* 分配空格 */
for(int i = 1; i < currentLine.size(); i++){
if(i <= extraSpace){
line += string(evenSpace + 1, ' ') + currentLine[i];
}
else{
line += string(evenSpace, ' ') + currentLine[i];
}
}
result.push_back(line);
}
/* 清空当前行的单词和长度,准备下一行 */
currentLine.clear();
currentLength = 0;
}
/* 将当前单词加入到当前行 */
currentLine.push_back(word);
currentLength += word.length();
}
/* 处理最后一行,左对齐 */
string lastLine = "";
for(int i = 0; i < currentLine.size(); i++){
lastLine += currentLine[i];
if(i < currentLine.size() - 1){
lastLine += " ";
}
}
lastLine += string(maxWidth - lastLine.length(), ' ');
result.push_back(lastLine);
return result;
}
};
125. Valid Palindrome
Solution: #Simulation
class Solution {
public:
string converString(string s){ // 清楚标点和大小写转换
int gap = 'A' - 'a';
string tmp = "";
for(int i = 0; i < s.size(); i++){
if('A' <= s[i] && s[i] <= 'Z'){
tmp.push_back(s[i] - gap);
}
else if('a' <= s[i] && s[i] <= 'z' || '0' <= s[i] && s[i] <= '9'){
tmp.push_back(s[i]);
}
}
return tmp;
}
bool isPalindrome(string s) {
string ss = converString(s);
string tmp = ss;
reverse(ss.begin(), ss.end());
return tmp == ss; // 反转后的字符串和反转前字符串比较
}
};
Solution:#Tow Pointers, 使用双指针和类成员函数优化后的代码
class Solution {
public:
bool isPalindrome(string s) {
int n = s.size(), l = 0, r = n - 1;
while(l < r){
while(l < r && !isalnum(s[l])) l++;
while(l < r && !isalnum(s[r])) r--;
if(tolower(s[l++]) != tolower(s[r--])) return false;
}
return true;
}
};
392. Is Subsequence
Solution: #Tow Pointers
class Solution {
public:
bool isSubsequence(string s, string t) {
int m = s.size();
int n = t.size();
if(m == 0 && n == 0) return true;
if(m > n) return false;
int j = 0;
for(int i = 0; i < n; i++){
if(t[i] == s[j]) j++;
if(j == m) return true;
}
return false;
}
};
优化后的双指针写法:
class Solution {
public:
bool isSubsequence(string s, string t) {
int sptr = 0;
int tptr = 0;
while(sptr < s.size() && tptr < t.size()){
if(s[sptr] == t[tptr]) sptr++;
tptr++;
}
return sptr == s.size();
}
};
Solution:#DP,也可以强行用动态规划来做
class Solution {
public:
bool isSubsequence(string s, string t) {
int n = s.size();
int m = t.size();
/* dp数组:当前两个字符串的子串所匹配的最大长度 */
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = dp[i][j - 1];
}
}
return dp[n][m] == n;
}
};
167. Two Sum II - Input Array Is Sorted
Solution: #Hash Table
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
vector<int> ans;
unordered_map<int, int> mp;
for(int i = 0; i < numbers.size(); i++){
/* 由小到大的index顺序 */
if(mp.count(target - numbers[i]) && mp[target - numbers[i]] < i){
ans.push_back(mp[target - numbers[i]] + 1);
ans.push_back(i + 1);
}
/* 没找到匹配才建立映射,顺序不能在if执行之前*/
mp[numbers[i]] = i;
}
return ans;
}
};
更精简的写法:#Two Pointers
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int n = numbers.size();
int l = 0, r = n - 1;
while(l < r){
if(numbers[l] + numbers[r] == target) break;
else if(numbers[l] + numbers[r] > target) r--;
else l++;
}
return {l + 1, r + 1};
}
};
11. Container With Most Water
Solution: #Two Pointers
class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0;
int r = height.size() - 1;
int ans = 0;
while(l < r){
int w = r - l;
int h = min(height[l], height[r]);
ans = max(ans, w * h);
/* 优先移动高最小的边界 */
if(height[l] < height[r]) l++;
else r--;
}
return ans;
}
};
15. 3Sum
Solution: #Two Pointers
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
for(int i = 0; i < nums.size() - 2; i++){
/*对目标元素进行去重*/
if(i > 0 && nums[i] == nums[i - 1]) continue;
int l = i + 1;
int r = nums.size() - 1;
while(l < r){
if(nums[i] + nums[l] + nums[r] == 0){
ans.push_back({nums[i], nums[l], nums[r]});
/*对左右指针去重*/
while(l + 1 < r && nums[l + 1] == nums[l]) l++;
l++;
while(r - 1 > l && nums[r-1] == nums[r]) r--;
r--;
}
else if(nums[i] + nums[l] + nums[r] < 0) l++;
else r--;
}
}
return ans;
}
};
209. Minimum Size Subarray Sum
Solution: #Sliding Window
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT_MAX;
int start = 0;
int sum = 0;
for(int end = 0; end < nums.size(); end++){
sum += nums[end];
/* 当前和大于target则移动窗口左边界 */
while(sum >= target){
int subLength = end - start + 1;
result = min(result, subLength);
sum -= nums[start];
start++;
}
}
return result == INT_MAX ? 0 : result;
}
};
3. Longest Substring Without Repeating Characters
Solution: #Sliding Window
class Solution {
public:
int lengthOfLongestSubstring(string s) {
set<char> st;
int length = 0;
int ans = 0;
int start = 0;
for(int end = 0; end < s.size(); end++){
/* 果字符已存在于集合中,移动左边界 */
while(st.count(s[end]) > 0){
st.erase(s[start]);
start++;
}
st.insert(s[end]);
length = end - start + 1;
ans = max(ans, length);
}
return ans;
}
};
30. Substring with Concatenation of All Words
Solution: #Brute Force, #Sliding Window
class Solution {
public:
bool checkSubstring(unordered_map<string, int> wordCount, string s, int wordLen){
for(int i = 0; i < s.size(); i += wordLen){
if(i + wordLen > s.size()) return false; // 确保不会越界
string w = s.substr(i, wordLen);
if(wordCount.find(w) != wordCount.end()){
if(--wordCount[w] < 0) return false;
}
else return false;
}
return true;
}
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> ans;
if(words.empty() || s.empty()) return ans;
unordered_map<string, int> wordCount;
for(auto x : words) wordCount[x]++;
int wordLen = words[0].size();
int subLen = words.size() * wordLen;
// 如果子串长度超过原字符串长度,直接返回空结果
if(subLen > s.size()) return ans;
for(int i = 0; i <= s.size() - subLen; i++){
if(checkSubstring(wordCount, s.substr(i, subLen), wordLen)) ans.push_back(i);
}
return ans;
}
};
优化后的代码:#Sliding Window
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> result;
if (s.empty() || words.empty()) return result;
int wordLength = words[0].size();
int wordCount = words.size();
int totalLength = wordLength * wordCount;
if (totalLength > s.length()) return result;
// 统计words中每个单词出现的次数
unordered_map<string, int> wordFreq;
for (const string& word : words) {
wordFreq[word]++;
}
// 对每个可能的起始位置进行处理(由于单词长度可能大于1,需要考虑不同的偏移)
for (int offset = 0; offset < wordLength; offset++) {
// 对于每个偏移量,使用滑动窗口
int left = offset;
unordered_map<string, int> currentFreq;
int count = 0; // 窗口内有效单词数量
for (int right = offset; right <= s.length() - wordLength; right += wordLength) {
string currentWord = s.substr(right, wordLength);
// 如果当前单词不在words中,重置窗口
if (wordFreq.find(currentWord) == wordFreq.end()) {
currentFreq.clear();
count = 0;
left = right + wordLength;
continue;
}
// 增加当前单词的计数
currentFreq[currentWord]++;
count++;
// 如果当前单词出现次数过多,缩小窗口直到匹配
while (currentFreq[currentWord] > wordFreq[currentWord]) {
string leftWord = s.substr(left, wordLength);
currentFreq[leftWord]--;
count--;
left += wordLength;
}
// 如果窗口大小正好等于所有单词的总数,找到一个匹配
if (count == wordCount) {
result.push_back(left);
// 移动窗口左边界,继续寻找下一个匹配
string leftWord = s.substr(left, wordLength);
currentFreq[leftWord]--;
count--;
left += wordLength;
}
}
}
return result;
}
};
76. Minimum Window Substring
Solution: #Sliding Window
class Solution {
public:
string minWindow(string s, string t) {
int m = s.size();
int n = t.size();
if(m < n) return "";
unordered_map<char, int> charCount;
for(char c : t) charCount[c]++; // 记录t中字符出现的次数
int required = charCount.size(); // 需要匹配的字符种类
int formed = 0; // 已匹配成功的字符种类
int left = 0;
int right = 0;
int minLen = INT_MAX;
int start = 0; // Start index of minimum window
unordered_map<char, int> windowCount;
while(right < s.size()){
char c = s[right];
windowCount[c]++;
if(charCount.find(c) != charCount.end() && windowCount[c] == charCount[c]){
formed++; // 成功匹配完一类字符
}
/* 尝试缩小滑动窗口 */
while(left <= right && formed == required){
c = s[left];
if(right - left + 1 < minLen){
minLen = right - left + 1;
start = left;
}
windowCount[c]--;
if(charCount.find(c) != charCount.end() && windowCount[c] < charCount[c]){
formed--; // 匹配次数减少,导致匹配字符类型数-1
}
left++;
}
right++;
}
return minLen == INT_MAX ? "" : s.substr(start, minLen);
}
};
36. Valid Sudoku
Solution: #Simulation
class Solution {
public:
/* 判断当前字符是否是1~9 */
bool isValidChar(char c){
if('1' <= c && c <= '9') return true;
return false;
}
/* 检查 3x3 的格子是否有重复数字 */
bool checkBox(vector<vector<char>>& board, int x, int y){
set<char> st;
for(int i = x; i < x + 3; i++){
for(int j = y; j < y + 3; j++){
char c = board[i][j];
if(isValidChar(c)){
if(st.find(c) != st.end()) return false;
st.insert(c);
}
}
}
return true;
}
bool isValidSudoku(vector<vector<char>>& board) {
set<char> row;
set<char> col;
for(int i = 0; i < 9; i++){ // 检查行
row.clear();
for(int j = 0; j < 9; j++){
char c = board[i][j];
if(isValidChar(c)){
if(row.find(c) != row.end()) return false;
row.insert(c);
}
}
}
for(int j = 0; j < 9; j++){ // 检查列
col.clear();
for(int i = 0; i < 9; i++){
char c = board[i][j];
if(isValidChar(c)){
if(col.find(c) != col.end()) return false;
col.insert(c);
}
}
}
for(int i = 0; i < 9; i += 3){ // 检查3x3的小格子
for(int j = 0; j < 9; j += 3){
if(!checkBox(board, i, j)) return false;
}
}
return true;
}
};
Solution: #Hash Table, 更加优化的方法,只用遍历一次数组,采用打表记录的方式
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
// 使用3个9x9的布尔数组,分别记录行、列和方格中数字的出现情况
bool row[9][9] = {false}; // row[i][j]表示第i行是否出现数字j+1
bool col[9][9] = {false}; // col[i][j]表示第i列是否出现数字j+1
bool box[9][9] = {false}; // box[i][j]表示第i个方格是否出现数字j+1
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] == '.') continue;
int num = board[i][j] - '1'; // 将字符转为0-8的索引
int box_index = (i / 3) * 3 + j / 3; // 计算当前位置属于哪个3x3方格
// 检查当前数字在行、列、方格中是否已经出现过
if (row[i][num] || col[j][num] || box[box_index][num]) {
return false;
}
// 标记数字已出现
row[i][num] = true;
col[j][num] = true;
box[box_index][num] = true;
}
}
return true;
}
};
54. Spiral Matrix
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> ans;
if(matrix.empty()) return ans;
int m = matrix.size();
int n = matrix[0].size();
int left = 0, right = n - 1, top = 0, bottom = m - 1; // 设置边界
while(left <= right && top <= bottom){
for(int j = left; j <= right; j++) ans.push_back(matrix[top][j]);
top++;
for(int i = top; i <= bottom; i++) ans.push_back(matrix[i][right]);
right--;
if(top <= bottom){ // 从右到左(需要检查top <= bottom)
for(int j = right; j >= left; j--) ans.push_back(matrix[bottom][j]);
bottom--;
}
if(left <= right){ // 从下到上(需要检查left <= right)
for(int i = bottom; i >= top; i--) ans.push_back(matrix[i][left]);
left++;
}
}
return ans;
}
};
优化后的代码
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> ans;
int m = matrix.size(); // line
int n = matrix[0].size(); // column
if(m == 0 || n == 0) return ans;
int left = 0, right = n - 1, top = 0, bottom = m - 1;
while(left <= right && top <= bottom){
for(int j = left; j <= right; j++) ans.push_back(matrix[top][j]);
for(int i = top + 1; i <= bottom; i++) ans.push_back(matrix[i][right]);
if(top < bottom && left < right){
for(int j = right - 1; j > left; j--) ans.push_back(matrix[bottom][j]);
for(int i = bottom; i > top; i--) ans.push_back(matrix[i][left]);
}
top++;
bottom--;
left++;
right--;
}
return ans;
}
};
48. Rotate Image
Solution: #Queue,开一个队列把所有数装进来…
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
queue<int> que;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
que.push(matrix[i][j]);
}
}
for(int j = n - 1; j >= 0; j--){
for(int i = 0; i < n; i++){
matrix[i][j] = que.front();
que.pop();
}
}
return;
}
};
优化后的方案,不用开辟新的空间。
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
// 先转置
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
swap(matrix[i][j], matrix[j][i]);
}
}
// 再反转
for(int i = 0; i < n; i++){
reverse(matrix[i].begin(), matrix[i].end());
}
}
};
73. Set Matrix Zeroes
Solution: #Brute Force
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
set<int> row;
set<int> col;
int m = matrix.size();
int n = matrix[0].size();
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(matrix[i][j] == 0){
row.insert(i);
col.insert(j);
}
}
}
for(auto i : row) matrix[i] = vector<int>(n, 0);
for(auto j : col){
for(int i = 0; i < m; i++) matrix[i][j] = 0;
}
}
};
优化后的方法:
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
bool firstRowZero = false, firstColZero = false;
// 记录第一列是否有0
for(int i = 0; i < m; i++){
if(matrix[i][0] == 0) firstColZero = true;
}
// 记录第一行是否有0
for(int j = 0; j < n; j++){
if(matrix[0][j] == 0) firstRowZero = true;
}
// 遍历整个矩阵,用第一行和第一列记录 0
for(int i = 1; i < m; i++){
for(int j = 1; j < n; j++){
if(matrix[i][j] == 0){
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}
for(int i = 1; i < m; i++){
for(int j = 1; j < n; j++){
if(matrix[i][0] == 0 || matrix[0][j] == 0) matrix[i][j] = 0;
}
}
if(firstRowZero){
for(int j = 0; j < n; j++) matrix[0][j] = 0;
}
if(firstColZero){
for(int i = 0; i < m; i++) matrix[i][0] = 0;
}
}
};
289. Game of Life
Solution: #Brute Force,对每一个点逐一检查
class Solution {
public:
bool Alive(vector<vector<int>>& board, int x, int y){
int bottom = board.size() - 1;
int right = board[0].size() - 1;
int cnt = 0;
for(int i = x - 1; i <= x + 1; i++){
for(int j = y - 1; j <= y + 1; j++){
if(i >= 0 && i <= bottom && j >= 0 && j <= right){
if(i == x && j == y) continue;
else if(board[i][j] == 1) cnt++;
}
}
}
bool curAlive = (board[x][y] == 1 ? true : false);
if(curAlive && cnt < 2) return false;
else if(curAlive && (cnt == 2 || cnt == 3)) return true;
else if(curAlive && cnt >= 3) return false;
else if(!curAlive && cnt == 3) return true;
else return false;
}
void gameOfLife(vector<vector<int>>& board) {
vector<vector<int>> tmp = board;
for(int i = 0; i < board.size(); i++){
for(int j = 0; j < board[0].size(); j++){
if(Alive(board, i, j)) tmp[i][j] = 1;
else tmp[i][j] = 0;
}
}
board = tmp;
return;
}
};
优化后的方法:
-1
表示 活细胞变死(即原本是1
,但根据规则变成0
)2
表示 死细胞变活(即原本是0
,但根据规则变成1
)。
class Solution {
public:
void gameOfLife(vector<vector<int>>& board) {
int m = board.size(), n = board[0].size();
vector<int> directions = {-1, 0, 1}; // 方向数组,表示相邻八个方向的移动
// 遍历计算每个细胞的状态
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
int liveNeighbors = 0;
for(int dx : directions){
for(int dy : directions){
if(dx == 0 && dy == 0) continue; // 跳过自身
int ni = i + dx, nj = j + dy;
// 原地修改,注意找活细胞要用绝对值
if(ni >= 0 && ni < m && nj >= 0 && nj < n && abs(board[ni][nj]) == 1){
liveNeighbors++;
}
}
}
if(board[i][j] == 1 && (liveNeighbors < 2 || liveNeighbors > 3)){
board[i][j] = -1; // 1 -> 0
}
else if(board[i][j] == 0 && liveNeighbors == 3){
board[i][j] = 2; // 0 -> 1
}
}
}
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(board[i][j] == -1) board[i][j] = 0;
else if(board[i][j] == 2) board[i][j] = 1;
}
}
}
};
383. Ransom Note
Solution: #Hash Table
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
unordered_map<char, int> mp; // 统计magazine中字符出现次数
for(char c : magazine){
mp[c]++;
}
for(char c : ransomNote){
if(mp.find(c) == mp.end()) return false; // 字符不存在
else{
if(--mp[c] < 0) return false; // 字符不够用
}
}
return true;
}
};
205. Isomorphic Strings
Solution: #Hash Table
class Solution {
public:
bool isIsomorphic(string s, string t) {
int m = s.size(), n = t.size();
if(m != n) return false;
unordered_map<int, int> mp;
set<char> st; // 记录已经被map的字符
for(int i = 0; i < m; i++){
if(mp.find(s[i]) != mp.end()){ // 如果有记录
if(mp[s[i]] != t[i]) return false;
}
else{ // 没有记录的情况
if(st.find(t[i]) != st.end()) return false;
else{
mp[s[i]] = t[i];
st.insert(t[i]);
}
}
}
return true;
}
};
优化过的代码:
class Solution {
public:
bool isIsomorphic(string s, string t) {
unordered_map<char, char> mp;
for(int i = 0; i < s.size(); i++){
char sc = s[i];
char tc = t[i];
if(mp.count(sc)){ // 映射不正确
if(mp[sc] != tc) return false;
}
else{
for(auto pair : mp){ // 检查是否映射冲突
if(pair.second == tc) return false;
}
mp[sc] = tc;
}
}
return true;
}
};
290. Word Pattern
Solution:# Hash Table
class Solution {
public:
bool wordPattern(string pattern, string s) {
unordered_map<char, string> mp;
vector<string> words;
string tmp;
istringstream iss(s);
while(iss >> tmp){
words.push_back(tmp);
}
if(pattern.size() != words.size()) return false; // 比对数量不对等
for(int i = 0; i < pattern.size(); i++){
char pc = pattern[i];
if(mp.count(pc)){
if(mp[pc] != words[i]) return false;
}
else{
for(pair str : mp){ // 检查是否有冲突映射
if(str.second == words[i]) return false;
}
mp[pc] = words[i];
}
}
return true;
}
};
242. Valid Anagram
Solution: #Sort
class Solution {
public:
bool isAnagram(string s, string t) {
sort(s.begin(), s.end());
sort(t.begin(), t.end());
return s == t;
}
};
Solution: #Hash Table
class Solution {
public:
bool isAnagram(string s, string t) {
unordered_map<char, int> mp;
int kinds = 0; // 记录字符种类
for(auto c : t){
if(mp.find(c) != mp.end()) mp[c]++;
else{
mp[c] = 1;
kinds++;
}
}
for(auto c : s){
if(!mp.count(c)) return false;
else{
if(--mp[c] == 0) kinds--; // 匹配完一种字符
else if(mp[c] < 0) return false;
}
}
return kinds == 0;
}
};
Solution: #Hash Table,优化后的映射
class Solution {
public:
bool isAnagram(string s, string t) {
vector<int>count(26, 0);
for(auto c : s){
count[c - 'a']++;
}
for(auto c : t){
count[c - 'a']--;
}
for(auto num : count){
if(num != 0) return false;
}
return true;
}
};
49. Group Anagrams
Solution: #Hash Table, #Sort
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> ans;
unordered_map<string, vector<int>> ump;
for(int i = 0; i < strs.size(); i++){
string tmp = strs[i];
sort(tmp.begin(), tmp.end()); // sort调整顺序
ump[tmp].push_back(i);
}
for(auto it : ump){
vector<string> vec;
for(auto idx : it.second){
vec.push_back(strs[idx]);
}
ans.push_back(vec);
}
return ans;
}
};
优化后的代码
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> mp;
for(auto s : strs){
string t = s;
sort(t.begin(), t.end());
mp[t].push_back(s);
}
vector<vector<string>> ans;
for(auto p : mp){
ans.push_back(p.second);
}
return ans;
}
};
手搓哈希映射的方法:
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> ans;
for(auto s : strs){
array<int, 26> count = {0};
for(char c : s){
count[c - 'a']++;
}
string key;
for(int num : count){
key += to_string(num) + "#"; // 避免哈希冲突
}
ans[key].push_back(s);
}
vector<vector<string>> result;
for(auto entry : ans){
result.push_back(entry.second);
}
return result;
}
};
1. Two Sum
Solution: #Hash Table
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> mp; // value : index
for(int i = 0; i < nums.size(); i++){
if(mp.count(target - nums[i])) return {mp[target - nums[i]], i};
else mp[nums[i]] = i;
}
return {};
}
};
Solution: #Two Pointers
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<pair<int, int>> v;
for(int i = 0; i < nums.size(); i++) v.push_back({nums[i], i}); // 记录初始位置
sort(v.begin(), v.end());
int i = 0, j = nums.size() - 1; // 双指针初始化
while(i < j){
if(v[i].first + v[j].first == target) return {v[i].second, v[j].second};
else if(v[i].first + v[j].first > target) j--;
else i++;
}
return {};
}
};
202. Happy Number
class Solution {
public:
int calc(int n){ // 计算每个digit的平方和
string tmp = to_string(n);
int sum = 0;
for(int i = 0; i < tmp.size(); i++){
sum += (tmp[i] - '0') * (tmp[i] - '0');
}
return sum;
}
bool isHappy(int n) {
set<int> st; // 检查是否有loop出现
while(calc(n) != 1){
if(st.count(calc(n))) return false;
else{
st.insert(calc(n));
n = calc(n);
}
}
return true;
}
};
优化后的方法,使用快慢指针。#Tow Pointers
class Solution {
public:
int calc(int n){ // 更优质的计算
int sum = 0;
while(n){
sum += (n % 10) * (n % 10);
n /= 10;
}
return sum;
}
bool isHappy(int n) {
int slow = n;
int fast = n;
do{ // 快慢指针
slow = calc(slow);
fast = calc(calc(fast));
if(fast == 1) return true;
}while(slow != fast);
return false;
}
};
219. Contains Duplicate II
Solution: #Hash Table
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_map<int, int> mp; // value : 最近一次出现的index
for(int i = 0; i < nums.size(); i++){
if(mp.count(nums[i])){
if(i - mp[nums[i]] <= k) return true;
else mp[nums[i]] = i;
}
else{
mp[nums[i]] = i;
}
}
return false;
}
};
优化的解法#Sliding Window
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_set<int> window; // 维护滑动窗口内的元素
for(int i = 0; i < nums.size(); i++){
if(window.count(nums[i])) return true;
window.insert(nums[i]);
if(window.size() > k) window.erase(nums[i - k]); // 维护窗口大小
}
return false;
}
};
128. Longest Consecutive Sequence
Solution: #Hash Table
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
set<int> st;
for(auto x : nums) st.insert(x); // 去重
int result = 0;
while(!st.empty()){
int x = *st.begin(); // 取第一个元素
int cnt = 1;
int left = x - 1;
int right = x + 1;
while(st.count(left)){ // 向左寻找连续序列
st.erase(left);
left--;
cnt++;
}
while(st.count(right)){ // 向右寻找连续序列
st.erase(right);
right++;
cnt++;
}
result = max(result, cnt);
st.erase(x);
}
return result;
}
};
优化后的代码:
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
if(nums.empty()) return 0;
unordered_set<int> st(nums.begin(), nums.end()); // 用 HashSet 存储去重后的元素
int result = 0;
for(int x : st){
if(!st.count(x - 1)){ // 没有向左连续的情况,那么直接向右找连续元素
int currNum = x;
int cnt = 1;
while(st.count(currNum + 1)){
currNum++;
cnt++;
}
result = max(result, cnt);
}
}
return result;
}
};
228. Summary Ranges
Solution: #Simulation
class Solution {
public:
vector<string> summaryRanges(vector<int>& nums) {
vector<string> ans;
int n = nums.size();
for (int i = 0; i < n; i++) {
int start = nums[i];
// 找出连续区间的结束位置
while (i + 1 < n && nums[i + 1] == nums[i] + 1) i++;
if (start == nums[i])
ans.push_back(to_string(start));
else
ans.push_back(to_string(start) + "->" + to_string(nums[i]));
}
return ans;
}
};
56. Merge Intervals
Solution: #Greedy, #Sort
class Solution {
public:
static bool cmp(const vector<int>& a, const vector<int>& b) {
if (a[0] == b[0]) return a[1] < b[1];
return a[0] < b[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), cmp);
vector<vector<int>> ans;
if (intervals.empty()) return ans;
ans.push_back(intervals[0]);
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i][0] > ans.back()[1]) { // 不合并区间
ans.push_back(intervals[i]);
} else { // 合并区间的情况
ans.back()[1] = max(ans.back()[1], intervals[i][1]);
}
}
return ans;
}
};
可以稍微优化一下
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
// 1. 使用lambda表达式简化排序
sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b) {
return a[0] < b[0]; // 只需按起始点排序即可
});
vector<vector<int>> result;
if (intervals.empty()) return result;
result.push_back(intervals[0]);
for (int i = 1; i < intervals.size(); i++) {
// 检查是否可以合并
if (intervals[i][0] <= result.back()[1]) {
// 合并区间
result.back()[1] = max(result.back()[1], intervals[i][1]);
} else {
// 添加新区间
result.push_back(intervals[i]);
}
}
return result;
}
};
57. Insert Interval
Solution: #Greedy
class Solution {
public:
vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
vector<vector<int>> ans;
intervals.push_back(newInterval);
sort(intervals.begin(), intervals.end(),[](const vector<int>& a, const vector<int>& b){return a[0] < b[0];});
ans.push_back(intervals[0]);
for(int i = 1; i < intervals.size(); i++){
int start = intervals[i][0];
int end = intervals[i][1];
if(start > ans.back()[1]) ans.push_back(intervals[i]);
else ans.back()[1] = max(ans.back()[1], end);
}
return ans;
}
};
优化后的代码:#Simulation
class Solution {
public:
vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
int n = intervals.size();
int i = 0;
vector<vector<int>> ans;
while(i < n && intervals[i][1] < newInterval[0]) ans.push_back(intervals[i++]);
while(i < n && newInterval[1] >= intervals[i][0]){
newInterval[0] = min(newInterval[0], intervals[i][0]);
newInterval[1] = max(newInterval[1], intervals[i][1]);
i++;
}
ans.push_back(newInterval);
while(i < n) ans.push_back(intervals[i++]);
return ans;
}
};
452. Minimum Number of Arrows to Burst Balloons
Solution: #Greedy
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
if(points.size() == 0) return 0;
sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b){
return a[0] < b[0];
});
int ans = 1;
int end = points[0][1];
for(int i = 1; i < points.size(); i++){
if(points[i][0] > end){
end = points[i][1];
ans++;
}
else{
end = min(end, points[i][1]);
}
}
return ans;
}
};
20. Valid Parentheses
Solution: #Stack
class Solution {
public:
bool isValid(string s) {
stack<char> st;
for(char c : s){
if(c == '(' || c == '[' || c == '{') st.push(c);
else if(c == ')'){
if(!st.empty() && st.top() == '(') st.pop();
else return false;
}
else if(c == '}'){
if(!st.empty() && st.top() == '{') st.pop();
else return false;
}
else if(c == ']'){
if(!st.empty() && st.top() == '[') st.pop();
else return false;
}
}
return st.empty();
}
};
Solution: #Hash Table
class Solution {
public:
bool isValid(string s) {
stack<char> st;
unordered_map<char, char> mp = {{'(', ')'}, {'{', '}'}, {'[', ']'}};
for(char c : s){
if(mp.find(c) != mp.end()){
st.push(c);
}
else if(!st.empty() && mp[st.top()] == c){
st.pop();
}
else return false;
}
return st.empty();
}
};
71. Simplify Path
Solution: #Stack
class Solution {
public:
string simplifyPath(string path) {
stack<string> st;
int i = 0;
while(i < path.size()){
if(path[i] == '/'){
i++;
continue;
}
string word;
while(i < path.size() && path[i] != '/'){
word += path[i];
i++;
}
if(word == "."){
i++;
continue;
}
else if(word == ".."){
if(!st.empty()) st.pop();
i++;
continue;
}
else{
st.push(word);
i++;
}
}
string ans;
if(st.empty()) return "/";
while(!st.empty()){
ans = "/" + st.top() + ans;
st.pop();
}
return ans;
}
};
优化后的代码:
class Solution {
public:
string simplifyPath(string path) {
vector<string> st;
int i = 0, n = path.size();
while (i < n) {
while (i < n && path[i] == '/') i++; // 跳过 '/'
string word;
while (i < n && path[i] != '/') word += path[i++];
if (word == "." || word.empty()) continue;
if (word == "..") {
if (!st.empty()) st.pop_back();
} else {
st.push_back(word);
}
}
string ans = "/";
for (int i = 0; i < st.size(); i++) {
if (i > 0) ans += "/";
ans += st[i];
}
return ans;
}
};
155. Min Stack
Solution: #Simulation
class MinStack {
public:
vector<int> vec;
MinStack() {
vec.clear();
}
void push(int val) {
vec.push_back(val);
}
void pop() {
vec.pop_back();
}
int top() {
return vec.back();
}
int getMin() {
int minn = INT_MAX;
for(auto x : vec) minn = min(minn, x);
return minn;
}
};
优化后的代码:
class MinStack {
public:
stack<pair<int, int>> st; // 放入元素 : 当前栈的最小元素
MinStack() {
}
void push(int val) {
if(st.empty()) st.push({val, val});
else st.push({val, min(st.top().second, val)}); // 维护栈顶的最小值
}
void pop() {
st.pop();
}
int top() {
return st.top().first;
}
int getMin() {
return st.top().second;
}
};
150. Evaluate Reverse Polish Notation
Solution: #Stack
class Solution {
public:
stack<int> stNum;
int evalRPN(vector<string>& tokens) {
for(auto s : tokens){
if(s[0] >= '0' && s[0] <= '9'){
int num = stoi(s);
stNum.push(num);
}
else if(s[0] == '-' && s[1] >= '0' && s[1] <= '9'){
int num = stoi(s.substr(1));
stNum.push(-1 * num);
}
else{
int a, b;
if(!stNum.empty()){
b = stNum.top();
stNum.pop();
}
if(!stNum.empty()){
a = stNum.top();
stNum.pop();
}
if(s == "+") stNum.push(a + b);
else if(s == "-") stNum.push(a - b);
else if(s == "*") stNum.push(a * b);
else{
if(b != 0) stNum.push(a / b);
}
}
}
return stNum.top();
}
};
优化后的代码:
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(string c : tokens){
if(c == "+"|| c == "-" || c == "*" || c == "/"){
int nums2 = st.top(); st.pop();
int nums1 = st.top(); st.pop();
if (c == "+") st.push(nums1 + nums2);
else if (c == "-") st.push(nums1 - nums2);
else if (c == "*") st.push(nums1 * nums2);
else if (c == "/") st.push(nums1 / nums2);
}
else st.push(stoi(c));
}
return st.top();
}
};
224. Basic Calculator
Solution: #Stack, #Simulation
class Solution {
public:
int calculate(string s) {
int ans = 0; // 结果变量,存储当前计算的总和
int num = 0; // 记录当前解析的数字
int sign = 1; // 记录当前的符号,初始为正(1 代表正,-1 代表负)
stack<int> st; // 栈用于存储括号内的符号作用
st.push(sign); // 初始化栈,默认作用域符号为 1(正号)
// 遍历字符串中的每个字符
for(const char c : s){
// 如果是数字,构造当前数字(支持多位数)
if(isdigit(c)){
num = num * 10 + (c - '0');
}
// 遇到左括号 '(',表示进入新的作用域,将当前作用域的 sign 存入栈
else if(c == '(') {
st.push(sign);
}
// 遇到右括号 ')',表示离开当前作用域,弹出栈顶的 sign
else if(c == ')') {
st.pop();
}
// 遇到加号 '+' 或减号 '-',进行计算
else if(c == '+' || c == '-'){
ans += sign * num; // 先计算前面解析的数字并累加到 ans
sign = (c == '+' ? 1 : -1) * st.top(); // 更新当前符号,受括号作用域影响
num = 0; // 归零,准备解析下一个数字
}
}
// 处理最后一个数字
return ans + sign * num;
}
};
使用两个栈的方法:
class Solution {
public:
int calculate(string s) {
stack<int> numStack;
stack<int> opStack; // 1 代表 '+', -1 代表 '-'
int num = 0, sign = 1, ans = 0;
for (char c : s) {
if (isdigit(c)) {
num = num * 10 + (c - '0');
} else if (c == '+' || c == '-') {
ans += sign * num; // 计算当前数字
num = 0;
sign = (c == '+') ? 1 : -1;
} else if (c == '(') {
numStack.push(ans);
opStack.push(sign);
ans = 0;
sign = 1;
} else if (c == ')') {
ans += sign * num; // 计算括号内的最后一个数
num = 0;
ans *= opStack.top(); opStack.pop(); // 处理括号前的符号
ans += numStack.top(); numStack.pop(); // 叠加之前的计算结果
}
}
return ans + sign * num; // 计算最后一个数
}
};
141. Linked List Cycle
Solution: #Two Pointers
/**
* 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 == nullptr || head->next == nullptr) return false;
ListNode* slow = head;
ListNode* fast = head;
while(fast != nullptr && fast->next != nullptr){
fast = fast->next->next;
slow = slow->next;
if(fast == slow) return true;
}
return false;
}
};
Solution: #Hash Table
/**
* 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) {
unordered_set<ListNode*> st;
ListNode* cur = head;
while(cur != nullptr){
if(st.find(cur) != st.end()) return true;
st.insert(cur);
cur = cur->next;
}
return false;
}
};
2. Add Two Numbers
Solution: #Simulation
/**
* 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:
// 递归获取链表长度
int listLength(ListNode* head){
if(head == nullptr) return 0;
return 1 + listLength(head->next);
}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
int size1 = listLength(l1);
int size2 = listLength(l2);
if(size1 < size2) swap(l1, l2); // 保证l1是最长的链表;
ListNode* cur1 = l1;
ListNode* cur2 = l2;
int carry = 0;
int cursum;
ListNode* last = l1;
while(cur2 != nullptr){
cursum = cur1->val + cur2->val + carry;
cur1->val = cursum % 10;
carry = cursum / 10;
last = cur1;
cur1 = cur1->next;
cur2 = cur2->next;
}
while(cur1 != nullptr){
cursum = cur1->val + carry;
cur1->val = cursum % 10;
carry = cursum / 10;
last = cur1;
cur1 = cur1->next;
}
if(carry > 0) last->next = new ListNode(carry);
return l1;
}
};
优化后的方法:使用虚拟头节点
/**
* 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* dummy = new ListNode(0);
ListNode* p1 = l1;
ListNode* p2 = l2;
ListNode* cur = dummy;
int carry = 0;
while(p1 != nullptr || p2 != nullptr || carry != 0){
int x = (p1 != nullptr) ? p1->val : 0;
int y = (p2 != nullptr) ? p2->val : 0;
int sum = x + y + carry;
carry = sum / 10;
cur->next = new ListNode(sum % 10);
cur = cur->next;
if(p1 != nullptr) p1 = p1->next;
if(p2 != nullptr) p2 = p2->next;
}
return dummy->next;
}
};
21. Merge Two Sorted Lists
Solution: #Simulation
/**
* 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 = new ListNode(0);
ListNode* cur = dummy;
while(list1 != nullptr || list2 != nullptr){
if(list1 != nullptr && list2 != nullptr){
if(list1->val < list2->val){
cur->next = list1;
list1 = list1->next;
}
else{
cur->next = list2;
list2 = list2->next;
}
cur = cur->next;
}
else{
while(list1 != nullptr){
cur->next = list1;
list1 = list1->next;
cur = cur->next;
}
while(list2 != nullptr){
cur->next = list2;
list2 = list2->next;
cur = cur->next;
}
}
}
return dummy->next;
}
};
优化后的代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
while(list1 && list2){
if(list1->val < list2->val){
cur->next =list1;
list1 = list1->next;
}
else{
cur->next = list2;
list2 = list2->next;
}
cur = cur->next;
}
if(list1) cur->next = list1;
if(list2) cur->next = list2;
return dummy->next;
}
};
138. Copy List with Random Pointer
Solution: #Hash Table
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(!head) return nullptr;
unordered_map<Node*, Node*> mp; // 旧节点->新节点
Node* cur = head;
// 第一遍:创建所有新节点,并建立映射关系
while(cur){
mp[cur] = new Node(cur->val);
cur = cur->next;
}
// 第二遍:复制 next 和 random 指针
cur = head;
while(cur){
mp[cur]->next = mp[cur->next];
mp[cur]->random = mp[cur->random];
cur = cur->next;
}
return mp[head];
}
};
- 浅拷贝 (Shallow Copy):仅复制对象的指针,而不是内容。例如,如果
mp[cur] = cur
; 这样做,新的链表仍然会指向原来的 random 节点。 - 深拷贝 (Deep Copy):复制数据本身,确保新旧对象互不影响。本代码中的
new Node(cur->val)
就是深拷贝的关键。
优化后的代码:
class Solution {
public:
Node* copyRandomList(Node* head) {
if (!head) return nullptr;
// Step 1: 复制节点并插入到原链表
Node* cur = head;
while (cur) {
Node* tmp = new Node(cur->val);
tmp->next = cur->next;
cur->next = tmp;
cur = tmp->next;
}
// Step 2: 复制 random 指针
cur = head;
while (cur) {
if (cur->random) {
cur->next->random = cur->random->next;
}
cur = cur->next->next;
}
// Step 3: 拆分新旧链表
Node* newHead = head->next;
cur = head;
Node* copyCur = newHead;
while (cur) {
cur->next = cur->next->next;
if (copyCur->next) {
copyCur->next = copyCur->next->next;
}
cur = cur->next;
copyCur = copyCur->next;
}
return newHead;
}
};
92. Reverse Linked List II
Solution: #Stack
/**
* 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* reverseBetween(ListNode* head, int left, int right) {
if(!head || left == right) return head;
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* cur = dummy;
for(int i = 1; i < left; i++) cur = cur->next;
ListNode* pre = cur;
cur = cur->next;
stack<ListNode*> st;
for(int i = 0; i < right - left + 1; i++){
st.push(cur);
cur = cur->next;
}
while(!st.empty()){
pre->next = st.top();
pre = pre->next;
st.pop();
}
pre->next = cur;
return dummy->next;
}
};
优化后的方法,Solution:#Simulation
/**
* 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* reverseBetween(ListNode* head, int left, int right) {
if(!head || left == right) return head;
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* pre = dummy;
for(int i = 1; i < left; i++) pre = pre->next;
ListNode* cur = pre->next;
/*反转区间的节点*/
for(int i = 0; i < right - left; i++){
ListNode* tmp = cur->next;
cur->next = tmp->next;
tmp->next = pre->next;
pre->next = tmp;
}
return dummy->next;
}
};
25. Reverse Nodes in k-Group
Solution: #Stack
/**
* 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:
int getLength(ListNode* head){
if(head == nullptr) return 0;
return 1 + getLength(head->next);
}
ListNode* reverseKGroup(ListNode* head, int k) {
int n = getLength(head);
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
stack<ListNode*> st;
for(int i = 0; i + k <= n; i += k){
for(int j = 0; j < k; j++){
st.push(head);
head = head->next;
}
while(!st.empty()){
cur->next = st.top();
st.pop();
cur = cur->next;
}
}
cur->next = head;
return dummy->next;
}
};
Solution: #Recursion, #Two Pointers
/**
* 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* reverseKGroup(ListNode* head, int k) {
ListNode* cur = head;
for(int i = 0; i < k; i++){
if(!cur) return head; // 剩余节点不足 k 个,直接返回
cur = cur->next;
}
ListNode* pre = nullptr;
cur = head;
for(int i = 0; i < k; i++){
ListNode* nxt = cur->next;
cur->next = pre;
pre = cur;
cur = nxt;
}
head->next = reverseKGroup(cur, k); // 反转后,head 变成 k 组的最后一个节点
return pre;
}
};
与栈的方法类似,还可以使用双端队列deque
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
deque<ListNode*> dq;
ListNode dummy(0);
ListNode* cur = &dummy;
while (head) {
dq.push_back(head);
head = head->next;
if (dq.size() == k) {
while (!dq.empty()) {
cur->next = dq.back();
dq.pop_back();
cur = cur->next;
}
}
}
while (!dq.empty()) {
cur->next = dq.front();
dq.pop_front();
cur = cur->next;
}
cur->next = nullptr;
return dummy.next;
}
};
19. Remove Nth Node From End of List
Solution: #Simulation
/**
* 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:
int getLength(ListNode* head){
if(head == nullptr) return 0;
return 1 + getLength(head->next);
}
ListNode* removeNthFromEnd(ListNode* head, int n) {
int len = getLength(head);
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* cur = head;
ListNode* prev = dummy;
for(int i = 0; i < len - n; i++){
prev = prev->next;
cur = cur->next;
}
prev->next = cur->next;
delete cur;
return dummy->next;
}
};
优化后的方法:使用快慢指针 #Two Pointers
/**
* 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;
for(int i = 0; i <= n; i++) fast = fast->next;
while(fast != nullptr){
slow = slow->next;
fast = fast->next;
}
ListNode* toDelete = slow->next;
slow->next = slow->next->next;
delete toDelete;
ListNode* newHead = dummy->next;
delete dummy;
return newHead;
}
};
82. Remove Duplicates from Sorted List II
Solution: #Deque
/**
* 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* deleteDuplicates(ListNode* head) {
if(!head || !head->next) return head;
deque<ListNode*> que;
ListNode* cur = head;
while(cur){
if(que.empty()){
que.push_back(cur);
cur = cur->next;
}
else{
if(cur->val == que.back()->val){
while(cur && cur->val == que.back()->val) cur = cur->next;
que.pop_back();
}
else{
que.push_back(cur);
cur = cur->next;
}
}
}
ListNode* dummy = new ListNode(0);
cur = dummy;
while(!que.empty()){
cur->next = que.front();
que.pop_front();
cur = cur->next;
}
cur->next = nullptr; // 重塑指针,不然无法AC
return dummy->next;
}
};
更优化的方法:#Two Pointers
/**
* 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* deleteDuplicates(ListNode* head) {
ListNode* dummy = new ListNode(0, head);
ListNode* prev = dummy;
ListNode* cur = head;
while(cur){
bool isDuplicate = false;
while(cur->next && cur->val == cur->next->val){
isDuplicate = true;
cur = cur->next;
}
if(isDuplicate){
prev->next = cur->next;
}
else{
prev = cur;
}
cur = cur->next;
}
return dummy->next;
}
};
61. Rotate List
Solution: #Deque
/**
* 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:
int getLength(ListNode* head){
if(head == nullptr) return 0;
return 1 + getLength(head->next);
}
ListNode* rotateRight(ListNode* head, int k) {
if(!head || !head->next) return head;
int n = getLength(head);
k = k % n;
deque<ListNode*> que;
ListNode* cur = head;
while(cur){
que.push_back(cur);
cur = cur->next;
}
while(k--){
ListNode* tmp = que.back();
que.push_front(tmp);
que.pop_back();
}
ListNode* dummy = new ListNode(0);
cur = dummy;
while(!que.empty()){
cur->next = que.front();
cur = cur->next;
que.pop_front();
}
cur->next = nullptr;
return dummy->next;
}
};
优化的方法:计算长度 + 直接拼接
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(!head || !head->next || k == 0) return head;
int n = 1;
ListNode* tail = head;
while(tail->next){
tail = tail->next;
n++;
}
k = k % n;
if(k == 0) return head;
tail->next = head; // 让链表变成一个环
ListNode* newTail = head;
for(int i = 0; i < n - k - 1; i++){
newTail = newTail->next; // 找到链表新的断点
}
ListNode* newHead = newTail->next;
newTail->next = nullptr;
return newHead;
}
};
使用快慢指针,#Two Pointers
/**
* 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* rotateRight(ListNode* head, int k) {
if(!head || !head->next || k == 0) return head;
int n = 1;
ListNode* tail = head;
while(tail->next){
tail = tail->next;
n++;
}
k %= n;
if(k == 0) return head;
ListNode* fast = head; // 让fast先走k步
for(int i = 0; i < k; i++) fast = fast->next;
ListNode* slow = head; // slow和fast一起走
while(fast->next){
fast = fast->next;
slow = slow->next;
}
ListNode* newHead = slow->next;
slow->next = nullptr;
fast->next = head;
return newHead;
}
};
86. Partition List
Solution: #Queue
/**
* 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* partition(ListNode* head, int x) {
if(!head) return head;
queue<ListNode*> sml;
queue<ListNode*> big;
ListNode* cur = head;
while(cur){
if(cur->val < x) sml.push(cur);
else big.push(cur);
cur = cur->next;
}
ListNode* dummy = new ListNode(0);
cur = dummy;
while(!sml.empty()){
cur->next = sml.front();
cur = cur->next;
sml.pop();
}
while(!big.empty()){
cur->next = big.front();
cur = cur->next;
big.pop();
}
cur->next = nullptr;
return dummy->next;
}
};
优化后的方法:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode* dummy1 = new ListNode(0);
ListNode* dummy2 = new ListNode(0);
ListNode* ptr1 = dummy1;
ListNode* ptr2 = dummy2;
ListNode* cur = head;
while(cur){
if(cur->val < x){
ptr1->next = cur;
ptr1 = ptr1->next;
}
else{
ptr2->next = cur;
ptr2 = ptr2->next;
}
cur = cur->next;
}
ptr2->next = nullptr;
ptr1->next = dummy2->next;
return dummy1->next;
}
};
146. LRU Cache
Solution: #list
class LRUCache {
private:
int cap; // 缓存容量
list<int> lruList; // 维护访问顺序,最近访问的在前
unordered_map<int, pair<int, list<int>::iterator>> cache; // key -> {value, 在lruList中的位置}
public:
LRUCache(int capacity) {
cap = capacity;
}
int get(int key) {
if (cache.find(key) == cache.end()) return -1; // key 不存在
// key 存在,移动到链表头部(表示最近使用)
lruList.erase(cache[key].second); // 先删除原来的位置
lruList.push_front(key); // 移动到前面
cache[key].second = lruList.begin(); // 更新迭代器
return cache[key].first; // 返回值
}
void put(int key, int value) {
if (cache.find(key) != cache.end()) {
// key 已存在,更新值,并移动到前面
lruList.erase(cache[key].second);
lruList.push_front(key);
cache[key] = {value, lruList.begin()};
} else {
// key 不存在,检查是否超出容量
if (cache.size() == cap) {
int oldKey = lruList.back(); // 找到最久未使用的 key
lruList.pop_back(); // 移除链表最后一个元素
cache.erase(oldKey); // 从哈希表中删除
}
// 插入新 key
lruList.push_front(key);
cache[key] = {value, lruList.begin()};
}
}
};
104. Maximum Depth of Binary Tree
Solution: #Recursion
/**
* 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 1 + max(maxDepth(root->left), maxDepth(root->right));
}
};
Solution: #Iteration
/**
* 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;
queue<TreeNode*> que;
que.push(root);
int height = 0;
while(!que.empty()){
int n = que.size();
while(n--){
TreeNode* node = que.front();
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
height++;
}
return height;
}
};
100. Same Tree
Solution: #Recursion
/**
* 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 isSameTree(TreeNode* p, TreeNode* q) {
if(p == nullptr && q == nullptr) return true;
else if(p == nullptr || q == nullptr) return false;
else if(p -> val != q ->val) return false;
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
};
Solution: #Iteration
/**
* 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 isSameTree(TreeNode* p, TreeNode* q) {
queue<TreeNode*> que1;
queue<TreeNode*> que2;
que1.push(p);
que2.push(q);
while(!que1.empty() && !que2.empty()){
auto node1 = que1.front();
auto node2 = que2.front();
que1.pop();
que2.pop();
if(node1 == nullptr || node2 == nullptr){
if(node1 != node2) return false;
continue;
}
if(node1->val != node2->val) return false;
que1.push(node1->left);
que1.push(node1->right);
que2.push(node2->left);
que2.push(node2->right);
}
return true;
}
};
226. Invert Binary Tree
Solution: #Recursion
/**
* 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 root;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
Solution: #Iteration
/**
* 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) {
queue<TreeNode*> que;
if(root) que.push(root);
while(!que.empty()){
int n = que.size();
while(n--){
TreeNode* node = que.front();
que.pop();
swap(node->left, node->right);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return root;
}
};
101. Symmetric Tree
Solution: Recursion
/**
* 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 solve(TreeNode* l, TreeNode* r){
if(!l && !r) return true;
if(!l || !r) return false;
if(l->val != r->val) return false;
return solve(l->left, r->right) && solve(l->right, r->left);
}
bool isSymmetric(TreeNode* root) {
if(!root) return true;
return solve(root->left, root->right);
}
};
Solution: #Iteration
/**
* 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;
queue<TreeNode*> que;
que.push(root->left);
que.push(root->right);
while(!que.empty()){
TreeNode* node1 = que.front();
que.pop();
TreeNode* node2 = que.front();
que.pop();
if(!node1 && !node2) continue; // 都为空
if(!node1 || !node2) return false; // 只有一个为空
if(node1->val != node2->val) return false; // 值不相等
que.push(node1->left);
que.push(node2->right);
que.push(node1->right);
que.push(node2->left);
}
return true;
}
};
105. Construct Binary Tree from Preorder and Inorder Traversal
Solution: #Recursion
/**
* 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) {
if(preorder.empty()) return nullptr; // 递归边界条件
TreeNode* root = new TreeNode(preorder[0]);
if(preorder.size() == 1 && inorder.size() == 1) return root; // 边界条件
vector<int> inleft;
vector<int> inright;
bool over = false;
for(int i = 0; i < inorder.size(); i++){
if(inorder[i] == root->val){
over = true;
continue;
}
else if(!over) inleft.push_back(inorder[i]);
else inright.push_back(inorder[i]);
}
vector<int> preleft;
vector<int> preright;
int cnt = 0;
for(int i = 1; i < preorder.size(); i++){
if(cnt < inleft.size()){
preleft.push_back(preorder[i]);
cnt++;
}
else preright.push_back(preorder[i]);
}
root->left = buildTree(preleft, inleft);
root->right = buildTree(preright, inright);
return root;
}
};
使用哈希表优化的递归解法
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
unordered_map<int, int> inMap;
// 将中序遍历的值与下标存储在哈希表中
for (int i = 0; i < inorder.size(); i++) {
inMap[inorder[i]] = i;
}
return buildTreeHelper(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1, inMap);
}
TreeNode* buildTreeHelper(vector<int>& preorder, int preStart, int preEnd,
vector<int>& inorder, int inStart, int inEnd,
unordered_map<int, int>& inMap) {
if (preStart > preEnd || inStart > inEnd) {
return nullptr;
}
// 前序遍历的第一个节点是根节点
TreeNode* root = new TreeNode(preorder[preStart]);
// 在中序遍历中找到根节点的位置
int inRoot = inMap[root->val];
// 计算左子树的节点数量
int numsLeft = inRoot - inStart;
// 递归构建左子树和右子树
root->left = buildTreeHelper(preorder, preStart + 1, preStart + numsLeft,
inorder, inStart, inRoot - 1, inMap);
root->right = buildTreeHelper(preorder, preStart + numsLeft + 1, preEnd,
inorder, inRoot + 1, inEnd, inMap);
return root;
}
};
迭代解法(使用栈)
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.empty()) return nullptr;
TreeNode* root = new TreeNode(preorder[0]);
stack<TreeNode*> st;
st.push(root);
int inorderIndex = 0;
for (int i = 1; i < preorder.size(); i++) {
TreeNode* curr = st.top();
if (curr->val != inorder[inorderIndex]) {
// 如果当前栈顶节点不等于中序遍历的当前节点
// 说明当前前序遍历的节点是栈顶节点的左子节点
curr->left = new TreeNode(preorder[i]);
st.push(curr->left);
} else {
// 如果当前栈顶节点等于中序遍历的当前节点
// 说明需要回溯到合适的父节点,然后添加右子节点
while (!st.empty() && st.top()->val == inorder[inorderIndex]) {
curr = st.top();
st.pop();
inorderIndex++;
}
curr->right = new TreeNode(preorder[i]);
st.push(curr->right);
}
}
return root;
}
};
使用索引而不是子数组的递归解法
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return build(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
}
TreeNode* build(vector<int>& preorder, int preStart, int preEnd,
vector<int>& inorder, int inStart, int inEnd) {
if (preStart > preEnd) return nullptr;
// 前序遍历的第一个节点是根节点
int rootVal = preorder[preStart];
TreeNode* root = new TreeNode(rootVal);
// 在中序遍历中找到根节点的位置
int rootIndex = inStart;
while (inorder[rootIndex] != rootVal) rootIndex++;
// 左子树的节点数量
int leftSize = rootIndex - inStart;
// 构建左子树和右子树
root->left = build(preorder, preStart + 1, preStart + leftSize,
inorder, inStart, rootIndex - 1);
root->right = build(preorder, preStart + leftSize + 1, preEnd,
inorder, rootIndex + 1, inEnd);
return root;
}
};
106. Construct Binary Tree from Inorder and Postorder Traversal
Solution: #Recursion
/**
* 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>& inorder, vector<int>& postorder) {
if(inorder.empty()) return nullptr;
int n = inorder.size();
TreeNode* root = new TreeNode(postorder[n - 1]);
vector<int> inleft;
vector<int> inright;
bool findRoot = false;
unordered_set<int> ust;
for(int i = 0; i < n; i++){
if(!findRoot && inorder[i] != root->val){
inleft.push_back(inorder[i]);
ust.insert(inorder[i]);
}
else if(!findRoot) findRoot = true;
else inright.push_back(inorder[i]);
}
vector<int> posleft;
vector<int> posright;
for(int i = 0; i < n - 1; i++){
if(ust.find(postorder[i]) != ust.end()) posleft.push_back(postorder[i]);
else posright.push_back(postorder[i]);
}
root->left = buildTree(inleft, posleft);
root->right = buildTree(inright, posright);
return root;
}
};
简化的代码
/**
* 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* rec(vector<int>& inorder, vector<int>& postorder, int inStart, int inEnd, int postStart, int postEnd){
if(inStart > inEnd) return nullptr;
TreeNode* root = new TreeNode(postorder[postEnd]);
int index = inStart;
while(inorder[index] != root->val) index++; // 找到中序遍历左右子树划分位置
int leftLen = index - inStart; // 左子树长度
root->left = rec(inorder, postorder, inStart, index - 1, postStart, postStart + leftLen - 1);
root->right = rec(inorder, postorder, index + 1, inEnd, postStart + leftLen, postEnd - 1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
return rec(inorder, postorder, 0, inorder.size() - 1, 0, postorder.size() - 1);
}
};
Solution: #Iteration
- 入栈节点:表示当前树的右子树还没构建完。
- 出栈节点:表示右子树构建完成,需要转向左子树。
class Solution { public: TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) { if (inorder.empty()) return nullptr; // 哈希表存储 inorder 中每个值的索引,O(1) 查询 unordered_map<int, int> inMap; for (int i = 0; i < inorder.size(); i++) { inMap[inorder[i]] = i; } stack<TreeNode*> st; int postIndex = postorder.size() - 1; // 先创建根节点 TreeNode* root = new TreeNode(postorder[postIndex--]); st.push(root); for (int i = postIndex; i >= 0; i--) { TreeNode* node = new TreeNode(postorder[i]); TreeNode* parent = nullptr; // 判断是否需要出栈(找到正确的父节点) while (!st.empty() && inMap[st.top()->val] > inMap[node->val]) { parent = st.top(); st.pop(); } // 根据 parent 是否存在决定插入左还是右子树 if (parent) { parent->left = node; } else { st.top()->right = node; } st.push(node); } return root; } };
117. Populating Next Right Pointers in Each Node II
Solution: #Queue
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
if(!root) return nullptr;
queue<Node*> que;
que.push(root);
while(!que.empty()){
int n = que.size();
int cnt = 0;
while(n--){
Node* node = que.front();
que.pop();
if(node->left){
que.push(node->left);
cnt++;
}
if(node->right){
que.push(node->right);
cnt++;
}
}
while(cnt > 0){
Node* cur = que.front();
que.pop();
que.push(cur);
if(cnt == 1) break;
cur->next = que.front();
cnt--;
}
}
return root;
}
};
Solution:#Recursion #DFS
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
void DFS(Node* root, vector<Node*>& collect, int level){
if(!root) return;
if(collect.size() == level) collect.push_back(root);
else{
collect[level]->next = root;
collect[level] = root;
}
DFS(root->left, collect, level + 1);
DFS(root->right, collect, level + 1);
}
Node* connect(Node* root) {
vector<Node*> collect;
DFS(root, collect, 0);
return root;
}
};
Solution: #Pre Pointer
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
if(!root) return nullptr;
queue<Node*> que;
que.push(root);
while(!que.empty()){
int n = que.size();
Node* prev = nullptr; // 每一层的遍历设置一个pre指针
for(int i = 0; i < n; i++){
Node* cur = que.front();
que.pop();
if(prev){
prev->next = cur;
}
prev = cur;
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
}
return root;
}
};
114. Flatten Binary Tree to Linked List
Solution: #Recursion #preOrder
/**
* 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* root, vector<TreeNode*>& vec){
if(!root) return;
vec.push_back(root);
preOrder(root->left, vec);
preOrder(root->right, vec);
}
void flatten(TreeNode* root) {
vector<TreeNode*> vec;
preOrder(root, vec);
TreeNode* dummy = new TreeNode(0);
TreeNode* cur = dummy;
for(auto x : vec){
x->left = nullptr;
cur->right = x;
cur = cur->right;
}
}
};
优化后的方法:#Recursion
/**
* 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 flatten(TreeNode* root) {
if(!root) return;
flatten(root->right);
flatten(root->left);
TreeNode* tmp = root->right;
root->right = root->left;
root->left = nullptr;
while(root->right) root = root->right;
root->right = tmp;
}
};
Solution: #Iteration
/**
* 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 flatten(TreeNode* root) {
if(!root) return;
TreeNode* cur = root;
while(cur){
if(cur->left){
TreeNode* rightmost = cur->left;
while(rightmost->right) rightmost = rightmost->right;
rightmost->right = cur->right;
cur->right = cur->left;
cur->left = nullptr;
}
cur = cur->right;
}
}
};
112. Path Sum
Solution: #Recursion
/**
* 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 hasPathSum(TreeNode* root, int targetSum) {
if(!root) return false;
if(!root->left && !root->right && root->val == targetSum) return true;
return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
}
};
Solution: #Iteration
/**
* 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 hasPathSum(TreeNode* root, int targetSum) {
if(root == nullptr) return false;
queue<pair<TreeNode*, int>> que;
que.push({root, root->val});
while(!que.empty()){
TreeNode* node = que.front().first;
int currSum = que.front().second;
que.pop();
if(!node->left && !node->right && currSum == targetSum) return true;
if(node->left) que.push({node->left, currSum + node->left->val});
if(node->right) que.push({node->right, currSum + node->right->val});
}
return false;
}
};
129. Sum Root to Leaf Numbers
Solution: #Iteration
/**
* 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 sumNumbers(TreeNode* root) {
if(!root) return 0;
int ans = 0;
queue<pair<TreeNode*, int>> que;
que.push({root, root->val});
while(!que.empty()){
TreeNode* node = que.front().first;
int curSum = que.front().second;
que.pop();
if(!node->left && !node->right) ans += curSum;
if(node->left) que.push({node->left, node->left->val + curSum * 10});
if(node->right) que.push({node->right, node->right->val + curSum * 10});
}
return ans;
}
};
Solution: #Recursion
/**
* 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 solve(TreeNode* root, int sum, vector<int>& nums){
if(!root) return;
sum = sum * 10 + root->val;
if(!root->left && !root->right) nums.push_back(sum); // 叶子节点
solve(root->left, sum, nums);
solve(root->right, sum, nums);
}
int sumNumbers(TreeNode* root) {
int ans = 0;
vector<int> nums;
solve(root, 0, nums);
for(auto x : nums) ans += x;
return ans;
}
};
124. Binary Tree Maximum Path Sum
Solution: #Recursion
/**
* 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 solve(TreeNode* node, int& maxSum){
if(!node) return 0;
int left = max(solve(node->left, maxSum), 0);
int right = max(solve(node->right, maxSum), 0);
int pathSum = left + right + node->val;
maxSum = max(maxSum, pathSum);
return node->val + max(left, right);
}
int maxPathSum(TreeNode* root) {
int maxSum = INT_MIN;
solve(root, maxSum);
return maxSum;
}
};
优化后的代码:
class Solution {
public:
unordered_map<TreeNode*, int> dp; // 记忆化存储最大路径和
int solve(TreeNode* node, int& maxSum) {
if (!node) return 0;
if (dp.count(node)) return dp[node]; // 记忆化
int left = max(solve(node->left, maxSum), 0);
int right = max(solve(node->right, maxSum), 0);
int pathSum = left + right + node->val;
maxSum = max(maxSum, pathSum);
return dp[node] = node->val + max(left, right);
}
int maxPathSum(TreeNode* root) {
int maxSum = INT_MIN;
solve(root, maxSum);
return maxSum;
}
};
173. Binary Search Tree Iterator
Solution: #中序遍历
class BSTIterator {
public:
vector<int> nums;
int index = 0;
void inorder(TreeNode* root) {
if (!root) return;
inorder(root->left);
nums.push_back(root->val);
inorder(root->right);
}
BSTIterator(TreeNode* root) {
inorder(root);
}
int next() {
return nums[index++];
}
bool hasNext() {
return index < nums.size();
}
};
Solution: #Stack
class BSTIterator {
public:
stack<TreeNode*> st;
BSTIterator(TreeNode* root) {
pushLeft(root);
}
int next() {
TreeNode* node = st.top();
st.pop();
pushLeft(node->right);
return node->val;
}
bool hasNext() {
return !st.empty();
}
private:
void pushLeft(TreeNode* node){
while(node){
st.push(node);
node = node->left;
}
}
};
222. Count Complete Tree Nodes
Solution: #Recursion
/**
* 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 countNodes(TreeNode* root) {
if(!root) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
};
Solution: #Iteration
class Solution {
public:
int countNodes(TreeNode* root) {
if(!root) return 0;
int res = 0;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
int n = que.size();
while(n--){
TreeNode* cur = que.front();
que.pop();
res++;
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
}
return res;
}
};
236. Lowest Common Ancestor of a Binary Tree
- 递归遍历二叉树,分别在 左子树 和 右子树 查找 p 和 q。
- 如果 p 和 q 分别出现在左右子树,则 root 是最近公共祖先。
- 如果 p 和 q 都出现在某一侧,就往那一侧递归。
- 如果当前 root == p 或 root == q,直接返回 root。
/**
* 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 == nullptr || root == p || root == q) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if(left == nullptr) return right;
if(right == nullptr) return left;
return root;
}
};
Solution: #Iteration
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
unordered_map<TreeNode*, TreeNode*> parent;
stack<TreeNode*> stk;
parent[root] = nullptr;
stk.push(root);
// 遍历整棵树,记录每个节点的父节点
while (parent.find(p) == parent.end() || parent.find(q) == parent.end()) {
TreeNode* node = stk.top(); stk.pop();
if (node->left) {
parent[node->left] = node;
stk.push(node->left);
}
if (node->right) {
parent[node->right] = node;
stk.push(node->right);
}
}
// 记录 p 的所有祖先
unordered_set<TreeNode*> ancestors;
while (p) {
ancestors.insert(p);
p = parent[p];
}
// 找到 q 的第一个公共祖先
while (!ancestors.count(q)) {
q = parent[q];
}
return q;
}
};
199. Binary Tree Right Side View
Solution: #Iteration
/**
* 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) {
if(!root) return {};
vector<int> ans;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
int n = que.size();
while(n--){
TreeNode* node = que.front();
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
if(n == 0) ans.push_back(node->val);
}
}
return ans;
}
};
Solution: #Recursion
/**
* 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 dfs(TreeNode* root, int depth, vector<int>& ans){
if(!root) return;
if(depth == ans.size()){
ans.push_back(root->val);
}
dfs(root->right, depth + 1, ans); // 保证右子树先被访问
dfs(root->left, depth + 1, ans);
}
vector<int> rightSideView(TreeNode* root) {
vector<int> ans;
dfs(root, 0, ans);
return ans;
}
};
637. Average of Levels in Binary Tree
Solution: #Iteration
/**
* 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<double> averageOfLevels(TreeNode* root) {
if(!root) return {};
vector<double> ans;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
int n = que.size();
int num = que.size();
double sum = 0;
while(n--){
TreeNode* node = que.front();
que.pop();
sum += node->val;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
ans.push_back(sum / (num * 1.0));
}
return ans;
}
};
Solution: #Recursion
/**
* 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 dfs(TreeNode* node, int level, vector<double>& sum, vector<int>& count){
if(!node) return;
// sum 和 count 记录每一层的信息,初始为空。
if(sum.size() <= level){
sum.push_back(0);
count.push_back(0);
}
sum[level] += node->val;
count[level] += 1;
dfs(node->left, level + 1, sum, count);
dfs(node->right, level + 1, sum, count);
}
vector<double> averageOfLevels(TreeNode* root) {
vector<double> sum;
vector<int> count;
dfs(root, 0, sum, count);
vector<double> res;
for(int i = 0; i < sum.size(); i++){
res.push_back(sum[i] / count[i]);
}
return res;
}
};
102. Binary Tree Level Order Traversal
Solution: #Iteration
/**
* 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>> ans;
if(!root) return ans;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
int n = que.size();
vector<int> tmp;
while(n--){
TreeNode* node = que.front();
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
tmp.push_back(node->val);
}
ans.push_back(tmp);
}
return ans;
}
};
Solution: #Recursion
/**
* 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 dfs(TreeNode* node, int level, vector<vector<int>>& ans){
if(!node) return;
if(level >= ans.size()){
ans.push_back({});
}
ans[level].push_back(node->val);
dfs(node->left, level + 1, ans);
dfs(node->right, level + 1, ans);
}
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;
dfs(root, 0, ans);
return ans;
}
};
103. Binary Tree Zigzag Level Order Traversal
Solution: #Iteration
/**
* 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>> ans;
if(!root) return ans;
queue<TreeNode*> que;
que.push(root);
int level = 0;
while(!que.empty()){
level++;
int n = que.size();
vector<int> tmp;
while(n--){
TreeNode* node = que.front();
que.pop();
tmp.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
if(level % 2 == 0) reverse(tmp.begin(), tmp.end());
ans.push_back(tmp);
}
return ans;
}
};
Solution: #Recursion
/**
* 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 dfs(TreeNode* node, int level, vector<vector<int>>& ans){
if(!node) return;
if(level >= ans.size()) ans.push_back({});
if(level % 2 == 0) ans[level].push_back(node->val);
else ans[level].insert(ans[level].begin(), node->val); // 从右到左
dfs(node->left, level + 1, ans);
dfs(node->right, level + 1, ans);
}
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> ans;
dfs(root, 0, ans);
return ans;
}
};
530. Minimum Absolute Difference in BST
Solution: 笨办法#Brute Force
/**
* 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 getMinimumDifference(TreeNode* root) {
if(!root) return 0;
queue<TreeNode*> que;
que.push(root);
vector<int> nums;
while(!que.empty()){
int n = que.size();
while(n--){
TreeNode* node = que.front();
que.pop();
nums.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
sort(nums.begin(), nums.end());
int gap = INT_MAX;
for(int i = 1; i < nums.size(); i++) gap = min(gap, nums[i] - nums[i - 1]);
return gap;
}
};
Solution: BST的中序遍历可以得到一个递增序列。
/**
* 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* root, vector<int>& nums){
if(!root) return;
inorder(root->left, nums);
nums.push_back(root->val);
inorder(root->right, nums);
}
int getMinimumDifference(TreeNode* root) {
vector<int> nums;
inorder(root, nums);
int gap = INT_MAX;
for(int i = 1; i < nums.size(); i++){
gap = min(gap, nums[i] - nums[i - 1]);
}
return gap;
}
};
230. Kth Smallest Element in a BST
Solution: #Inorder
/**
* 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* root, vector<int>& nums){
if(!root) return;
inorder(root->left, nums);
nums.push_back(root->val);
inorder(root->right, nums);
}
int kthSmallest(TreeNode* root, int k) {
vector<int> nums;
inorder(root, nums);
return nums[k - 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 count = 0;
int ans = 0;
void inorder(TreeNode* root, int k){
if(!root) return;
inorder(root->left, k);
count++;
if(count == k){
ans = root->val;
return;
}
inorder(root->right, k);
}
int kthSmallest(TreeNode* root, int k) {
inorder(root, k);
return ans;
}
};
98. Validate Binary Search Tree
Solution: #Recursion
/**
* 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* root, vector<int>& nums){
if(!root) return;
inorder(root->left, nums);
nums.push_back(root->val);
inorder(root->right, nums);
}
bool isValidBST(TreeNode* root) {
vector<int> nums;
inorder(root, nums);
int pre = nums[0];
for(int i = 1; i < nums.size(); i++){
if(nums[i] <= pre) return false;
pre = nums[i];
}
return true;
}
};
优化后的方法:不用完全遍历且不用额外空间
/**
* 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* prev = nullptr;
bool inorder(TreeNode* root){
if(!root) return true;
if(!inorder(root->left)) return false; // 左子树不满足BST
if(prev && root->val <= prev->val) return false;
prev = root;
return inorder(root->right);
}
bool isValidBST(TreeNode* root) {
return inorder(root);
}
};
200. Number of Islands
Solution: #BFS
class Solution {
public:
int ox[4] = {-1, 0, 1, 0};
int oy[4] = {0, 1, 0, -1};
bool isValid(int x, int y, int m, int n, vector<vector<char>>& grid){
return x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1'; // 合法的'1'
}
void bfs(int x, int y, int m, int n, vector<vector<char>>& grid){
if(!isValid(x, y, m, n, grid)) return;
grid[x][y] = '0';
for(int i = 0; i < 4; i++){
int dx = x + ox[i];
int dy = y + oy[i];
bfs(dx, dy, m, n, grid);
}
}
int numIslands(vector<vector<char>>& grid) {
int cnt = 0;
int m = grid.size();
int n = grid[0].size();
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == '1') {
bfs(i, j, m, n, grid);
cnt++;
}
}
}
return cnt;
}
};
Solution: #UnionSet
class Solution {
public:
int find(int x, vector<int>& parent){
if(parent[x] != x){
parent[x] = find(parent[x], parent); // 路径压缩
}
return parent[x];
}
void unionSets(int x, int y, vector<int>& parent, vector<int>& rank){
int rootX = find(x, parent);
int rootY = find(y, parent);
if(rootX != rootY){
if(rank[rootX] > rank[rootY]) parent[rootY] = rootX; // 合并小树到大树
else if(rank[rootX] < rank[rootY]) parent[rootX] = rootY;
else{
parent[rootY] = rootX;
rank[rootX]++;
}
}
}
int numIslands(vector<vector<char>>& grid) {
int m = grid.size();
int n = grid[0].size();
if(m == 0 || n == 0) return 0;
vector<int> parent(m * n); // 并查集父节点数组
vector<int> rank(m * n, 0); // 记录树的高度
int directions[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
for(int i = 0; i < m * n; i++) parent[i] = i; // 初始化每个位置的父节点
int numIslands = 0;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == '1'){
int cur = i * n + j;
for(auto& dir : directions){
int ni = i + dir[0];
int nj = j + dir[1];
if(ni >= 0 && ni < m && nj >= 0 && nj < n && grid[ni][nj] == '1'){
int next = ni * n + nj;
unionSets(cur, next, parent, rank);
}
}
}
}
}
// 统计岛屿的数量,判断每个节点的根节点是否唯一
unordered_set<int> islandRoots;
for(int i = 0; i < m * n; i++){
if(grid[i / n][i % n] == '1') islandRoots.insert(find(i, parent));
}
return islandRoots.size();
}
};
130. Surrounded Regions
- 从边界上的
O
开始,用 BFS 标记所有能连到边界的O
(换成#
)。 - 遍历整个网格,把所有未标记的
O
(被包围的)变成X
。 - 恢复标记的
#
变回O
(这些是原本连接到边界的O
)。
class Solution {
public:
void bfs(vector<vector<char>>& board, int x, int y){
int m = board.size(); // line
int n = board[0].size(); // col
if(x < 0 || x >= m || y < 0 || y >= n) return; // 超出界限
if(board[x][y] == 'O'){
board[x][y] = '#';
queue<pair<int, int>> que;
que.push({x, y});
while(!que.empty()){
int i = que.front().first;
int j = que.front().second;
que.pop();
if(i - 1 >= 0 && board[i - 1][j] == 'O'){
board[i - 1][j] = '#';
que.push({i - 1, j});
}
if(i + 1 < m && board[i + 1][j] == 'O'){
board[i + 1][j] = '#';
que.push({i + 1, j});
}
if(j - 1 >= 0 && board[i][j - 1] == 'O'){
board[i][j - 1] = '#';
que.push({i, j - 1});
}
if(j + 1 < n && board[i][j + 1] == 'O'){
board[i][j + 1] = '#';
que.push({i, j + 1});
}
}
}
}
void solve(vector<vector<char>>& board) {
int m = board.size();
if(m == 0) return;
int n = board[0].size();
for(int i = 0; i < m; i++){
bfs(board, i, 0);
bfs(board, i, n - 1);
}
for(int j = 0; j < n; j++){
bfs(board, 0, j);
bfs(board, m - 1, j);
}
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(board[i][j] == 'O') board[i][j] = 'X';
else if(board[i][j] == '#') board[i][j] = 'O';
}
}
}
};
Solution: #DFS
class Solution {
public:
void dfs(vector<vector<char>>& board, int x, int y) {
int m = board.size(), n = board[0].size();
if (x < 0 || x >= m || y < 0 || y >= n || board[x][y] != 'O') return;
board[x][y] = '#'; // 先标记
dfs(board, x - 1, y); // 上
dfs(board, x + 1, y); // 下
dfs(board, x, y - 1); // 左
dfs(board, x, y + 1); // 右
}
void solve(vector<vector<char>>& board) {
int m = board.size();
if (m == 0) return;
int n = board[0].size();
// 处理边界
for (int i = 0; i < m; i++) {
dfs(board, i, 0); // 第一列
dfs(board, i, n - 1); // 最后一列
}
for (int j = 0; j < n; j++) {
dfs(board, 0, j); // 第一行
dfs(board, m - 1, j); // 最后一行
}
// 遍历整个矩阵,修改值
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == 'O') board[i][j] = 'X';
else if (board[i][j] == '#') board[i][j] = 'O';
}
}
}
};
Solution: #Union-Find
class UnionFind {
public:
vector<int> parent;
vector<int> rank;
UnionFind(int n) {
parent.resize(n);
rank.resize(n, 0);
for (int i = 0; i < n; i++) parent[i] = i;
}
int find(int x) {
if (parent[x] != x)
parent[x] = find(parent[x]); // 路径压缩
return parent[x];
}
void unite(int x, int y) {
int rootX = find(x), rootY = find(y);
if (rootX != rootY) {
if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX;
} else if (rank[rootX] < rank[rootY]) {
parent[rootX] = rootY;
} else {
parent[rootY] = rootX;
rank[rootX]++;
}
}
}
bool connected(int x, int y) {
return find(x) == find(y);
}
};
class Solution {
public:
void solve(vector<vector<char>>& board) {
int m = board.size();
if (m == 0) return;
int n = board[0].size();
int dummy = m * n; // 虚拟节点
UnionFind uf(dummy + 1);
// 遍历整个矩阵,处理边界上的 'O'
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == 'O') {
int index = i * n + j;
if (i == 0 || i == m - 1 || j == 0 || j == n - 1) {
// 边界上的 'O' 连接到 dummy
uf.unite(index, dummy);
} else {
// 合并相邻的 'O'
if (i > 0 && board[i - 1][j] == 'O') uf.unite(index, (i - 1) * n + j);
if (i < m - 1 && board[i + 1][j] == 'O') uf.unite(index, (i + 1) * n + j);
if (j > 0 && board[i][j - 1] == 'O') uf.unite(index, i * n + (j - 1));
if (j < n - 1 && board[i][j + 1] == 'O') uf.unite(index, i * n + (j + 1));
}
}
}
}
// 遍历整个矩阵,修改值
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == 'O') {
int index = i * n + j;
if (!uf.connected(index, dummy)) {
board[i][j] = 'X';
}
}
}
}
}
};
133. Clone Graph
Solution: #Hash Table
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> neighbors;
Node() {
val = 0;
neighbors = vector<Node*>();
}
Node(int _val) {
val = _val;
neighbors = vector<Node*>();
}
Node(int _val, vector<Node*> _neighbors) {
val = _val;
neighbors = _neighbors;
}
};
*/
class Solution {
public:
unordered_map<Node*, Node*> copies;
Node* cloneGraph(Node* node) {
if(!node) return nullptr;
Node* copy = new Node(node->val);
copies[node] = copy;
queue<Node*> que;
que.push(node);
while(!que.empty()){
Node* cur = que.front();
que.pop();
for(Node* neighbor : cur->neighbors){
if(copies.find(neighbor) == copies.end()){
copies[neighbor] = new Node(neighbor->val);
que.push(neighbor);
}
copies[cur]->neighbors.push_back(copies[neighbor]);
}
}
return copy;
}
};
399. Evaluate Division
Solution: #Union-Find
class Solution {
public:
unordered_map<string, string> parent;
unordered_map<string, double> weight;
string find(string x){
if(parent.find(x) == parent.end()){
parent[x] = x;
weight[x] = 1.0;
}
if(parent[x] != x){
string origin = parent[x];
parent[x] = find(parent[x]); // 路径压缩
weight[x] *= weight[origin]; // 更新路径中的比值
}
return parent[x];
}
void unionSets(string x, string y, double value){
string rootX = find(x);
string rootY = find(y);
if(rootX != rootY){
parent[rootX] = rootY;
weight[rootX] = value * weight[y] / weight[x];
}
}
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
for(int i = 0; i < equations.size(); i++){
string a = equations[i][0], b = equations[i][1];
double value = values[i];
unionSets(a, b, value);
}
vector<double> result;
for(auto& query : queries){
string a = query[0], b = query[1];
if(parent.find(a) == parent.end() || parent.find(b) == parent.end() || find(a) != find(b)){
result.push_back(-1.0);
}
else result.push_back(weight[a] / weight[b]);
}
return result;
}
};
Solution:构图 + BFS
class Solution {
public:
unordered_map<string, vector<pair<string, double>>> graph;
double bfs(string start, string end) {
if(graph.find(start) == graph.end() || graph.find(end) == graph.end()) return -1.0;
queue<pair<string, double>> q;
unordered_set<string> visited;
q.push({start, 1.0});
visited.insert(start);
while(!q.empty()) {
auto [curr, val] = q.front();
q.pop();
if(curr == end) return val; // 找到目标
for(auto& neighbor : graph[curr]) {
string nextNode = neighbor.first;
double weight = neighbor.second;
if(visited.count(nextNode)) continue; // 避免重复访问
visited.insert(nextNode);
q.push({nextNode, val * weight}); // 更新累计权重
}
}
return -1.0;
}
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
// 1. 构造图
for(int i = 0; i < equations.size(); i++) {
string a = equations[i][0], b = equations[i][1];
double value = values[i];
graph[a].push_back({b, value});
graph[b].push_back({a, 1.0 / value});
}
// 2. 处理查询
vector<double> result;
for(auto& query : queries) {
result.push_back(bfs(query[0], query[1]));
}
return result;
}
};
207. Course Schedule
Solution: #BFS
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> graph(numCourses); // 邻接表
vector<int> indegree(numCourses, 0); // 入度表
for(auto p : prerequisites){
int a = p[0], b = p[1];
graph[b].push_back(a); // b -> a
indegree[a]++;
}
queue<int> q;
for(int i = 0; i < numCourses; i++){
if(indegree[i] == 0) q.push(i); // 入度为0的可以先学
}
int finished = 0;
while(!q.empty()){
int course = q.front();
q.pop();
finished++;
for(int neighbor : graph[course]){
indegree[neighbor]--;
if(indegree[neighbor] == 0) q.push(neighbor);
}
}
return finished == numCourses;
}
};
210. Course Schedule II
Solution: #BFS
class Solution {
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> graph(numCourses); // 邻接表
vector<int> indegree(numCourses, 0); // 入度表
for(auto p : prerequisites){
int a = p[0], b = p[1];
graph[b].push_back(a); // b -> a
indegree[a]++;
}
queue<int> q;
vector<int> ans;
for(int i = 0; i < numCourses; i++){
if(indegree[i] == 0){
q.push(i);
ans.push_back(i);
}
}
while(!q.empty()){
int course = q.front();
q.pop();
for(int neighbor : graph[course]){
indegree[neighbor]--;
if(indegree[neighbor] == 0){
q.push(neighbor);
ans.push_back(neighbor);
}
}
}
if(ans.size() == numCourses) return ans;
else return {};
}
};
909. Snakes and Ladders
Solution: #BFS
class Solution {
public:
int snakesAndLadders(vector<vector<int>>& board) {
int n = board.size();
vector<int> flat(n * n + 1, -1); // 1-based indexing
int idx = 1;
bool leftToRight = true;
for(int i = n - 1; i >= 0; i--){
if(leftToRight){
for(int j = 0; j < n; j++) flat[idx++] = board[i][j];
}
else{
for(int j = n - 1; j >= 0; j--) flat[idx++] = board[i][j];
}
leftToRight = !leftToRight; // 换遍历方向
}
queue<int> que;
vector<bool> visited(n * n + 1, false);
que.push(1);
visited[1] = true;
int steps = 0;
while(!que.empty()){
int sz = que.size();
while(sz--){
int cur = que.front();
que.pop();
if(cur == n * n) return steps;
for(int move = 1; move <= 6 && cur + move <= n * n; move++){
int next = cur + move;
if (flat[next] != -1) next = flat[next];
if(!visited[next]){
visited[next] = true;
que.push(next);
}
}
}
steps++;
}
return -1;
}
};
433. Minimum Genetic Mutation
Solution:#BFS
class Solution {
public:
int minMutation(string startGene, string endGene, vector<string>& bank) {
unordered_set<string> ust(bank.begin(), bank.end());
if(ust.find(endGene) == ust.end()) return -1; // 目标基因不在bank中
unordered_set<string> visited; // 用来记录已访问的基因
queue<string> que;
que.push(startGene);
visited.insert(startGene);
int mutations = 0; // 记录变异次数
string genes = "ACGT";
while(!que.empty()){
int size = que.size();
for(int i = 0; i < size; i++){
string curr = que.front();
que.pop();
if(curr == endGene) return mutations;
for(int j = 0; j < 8; j++){
for(char c : genes){
if(curr[j] != c){
string newGene = curr;
newGene[j] = c;
if(ust.find(newGene) != ust.end() && visited.find(newGene) == visited.end()){
visited.insert(newGene);
que.push(newGene);
}
}
}
}
}
mutations++;
}
return -1;
}
};
127. Word Ladder
Solution: #BFS,暴力枚举可能出现的字段
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> wordSet(wordList.begin(), wordList.end());
if(wordSet.find(endWord) == wordSet.end()) return 0; // 终点不在wordList中
unordered_set<string> visited;
queue<string> que;
que.push(beginWord);
visited.insert(beginWord);
int steps = 1;
while(!que.empty()){
int size = que.size();
for(int i = 0; i < size; i++){
string cur = que.front();
que.pop();
if(cur == endWord) return steps;
for(int j = 0; j < cur.size(); j++){
char original = cur[j];
for(char c = 'a'; c <= 'z'; c++){
if(c == original) continue;
cur[j] = c;
if(wordSet.find(cur) != wordSet.end() && visited.find(cur) == visited.end()){
visited.insert(cur);
que.push(cur);
}
}
cur[j] = original; // 还原
}
}
steps++;
}
return 0;
}
};
208. Implement Trie (Prefix Tree)
Solution: 暴力映射找前缀
class Trie {
public:
unordered_set<string> ust;
Trie() {
ust.clear();
}
void insert(string word) {
ust.insert(word);
}
bool search(string word) {
return !(ust.find(word) == ust.end());
}
bool startsWith(string prefix) {
int n = prefix.size();
for(auto str : ust){
if(str.substr(0, n) == prefix) return true;
}
return false;
}
};
/**
* 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);
*/
Solution: #BFS
class TrieNode {
public:
unordered_map<char, TrieNode*> children; // 存储子节点
bool is_end_of_word; // 标记是否为单词的结尾
TrieNode() : is_end_of_word(false) {}
};
class Trie {
public:
TrieNode* root; // 根节点
Trie() {
root = new TrieNode(); // 初始化根节点
}
// 插入单词
void insert(string word) {
TrieNode* node = root;
for (char c : word) {
if (node->children.find(c) == node->children.end()) {
node->children[c] = new TrieNode();
}
node = node->children[c];
}
node->is_end_of_word = true; // 标记该节点为单词的结尾
}
// 搜索单词
bool search(string word) {
TrieNode* node = root;
for (char c : word) {
if (node->children.find(c) == node->children.end()) {
return false; // 如果某个字符不存在,返回 false
}
node = node->children[c];
}
return node->is_end_of_word; // 如果到达的节点是单词的结尾,则返回 true
}
// 判断是否有单词以 prefix 开头
bool startsWith(string prefix) {
TrieNode* node = root;
for (char c : prefix) {
if (node->children.find(c) == node->children.end()) {
return false; // 如果某个字符不存在,返回 false
}
node = node->children[c];
}
return true; // 如果遍历完 prefix 的所有字符,返回 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);
*/
🖼️ 图示(插入 “apple” 和 “app” 之后)
(root)
|
a
|
p
|
p* ← app 的结尾(is_end_of_word = true)
|
l
|
e* ← apple 的结尾(is_end_of_word = true)
211. Design Add and Search Words Data Structure
Solution: #Trie
class WordDictionary {
private:
struct TrieNode {
TrieNode* children[26] = {nullptr};
bool isEnd = false;
};
TrieNode* root;
public:
WordDictionary() {
root = new TrieNode();
}
void addWord(string word) {
TrieNode* node = root;
for (char c : word) {
int idx = c - 'a';
if (!node->children[idx])
node->children[idx] = new TrieNode();
node = node->children[idx];
}
node->isEnd = true;
}
bool search(string word) {
return searchHelper(word, 0, root);
}
private:
bool searchHelper(const string& word, int index, TrieNode* node) {
if (!node) return false;
if (index == word.size())
return node->isEnd;
char c = word[index];
if (c == '.') {
// Try all possible children
for (int i = 0; i < 26; ++i) {
if (node->children[i] && searchHelper(word, index + 1, node->children[i]))
return true;
}
return false;
} else {
int idx = c - 'a';
return searchHelper(word, index + 1, node->children[idx]);
}
}
};
字典树🌲插入方式如下
(root)
├── b
│ └── a
│ └── d*
├── d
│ └── a
│ └── d*
└── m
└── a
└── d*
212. Word Search II
Solution: #Trie #BFS #Recursion
(root)
├── o
│ └── a
│ └── t
│ └── h [word: "oath"]
├── p
│ └── e
│ └── a [word: "pea"]
├── e
│ └── a
│ └── t [word: "eat"]
└── r
└── a
└── i
└── n [word: "rain"]
class Solution {
public:
struct TrieNode {
unordered_map<char, TrieNode*> children;
string word = "";
};
TrieNode* buildTrie(const vector<string>& words) {
TrieNode* root = new TrieNode();
for (const string& word : words) {
TrieNode* node = root;
for (char c : word) {
if (!node->children.count(c)) {
node->children[c] = new TrieNode();
}
node = node->children[c];
}
node->word = word; // Store word at the end node
}
return root;
}
void dfs(vector<vector<char>>& board, int i, int j, TrieNode* node, vector<string>& result) {
char c = board[i][j];
if (c == '#' || !node->children.count(c)) return;
node = node->children[c];
if (!node->word.empty()) {
result.push_back(node->word);
node->word = ""; // avoid duplicate
}
board[i][j] = '#'; // mark visited
int dirs[4][2] = {{0,1}, {1,0}, {0,-1}, {-1,0}};
for (auto& d : dirs) {
int x = i + d[0], y = j + d[1];
if (x >= 0 && y >= 0 && x < board.size() && y < board[0].size()) {
dfs(board, x, y, node, result);
}
}
board[i][j] = c; // backtrack
}
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
vector<string> result;
TrieNode* root = buildTrie(words);
for (int i = 0; i < board.size(); ++i) {
for (int j = 0; j < board[0].size(); ++j) {
dfs(board, i, j, root, result);
}
}
return result;
}
};
17. Letter Combinations of a Phone Number
Solution: #Recursion
class Solution {
private:
unordered_map<char, string> mp = {
{'2', "abc"},
{'3', "def"},
{'4', "ghi"},
{'5', "jkl"},
{'6', "mno"},
{'7', "pqrs"},
{'8', "tuv"},
{'9', "wxyz"}
};
void rec(string digits, int idx, vector<string>& ans, string tmp){
if(idx >= digits.size()){
ans.push_back(tmp);
return;
}
string word = mp[digits[idx]];
for(int i = 0; i < word.size(); i++) rec(digits, idx + 1, ans, tmp + word[i]);
}
public:
vector<string> letterCombinations(string digits) {
if(digits.empty()) return {};
vector<string> ans;
string tmp = "";
rec(digits, 0, ans, tmp);
return ans;
}
};
实例图
Level 0:
└── "" // 开始递归,空字符串
Level 1: 处理 digits[0] = '2' → "abc"
├── "a"
├── "b"
└── "c"
Level 2: 处理 digits[1] = '3' → "def"
├── "a"
│ ├── "ad"
│ ├── "ae"
│ └── "af"
├── "b"
│ ├── "bd"
│ ├── "be"
│ └── "bf"
└── "c"
├── "cd"
├── "ce"
└── "cf"
最终结果:
["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]