哈希
1.两数之和
错误思路
排序后二分查找结果与当前元素之差的存在情况。因为要求返回的是下标,但下标在排序后已经更新。
题解
创建哈希表方法:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
// 无序map 省略排序步骤
unordered_map<int, int> hashtable;
// 对元素x,查询哈希表中是否存在target - x
// 若存在就返回,若不存在就将元素x作为下标插入哈希表,值为i(sum中的下标)
for(int i = 0; i < nums.size(); i++)
{
//key是目标值,value是原数组下标
auto it = hashtable.find(target - nums[i]);
if(it != hashtable.end())
return {it->second, i};
hashtable[nums[i]] = i;
}
return {};
}
};
49.字母异位词分组
题解
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> words;
// 将排序后的字符串作为key,将字符串插入到value的vector中去
for(const string&str : strs)
{
string key = str;
sort(key.begin(), key.end());
words[key].emplace_back(str);
}
vector<vector<string>> ans;
for(auto it = words.begin(); it != words.end(); it++)
ans.emplace_back(it->second);
return ans;
}
};
128.最长连续序列
错误记录
unordered_set 不支持下标访问
题解
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> num_set;
// 加入哈希表去重
for(int i = 0; i < nums.size(); i++)
{
num_set.insert(nums[i]);
}
int ans = 0;
for(const int& num : num_set)
{
// 上一个连续的元素不存在,这么做是为了减少重复情况的判定
// 因为遍历是正向的,如果存在上一个连续的元素,说明这个元素也已经被计算过了
if(!num_set.count(num - 1))
{
int temp = 1;
int curNum = num + 1;
// 向后遍历
while(num_set.count(curNum))
{
temp++;
curNum++;
}
ans = max(ans, temp);
}
}
return ans;
}
};
双指针
283.移动零
普通思路
第一次遍历:使用i指针遍历,使用j指针记录非零元素的个数,nums[j++] = nums[i];
第二次遍历将指针后面的数赋值0
题解
快速排序思想,以第一个0作为中心点
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int j = 0;
for(int i = 0; i < nums.size(); i++)
{
// i与j一直相等,实际上没有交换,直到遇到第一个非零数
// j标记了第一个0的位置,i标记的是非零的数,所以要交换
// 而后j自增,又指向了第一个0的位置
if(nums[i])
{
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
j++;
}
}
}
};
11.盛最多水的容器
题解
先分析面积公式 min(height[i], height[j]) * (j - i),最开始i、j分立容器两端,即最大边长
长板向内移动则面积不变或减小(有了新的短板,或者不足以抵消边长的减小带来的影响)
短板向内移动则面积可能变大(换了一个短板,而且增加的面积足以抵消边长的影响)
class Solution {
public:
int maxArea(vector<int>& height) {
int i = 0, j = height.size() - 1;
int area = min(height[i], height[j]) * (j - i);
while(true)
{
int temp;
if(height[i] <= height[j])
{
i++;
}
else
{
j--;
}
temp = min(height[i], height[j]) * (j - i);
area = max(area, temp);
if(i == j)
break;
}
return area;
}
};
15.三数之和
题解
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
// 首先进行排序
sort(nums.begin(), nums.end());
// 外层循环,遍历数组,要注意去重
for(int i = 0; i < nums.size(); i++)
{
int k = nums.size() - 1;
// 这里是去重,外层相同的值结果是一样的
if(i > 0 && nums[i] == nums[i - 1])
continue;
// 内层循环,也需要注意去重
for(int j = i + 1; j < nums.size(); j++)
{
// 去重,防止内层重复
if(j > i + 1 && nums[j] == nums[j - 1])
continue;
// 关键步骤,保证j在k左侧的同时,寻找可行解
while(j < k && nums[j] + nums[k] > -nums[i])
k--;
// 未找到
if(j == k)
break;
// 一组可行解
if(nums[j] + nums[k] + nums[i] == 0)
ans.push_back({nums[i], nums[j], nums[k]});
}
}
return ans;
}
};
42.接雨水
题解
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
if(n == 0)
return 0;
// 维护向左的高度最大值
vector<int> leftMax(n);
leftMax[0] = height[0];
for(int i = 1; i < n; i++)
{
leftMax[i] = max(leftMax[i - 1], height[i]);
}
// 维护向右的高度最大值
vector<int> rightMax(n);
rightMax[n - 1] = height[n - 1];
for(int i = n - 2; i >= 0; i--)
{
rightMax[i] = max(rightMax[i + 1], height[i]);
}
// 相当于按列计算,将左右高度的最小值减去柱子高度即可
int ans = 0;
for(int i = 0; i < n; i++)
ans += min(leftMax[i], rightMax[i]) - height[i];
return ans;
}
};
滑动窗口
3.无重复字符的最长子串
题解1-滑动窗口
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char> occ;
int n = s.size();
// 右指针
int rk = -1, ans = 0;
for(int i = 0; i < n; i++)
{
if(i != 0)
// 左指针右移一格,移除一个字符
occ.erase(s[i - 1]);
while(rk + 1 < n && !occ.count(s[rk + 1]))
{
// 不断右移指针
occ.insert(s[rk + 1]);
rk++;
}
// i到rk个字符即为无重复子串
ans = max(ans, rk - i + 1);
}
return ans;
}
};
题解2-动态规划
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> dic;
int res = 0, tmp = 0, len = s.size(), i;
for(int j = 0; j < len; j++)
{
// 向左已经找不到相同字符,指针指向-1
if(dic.find(s[j]) == dic.end())
i = -1;
// 获取索引
else
i = dic.find(s[j])->second;
// 更新哈希表
dic[s[j]] = j;
// dp[j-1]->dp[j]
tmp = tmp < j - i? tmp + 1 : j - i;
// 更新答案
res = max(res, tmp);
}
return res;
}
};
438.找到字符串中所有字母异位词
题解
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int sLen = s.size(), pLen = p.size();
if(sLen < pLen)
return vector<int>();
vector<int> ans;
vector<int> sCount(26);
vector<int> pCount(26);
for(int i = 0; i < pLen; i++)
{
++sCount[s[i] - 'a'];
++pCount[p[i] - 'a'];
}
if(sCount == pCount)
ans.emplace_back(0);
for(int i = 0; i < sLen - pLen; i++)
{
--sCount[s[i] - 'a'];
++sCount[s[i + pLen] - 'a'];
if(sCount == pCount)
ans.emplace_back(i + 1);
}
return ans;
}
};
239.滑动窗口最大值
题解
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
// 优先队列——自动排序
priority_queue<pair<int, int>> q;
// 先放入前k个元素做第一次判断
for(int i = 0; i < k; i++)
{
q.emplace(nums[i], i);
}
vector<int> ans = {q.top().first};
for(int i = k; i < n; i++)
{
// 放入一个元素
q.emplace(nums[i], i);
// 弹出第一个元素
while(q.top().second <= i - k)
q.pop();
// 放入答案,注意first是值而second是下标
ans.push_back(q.top().first);
}
return ans;
}
};
子串
560.和为k的子数组
题解-动态规划
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int, int> mp;
// 和为0出现一次
mp[0] = 1;
// cnt用于记录子数组和等于k的个数,pre用于记录当前的前缀和
int cnt = 0, pre = 0;
for(auto& x:nums)
{
// 将pre更新为当前前缀和加上x
pre += x;
// 判断mp中是否存在pre - k的键
if(mp.find(pre - k) != mp.end())
{
// 计数器cnt加上mp[pre - k]
cnt += mp[pre - k];
}
// 记录当前前缀和pre出现的次数
mp[pre]++;
}
return cnt;
}
};
普通数组
53.最大子数组和
题解-动态规划
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int len = nums.size();
int ans = nums[0];
vector<int> dp(len);
dp[0] = nums[0];
for(int i = 1; i < len; i++)
{
if(dp[i - 1] >= 0)
dp[i] = dp[i - 1] + nums[i];
else
dp[i] = nums[i];
ans = max(ans, dp[i]);
}
return ans;
}
};
56.合并区间
题解
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if(intervals.size() == 0)
return {};
sort(intervals.begin(), intervals.end());
vector<vector<int>> merged;
for(int i = 0; i < intervals.size(); i++)
{
int L = intervals[i][0], R = intervals[i][1];
if(!merged.size() || merged.back()[1] < L)
merged.push_back({L, R});
else
merged.back()[1] = max(merged.back()[1], R);
}
return merged;
}
};
189.轮转数组
题解-数组翻转
class Solution {
public:
void reverse(vector<int>& nums, int start, int end)
{
while(start < end)
{
swap(nums[start], nums[end]);
start++;
end--;
}
}
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k = k % n;
reverse(nums, 0, n - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, n - 1);
}
};
238.除自身以外数组的乘积
题解
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
// 前缀积
vector<int> L(n, 0);
vector<int> R(n, 0);
vector<int> ans(n);
// 初始化
L[0] = 1; R[n - 1] = 1;
// 遍历,计算
for(int i = 1; i < n; i++)
L[i] = nums[i - 1] * L[i - 1];
for(int i = n - 2; i >= 0; i--)
R[i] = nums[i + 1] * R[i + 1];
// 相乘
for(int i = 0; i < n; i++)
ans[i] = L[i] * R[i];
return ans;
}
};
41.缺失的第一个正数
题解-置换
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
// 对于一个长度为 N 的数组,其中没有出现的最小正整数只能在 [1,N+1] 中
int n = nums.size();
for(int i = 0; i < n; i++)
{
while(nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i])
{
swap(nums[nums[i] - 1], nums[i]);
}
}
for(int i = 0; i < n; i++)
if(nums[i] != i + 1)
return i + 1;
return n + 1;
}
};
二叉树
94.二叉树的中序遍历
题解-递归
class Solution {
public:
void inorder(TreeNode* root, vector<int>& res)
{
if(!root)
return;
inorder(root->left, res);
res.push_back(root->val);
inorder(root->right, res);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inorder(root, res);
return res;
}
};
104.二叉树的最大深度
题解-DFS
class Solution {
public:
int maxDepth(TreeNode* root) {
if(!root)
return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
226.翻转二叉树
题解
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(!root)
return nullptr;
TreeNode* left = invertTree(root->left);
TreeNode* right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
};
101.对称二叉树
题解
class Solution {
public:
bool check(TreeNode* p, TreeNode* q)
{
if(!p && !q)
return true;
if(!p || !q)
return false;
if(p->val == q->val && check(p->left, q->right) && check(p->right, q->left))
return true;
else
return false;
}
bool isSymmetric(TreeNode* root) {
return check(root, root);
}
};
543.二叉树的直径
题解
class Solution {
int ans;
int depth(TreeNode* t)
{
if(!t)
return 0;
int L = depth(t->left);
int R = depth(t->right);
ans = max(ans, L + R);
return max(L, R) + 1;
}
public:
int diameterOfBinaryTree(TreeNode* root) {
ans = 0;
depth(root);
return ans;
}
};
102.二叉树的层序遍历
题解
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ret;
if(!root)
return ret;
queue <TreeNode*> q;
q.push(root);
while(!q.empty())
{
int cur = q.size();
ret.push_back(vector<int> ());
for(int i = 1; i <= cur; i++)
{
auto node = q.front(); q.pop();
ret.back().push_back(node->val);
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
}
return ret;
}
};
二分
35.搜索插入位置
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0, right = n - 1, ans = n;
while(left <= right)
{
int mid = ((right - left) >> 1) + left;
if(target <= nums[mid])
{
ans = mid;
right = mid - 1;
}
else
{
left = mid + 1;
}
}
return ans;
}
};
矩阵
73.矩阵置零
题解
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int x = matrix.size();
int y = matrix[0].size();
vector<int> row(x), col(y);
for(int i = 0; i < x; i++)
for(int j = 0; j < y; j++)
{
if(!matrix[i][j])
row[i] = col[j] = 1;
}
for(int i = 0; i < x; i++)
for(int j = 0; j < y; j++)
{
if(row[i] || col[j])
matrix[i][j] = 0;
}
}
};
54.螺旋矩阵
题解
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> ans;
if(matrix.empty())
return ans;
// 赋值边界
int u = 0;
int d = matrix.size() - 1;
int l = 0;
int r = matrix[0].size() - 1;
while(true)
{
// 向右移动
for(int i = l; i <= r; i++)
ans.push_back(matrix[u][i]);
// 上边界下移
if(++u > d)
break;
// 向下移动
for(int i = u; i <= d; i++)
ans.push_back(matrix[i][r]);
// 右边界左移
if(--r < l)
break;
// 向左移动
for(int i = r; i >= l; i--)
ans.push_back(matrix[d][i]);
// 下边界上移
if(--d < u)
break;
// 向上移动
for(int i = d; i >= u; i--)
ans.push_back(matrix[i][l]);
// 左边界右移
if(++l > r)
break;
}
return ans;
}
};
48.旋转图像
题解
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
for(int i = 0; i < n / 2; i++)
{
for(int j = 0; j < (n + 1) / 2; j++)
{
int temp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = temp;
}
}
}
};
240.搜索二维矩阵
题解
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size(), n = matrix[0].size();
// 从右上角开始
int x = 0, y = n - 1;
while(x < m && y >= 0)
{
// 最好情况
if(matrix[x][y] == target)
return true;
// 比目标大,那么这一列都大,所以减小y
else if(matrix[x][y] > target)
y--;
// 比目标小,那么这一行都小,所以增大x
else
x++;
}
return false;
}
};
链表
160.相交链表
题解
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* p1 = headA;
ListNode* p2 = headB;
// 特殊情况
if(!p1 && !p2)
return NULL;
// 可证明,p1和p2相等的情况只有两种:空和相交点
while(p1 != p2)
{
if(!p1)
p1 = headB;
else
p1 = p1 ->next;
if(!p2)
p2 = headA;
else
p2 = p2 ->next;
}
return p1;
}
};
206.反转链表
题解
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* cur = head;
while(cur)
{
ListNode* next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
234.回文链表
题解-递归
class Solution {
ListNode* frontPointer;
public:
bool recursivelyCheck(ListNode* currentNode)
{
if(currentNode)
{
if(!recursivelyCheck(currentNode->next))
return false;
if(currentNode->val != frontPointer->val)
return false;
frontPointer = frontPointer->next;
}
return true;
}
bool isPalindrome(ListNode* head) {
frontPointer = head;
return recursivelyCheck(head);
}
};
141.环形链表
题解
class Solution {
public:
bool hasCycle(ListNode *head) {
if(!head || !head->next)
return false;
// 快慢指针,如果存在环的话,快慢指针会“套圈”
ListNode* slow = head;
ListNode* fast = head->next;
while(slow != fast)
{
if(!fast || !fast->next)
return false;
// 慢指针每次移动一步
slow = slow->next;
// 快指针每次移动两步
fast = fast->next->next;
}
return true;
}
};
142.环形链表II
题解
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* slow = head, *fast = head;
// 从相遇点到入环点的距离加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离
while(fast)
{
slow = slow->next;
if(!fast->next)
return NULL;
fast = fast->next->next;
if(fast == slow)
{
ListNode* ptr = head;
while(ptr != slow)
{
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
}
return NULL;
}
};
21.合并两个有序列表
题解
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dum = new ListNode(0);
ListNode* cur = dum;
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 = list2;
else
cur->next = list1;
return dum->next;
}
};
2.两数相加
题解
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* head = nullptr, *tail = nullptr;
int carry = 0;
while(l1 || l2)
{
int n1 = l1? l1->val: 0;
int n2 = l2? l2->val: 0;
int sum = n1 + n2 + carry;
carry = sum / 10;
if(!head)
{
head = tail = new ListNode(sum % 10);
}
else
{
tail->next = new ListNode(sum % 10);
tail = tail->next;
}
if(l1)
l1 = l1->next;
if(l2)
l2 = l2->next;
}
if(carry)
tail->next = new ListNode(carry);
return head;
}
};
19.删除链表的倒数第N个节点
题解
/**
* 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, head);
ListNode* first = head;
ListNode* second = dummy;
for(int i = 0; i < n; i++)
first = first -> next;
while(first)
{
first = first -> next;
second = second -> next;
}
second -> next = second -> next -> next;
ListNode* ans = dummy -> next;
delete dummy;
return ans;
}
};
24.两两交换链表中的节点
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* temp = dummy;
while(temp->next && temp->next->next)
{
ListNode* node1 = temp->next;
ListNode* node2 = temp->next->next;
temp->next = node2;
node1->next = node2->next;
node2->next = node1;
temp = node1;
}
ListNode* ans = dummy->next;
delete dummy;
return ans;
}
};
技巧
136.只出现一次的数字
题解-异或
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ans = nums[0];
if(nums.size() > 1)
{
for(int i = 1; i < nums.size(); i++)
ans = ans ^ nums[i];
}
return ans;
}
};
169.多数元素
题解
这个数如果存在,那么一定是众数,一定在排序后下标为n/2的位置
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size() / 2];
}
};
75.颜色分类
题解
class Solution {
public:
void sortColors(vector<int>& nums) {
int n = nums.size();
int ptr = 0;
for(int i = 0; i < n; i++)
{
if(nums[i] == 0)
{
swap(nums[i], nums[ptr]);
ptr++;
}
}
for(int i = ptr; i < n; i++)
{
if(nums[i] == 1)
{
swap(nums[i], nums[ptr]);
ptr++;
}
}
}
};
31.下一个排列
题解
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int n = nums.size();
if(n <= 1)
return;
int i = n - 2, j = n - 1, k = n - 1;
// 从后向前查找第一个相邻的升序元素对
while(i >= 0 && nums[i] >= nums[j])
{
i--;
j--;
}
// 从后向前找第一个大于a[i]的值a[k]并交换
if(i >= 0)
{
while(nums[i] >= nums[k])
k--;
swap(nums[i], nums[k]);
}
// 逆置j以后的元素
i = j;
j = n - 1;
while(i < j)
{
swap(nums[i], nums[j]);
i++;
j--;
}
}
};
287.寻找重复数
题解
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int n = nums.size();
int l = 1, r = n - 1, ans = -1;
// 二分查找
while(l <= r)
{
int mid = (l + r) / 2;
int cnt = 0;
// cnt表示数组中<=i的数个数
for(int i = 0; i < n; i++)
if(nums[i] <= mid)
cnt++;
if(cnt <= mid)
l = mid + 1;
else
{
r = mid - 1;
ans = mid; // 更新答案,新的答案只可能比旧的值更小
}
}
return ans;
}
};